npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

eleventy-plugin-i18n-gettext

v1.5.1

Published

Eleventy plugin which adds i18n support with Gettext string translation and moment.js date and times localization

Downloads

741

Readme

eleventy-plugin-i18n-gettext

Eleventy plugin which adds i18n support with Gettext string translation and moment.js date and times localization.

Gettext is commonly used in Linux C and WordPress worlds. It comes with a few handy features:

  • Can extract translation keys from source code. Poedit configuration for translations extraction.
  • Supports pluralization.
  • Translation keys are their own translation fallback value. It means that if you don't have a translation file or didn't translate some keys, the value of the key itself is used as the translated value.
  • PO files editors exists, like Poedit.

In addition to Gettext features, this plugin:

Table of content

Install

Available on npm.

npm install eleventy-plugin-i18n-gettext --save

Demo

Get Started

Define language site directories

Create directories at the site root you can use either a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

Locale folder casing must be exactly the same in locales and src. In this example I choose lowercase in order to stick to Eleventy slugification configuration.

├─ locales
   └─ fr-fr
       ├─ messages.mo
       └─ messages.po
   └─ pt
       ├─ messages.mo
       └─ messages.po
├─ src
   └─ fr-fr
       └─ fr-fr.11tydata.js
   └─ pt
       └─ pt.11tydata.js
   └─ en
       └─ en.11tydata.js

Create messages.po files

The easiest way to create messages.po files, is to copy them from the demo code source.

  • messages.po files store translations in plain text.
  • messages.mo files are compiled from messages.po. Poedit handle the creation of these files automatically, pushing them into your code repository is recommended.

Create xx.11tydata.js files

In these files we enhance the Eleventy directory data object with i18n.enhance11tydata(obj, locale, dir?).

// xx.11tydata.js
const i18n = require('eleventy-plugin-i18n-gettext')

module.exports = () => {
    return i18n.enhance11tydata({}, __dirname)
}

Configuration

Open up your Eleventy config file (probably .eleventy.js), import the plugin and use addPlugin.

// .eleventy.js
const i18n = require('eleventy-plugin-i18n-gettext')

module.exports = function (eleventyConfig) {
  eleventyConfig.addPlugin(i18n, {
    localesDirectory: 'locales',
    parserMode: 'po',
    javascriptMessages: 'messages.js',
    tokenFilePatterns: [
      'src/**/*.njk',
      'src/**/*.js'
    ],
    localeRegex: /^(?<lang>.{2})(?:-(?<country>.{2}))*$/
  })
}

On activation, the plugin:

When beforeBuild and beforeWatch events are raised, the plugin:

  • browses every folders of the localesDirectory
  • loads/reloads translations from messages.po files (or messages.mo)
  • finds files depending on tokenFilePatterns
  • searches in these files for translations keys
  • creates or update the javascriptMessages file with these translation keys

localesDirectory

Type: string | Default: locales

Name of the directory where messages.po, messages.mo and messages.js files are located.

It's relative to the Node process current working directory, usually the directory where is located package.json and from where npm run commands are executed.

parserMode

Type: string | Default: po | AllowedValues: po, mo

By default, gettext-parser is configured to parse messages.po text files. For large translations it may be more efficient to parse messages.mo binary files.

javascriptMessages

Type: string | Default: messages.js

Name of the file where this plugin stores translation keys found in code source files. It's a path relative to localesDirectory.

tokenFilePatterns

Type: string[] | Default: ['src/**/*.njk', 'src/**/*.js']

Glob patterns used to know which code source files to search for translation keys.

It's relative to the Node process current working directory, usually the directory where is located package.json and from where npm run commands are executed.

localeRegex

Type: Regex | Default: /^(?<lang>.{2})(?:-(?<country>.{2}))*$/

This Regex is used to identify which part of locale directories are the language and the country.

The default value identifies standard locales values: nl-be, nl, fr-be, fr, ...

