11 | {% endif %}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Adam Duncan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eleventy-plugin-i18n-demo",
3 | "version": "0.1.0",
4 | "description": "Demo site for eleventy-plugin-i18n",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "npm run serve",
8 | "serve": "cross-env ELEVENTY_ENV=development npx eleventy --serve",
9 | "build": "cross-env ELEVENTY_ENV=production npx eleventy",
10 | "debug": "cross-env DEBUG=* npx eleventy"
11 | },
12 | "keywords": [],
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/adamduncan/eleventy-plugin-i18n-demo.git"
16 | },
17 | "author": "Adam Duncan (https://adamduncandesigns.com)",
18 | "bugs": {
19 | "url": "https://github.com/adamduncan/eleventy-plugin-i18n-demo/issues"
20 | },
21 | "homepage": "https://github.com/adamduncan/eleventy-plugin-i18n-demo#readme",
22 | "license": "MIT",
23 | "devDependencies": {
24 | "@11ty/eleventy": "^0.11.1",
25 | "cross-env": "^7.0.2",
26 | "lodash.get": "^4.4.2",
27 | "prettier": "^2.0.5",
28 | "templite": "^1.1.0"
29 | },
30 | "dependencies": {
31 | "eleventy-plugin-i18n": "0.1.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/_data/i18n/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // General
3 | website: {
4 | 'ar-AE': 'موقع الكتروني',
5 | 'en-GB': 'Website',
6 | 'es-ES': 'Sitio web'
7 | },
8 | select_language: {
9 | 'ar-AE': 'اختار اللغة',
10 | 'en-GB': 'Select language',
11 | 'es-ES': 'Seleccione el idioma'
12 | },
13 |
14 | // Greetings
15 | hello: {
16 | 'ar-AE': 'مرحبا',
17 | 'en-GB': 'Hello',
18 | 'es-ES': 'Hola'
19 | },
20 | hello_name: {
21 | 'ar-AE': '{{ name }}مرحبا',
22 | 'en-GB': 'Hello, {{ name }}!',
23 | 'es-ES': '¡Hola {{ name }}!'
24 | },
25 |
26 | // Navigation
27 | homepage: {
28 | 'ar-AE': 'الصفحة الرئيسية',
29 | 'en-GB': 'Homepage',
30 | 'es-ES': 'Página principal'
31 | },
32 | posts: {
33 | 'ar-AE': 'المشاركات',
34 | 'en-GB': 'Posts',
35 | 'es-ES': 'Publicaciones'
36 | },
37 |
38 | // Nested organisation example
39 | actions: {
40 | click: {
41 | 'ar-AE': 'انقر',
42 | 'en-GB': 'Click',
43 | 'es-ES': 'Hacer clic'
44 | },
45 | loading: {
46 | 'ar-AE': 'جار التحميل',
47 | 'en-GB': 'Loading',
48 | 'es-ES': 'Cargando'
49 | }
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/.eleventy.js:
--------------------------------------------------------------------------------
1 | const i18n = require('eleventy-plugin-i18n');
2 | const translations = require('./src/_data/i18n');
3 |
4 | module.exports = function (eleventyConfig) {
5 | // Plugins
6 | eleventyConfig.addPlugin(i18n, {
7 | translations,
8 | fallbackLocales: {
9 | '*': 'en-GB'
10 | }
11 | });
12 |
13 | // TEMP demo of what could be an i18n-aware plural package?
14 | eleventyConfig.addFilter('pluralize', function (term, count = 1) {
15 | // Poorman's pluralize for now...
16 | return count === 1 ? term : `${term}s`;
17 | });
18 |
19 | // Browsersync
20 | // Redirect from root to default language root during --serve
21 | // Can also be handled by netlify.toml?
22 | eleventyConfig.setBrowserSyncConfig({
23 | callbacks: {
24 | ready: function (err, bs) {
25 | bs.addMiddleware('*', (req, res) => {
26 | if (req.url === '/') {
27 | res.writeHead(302, {
28 | location: '/en-GB/'
29 | });
30 | res.end();
31 | }
32 | });
33 | }
34 | }
35 | });
36 |
37 | // Configuration
38 | return {
39 | dir: {
40 | input: 'src'
41 | },
42 | markdownTemplateEngine: 'njk'
43 | };
44 | };
45 |
--------------------------------------------------------------------------------
/src/_includes/layouts/base.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | eleventy-plugin-i18n — {{ title }}
10 |
11 |
20 |
21 |
22 |
23 |
24 | {{ site.title }}
25 |
29 |
30 |
31 |
32 | {{ content | safe }}
33 |
34 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # eleventy-plugin-i18n-demo
2 |
3 | Demo site for [`eleventy-plugin-i18n`](https://github.com/adamduncan/eleventy-plugin-i18n).
4 |
5 | ## Goal
6 |
7 | - [x] Leverage Eleventy's data cascade to build a clever, language-aware `{{ 'hello' | i18n }}` filter, backed by multilingual dictionary translations.
8 | - [x] Package the filter up into a plugin, so can be easily configured and used in any Eleventy site.
9 | - [x] Add a `data` argument and interpolate values into the translations: `{ 'hello_name': 'Hello, {{ name }}!' }`
10 | - [ ] Write up tutorial to build on some great concepts ([multilingual](https://www.webstoemp.com/blog/multilingual-sites-eleventy/), [language toggle](https://www.webstoemp.com/blog/language-switcher-multilingual-jamstack-sites/)) in this area. Dive further into how to architect and implement multilingual Eleventy sites, and leverage the plugin (e.g. [smart language switching](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_includes/components/language-selector.njk)).
11 | - [ ] Explore shipping additional `pluralize` filter for i18n usage `{{ 'hello' | i18n | pluralize(3) }}` (Awesome suggestion from [@alexcarpenter](https://github.com/alexcarpenter)).
12 |
13 | ## TL;DR just riffin'
14 |
15 | - We start with logical [country-code directories for the site `src`](https://github.com/adamduncan/eleventy-plugin-i18n-demo/tree/master/src) (`/en` or `/en-GB`). Country codes or country codes with language suffixes (if we're talking dialects) are both fair game.
16 | - Set [country-specific locale data](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/en-gb/en-gb.json) in each language directory. This data is used deeper in the country sites' cascade, as well as at the [document level for `lang` and `dir` attributes](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_includes/layouts/base.njk#L2).
17 | - As we maintain independent site trees per language, the guts of the content pages will likely be written in their respective languages. But;
18 | - With "UI text" though, in layouts, forms, and reusable components we often find ourselves hard-coding little chunks of copy throughout. What if we lift these out into a structured [dictionary of terms and translations](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_data/i18n/index.js)? (We could also break this down into any number/schema of dictionary files per language.)
19 | - Then we give ourselves a clever [`i18n` filter](https://github.com/adamduncan/eleventy-plugin-i18n/blob/master/i18n.js) to play with:
20 | - This takes a `key` to look up in the dictionary. It [uses](https://github.com/adamduncan/eleventy-plugin-i18n/blob/master/i18n.js#L25) `lodash.get`-style dot notation to support structured dictionary objects. E.g. [`{{ 'actions.click' | i18n }}`](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_data/i18n/index.js#L39-L50) :sunglasses:
21 | - Under the hood, the `i18n` function will be clever enough to [infer its language "scope"](https://github.com/adamduncan/eleventy-plugin-i18n/blob/master/i18n.js#L22) based on `page.url` language prefix.
22 | - We can interpolate values from a data object, by passing it as the first argument: `{{ 'hello_name' | i18n({ name: 'Eve' }) }}`.
23 | - To override page `locale`, we can pass a language code as the second argument: `{{ 'hello' | i18n({}, 'fr-FR') }}`. (_Note:_ we still pass an empty data object—or `undefined`—here if no interpolation is needed).
24 | - If a dictionary lookup can't be found, we can also set [`fallbackLocales`](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/.eleventy.js#L8-L10) via plugin options. This key/value maps lanaguages to their fallbacks. E.g. `{ 'en-US': 'en-GB' }` or use a wildcard to catch all `{ '*': 'en-GB' }`. Let's [warn the user](https://github.com/adamduncan/eleventy-plugin-i18n/blob/master/i18n.js#L37-L41) in the console when fallbacks are used.
25 | - If neither a translation _nor_ its fallback can be found, let's return the original `key` and [really warn the user](https://github.com/adamduncan/eleventy-plugin-i18n/blob/master/i18n.js#L46-L50) that something's definitely lost in translation.
26 | - One more thing: Because we know about our [`locales.json`](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_data/locales.js) up front, and our site is structured predictably, we can easily create a smart [language-switcher](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_includes/components/language-selector.njk) component. This will automatically link you through to the correct page in each respective language based on the page you're on. No extra front matter or permalinking required. :kissing:
27 |
28 | P.S. I've naively taken [translations here](https://github.com/adamduncan/eleventy-plugin-i18n-demo/blob/master/src/_data/i18n/index.js) from Google translate. I'm sure they're wrong, but would love to get them right! If you speak Spanish or Arabic and can correct me, I'd love for you to reach out: [@duncanadam](https://twitter.com/duncanadam).
29 |
--------------------------------------------------------------------------------