For example, /^(?:(?<country>.{2}))*(?<lang>.{2})$/ use the same locales pattern as used by Apple: benl, nl, befr, fr, ...

📌 In both Regex, country part can be omitted but not the language part.

API

i18n.enhance11tydata(obj, locale, dir?)

Returns: object

Attaches additional properties and methods to obj and returns it:

| Type | Name |-|-| | Property | lang | Property | langDir | Property | locale | Method | _(key, ...args) | Method | _i(key, obj) | Method | _n(singular, plural, count, ...args) | Method | _ni(singular, plural, count, obj) | Method | _d(format, date, timezone?) | Method | _p(basePath)

lang and langDir properties are meant to be used in the <html> tag.

locale property is meant to be used in custom filters and shortcodes.

obj

Type: object

Contains the custom data you want to use in your templates.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

dir

Type: string | Default: ltr | AllowedValues: ltr, rtl

The locale direction, left-to-right or right-to-left.

i18n._(locale, key, ...args)

Returns: string

Retrieve a Gettext translated string then apply printf() on it.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

key

Type: string

The translation key to translate.

args

Type: string[]

Arguments sent to printf().

i18n._i(locale, key, obj)

Returns: string

Retrieve a Gettext translated string then apply template literals/string interpolation on it.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

key

Type: string

The translation key to translate.

obj

Type: obj

A simple object which properties will be used as variables for template literals/string interpolation.

i18n._n(locale, singular, plural, count, ...args)

Returns: string

Retrieve a Gettext singular or plural translated string then apply printf() on it.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

singular

Type: string

The singular form of the translation key to translate.

plural

Type: string

The plural form of the translation key to translate.

count

Type: int

The quantity which helps Gettext to determine whether to return the singular or the plural translated value.

args

Type: string[]

Arguments sent to printf().

i18n._ni(locale, singular, plural, count, obj)

Returns: string

Retrieve a Gettext singular or plural translated string then apply template literals/string interpolation on it.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

singular

Type: string

The singular form of the translation key to translate.

plural

Type: string

The plural form of the translation key to translate.

count

Type: int

The quantity which helps Gettext to determine whether to return the singular or the plural translated value.

obj

Type: obj

A simple object which properties will be used as variables for template literals/string interpolation.

i18n._d(locale, format, date, timezone?)

Returns: string

Return the localized form of a date using moment.js localized formats.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

format

Type: string

A moment.js localized format

| | | | |-|-|- | Time | LT | 8:30 PM | Time with seconds | LTS | 8:30:25 PM | Month numeral, day of month, year | L | 09/04/1986 | | l | 9/4/1986 | Month name, day of month, year | LL | September 4, 1986 | | ll | Sep 4, 1986 | Month name, day of month, year, time | LLL | September 4, 1986 8:30 PM | | lll | Sep 4, 1986 8:30 PM | Month name, day of month, day of week, year, time | LLLL | Thursday, September 4, 1986 8:30 PM | | llll | Thu, Sep 4, 1986 8:30 PM

date

Type: string

Any type of string that moment() can use.

timezone

Type: string

A timezone string listed in tz database time zones

i18n._p(locale, basePath)

Returns: string

Returns basePath prefixed with locale. The main intent of this method is to be used in the permalink parameter of templates.

locale

Type: string

The locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

basePath

Type: string

Path which will be prefixed with the locale.

API Usage

With functions in templates

In this context, locale parameter is useless.

It's set by i18n.enhance11tydata(obj, locale, dir?) in xx.11tydata.js data directory files.

<!-- index.njk -->
<html lang="{{ lang }}" dir="{{ langDir }}">
  <body>
    <div>{{ _('I like Gettext') }}</div>
    <div>{{ _('%s and I like Gettext as much as %s', 'Bob', 'John') }}</div>
    <div>{{ _i('${friend} and I like Gettext as much as ${chief}', { friend: 'Bob', chief: 'John' }) }}</div>

    <div>{{ _n('I like Gettext', 'They like Gettext', peopleCount) }}</div>
    <div>{{ _n('I like Gettext as much as %s', 'They like Gettext as much as %s', peopleCount, 'John') }}</div>
    <div>{{ _ni('I like Gettext as much as ${chief}', 'They like Gettext as much as ${chief}', peopleCount, { chief: 'John' }) }}</div>

    <div>{{ _d('LL', page.date) }}</div>
    <div>{{ _d('LL', '2020-08-01T21:57:00.000Z', 'Asia/Bangkok') }}</div>
    <div>{{ _d('LL', 1596319020000, 'UTC') }}</div>
    <div>{{ _p('/') | url }}</div>
  </body>
</html>

With shortcodes in templates

In this context, locale parameter is mandatory.

<!-- index.njk -->
<html lang="{{ lang }}" dir="{{ langDir }}">
  <body>
    <div>{% _   locale, 'I like Gettext' %}</div>
    <div>{% _   locale, '%s and I like Gettext as much as %s', 'Bob', 'John' %}</div>
    <div>{% _i  locale, '${friend} and I like Gettext as much as ${chief}', { friend: 'Bob', chief: 'John' } %}</div>

    <div>{% _n  locale, 'I like Gettext', 'They like Gettext', peopleCount %}</div>
    <div>{% _n  locale, 'I like Gettext as much as %s', 'They like Gettext as much as %s', peopleCount, 'John' %}</div>
    <div>{% _ni locale, 'I like Gettext as much as ${chief}', 'They like Gettext as much as ${chief}', peopleCount, { chief: 'John' } %}</div>

    <div>{% _d  locale, 'LL', page.date %}</div>
    <div>{% _d  locale, 'LL', '2020-08-01T21:57:00.000Z', 'Asia/Bangkok' %}</div>
    <div>{% _d  locale, 'LL', 1596319020000, 'UTC' %}</div>
    <!-- `_p` shortcode applies the `url` built-in filter by itself -->
    <div>{% _p  locale, '/' %}</div>
  </body>
</html>

In filters and shortcodes

In this context, locale parameter is mandatory.

// .eleventy.js
const i18n = require('eleventy-plugin-i18n-gettext')

module.exports = eleventyConfig => {
  eleventyConfig.addShortcode("custom_shortcode", (locale, fruit) => {
    return i18n._(locale, fruit.name)
  })
  ...
}

When i18n.enhance11tydata(obj, locale, dir?) is used in xx.11tydata.js data directory files, it adds a property named locale which can be used in templates and passed to filters and shortcodes.

<!-- index.njk -->
<div>{%- custom_shortcode locale, fruit -%}</div>

Shortcode

relocalizePath targetedLocale, pagePath

Returns: string

The intent of this shortcode is to construct language selectors.

It replaces the locale part of the current url with the targeted locale then it applies the url built-in filter.

// _data/locales.json
[
    { "path": "fr-fr", "name": "Français"   },
    { "path": "nl-nl", "name": "Nederlands" },
    { "path": "pt-pt", "name": "Português"  },
    { "path": "en-us", "name": "English"    },
    { "path": "ar",    "name": "عربى"       }
]
<!-- _includes/layout.njk -->
{%- for locale in locales -%}
  <a href="{%- relocalizePath locale.path, page.url -%}">{{ locale.name }}</a>
{%- endfor -%}

targetedLocale

Type: string

The target locale as a simple language code (e.g. en) or language code with country code suffix (e.g. en-us).

pagePath

Type: string

The path on which the current locale will be replaced with targetedLocale.

⚠️ Path prefix bug

This method is able to handle pathPrefix when set in .eleventy.js configuration file.

But when pathPrefix is set by the commandline eleventy --pathprefix=eleventy-base-blog, its value in the Config object is '/' instead of '/eleventy-base-blog/'.

Sources

Credits

Inspired by adamduncan work on eleventy-plugin-i18n