@gaia-law/i18n-modules
CSS Modules for i18n. A webpack plugin that brings support for a modular approach to dictionaries. Especially useful for tools such as Phraseapp. Pairs well with react-polyglot.
Usage:
Setup
In your webpack config:
const I18nModules = require('@gaia-law/i18n-modules');
plugins: [
new I18nModules(),
],
Create an options RC file in your project's root named .i18n-modules-rc.js
with this example content:
module.exports = {
keysRoot: './',
moduleEnding: '.translations.json',
dictionaryPattern: './dictionaries/[locale_code].json',
emitFile: true,
getId: (keysRoot, moduleEnding, filePath) => idString,
dictionaryAdapter: {
parse: (buffer) => someObj,
stringify: (obj) => someStr,
}
};
If you don't expect to use the CLI (see below), then instead of creating an RC file you
can simply pass an options object as an argument to the plugin. Passed options will be
merged with those in the RC file if present and the defaults. This way you can also
override RC options by passing extra options directly.
Loader alone
In case you don't need to bundle or update original dictionaries and only need the part
that remaps module paths, you can use the module loader directly. In webpack config use it
as any other loader:
const { loader: i18nLoader } = require('@gaia-law/i18n-modules');
module: {
rules: [
{
test: /\.translations\.json/,
use: [{
loader: i18nLoader,
options: {
},
}],
},
]
}
Working with modules
Create a translations module myfile.translations.json
:
{
"de-DE": {
"content": {
"totally": {
"necessary": {
"nesting": "Majestätischer Inhalt"
}
}
},
"title": "Unglaublicher Titel"
},
"en-US": {
"content": {
"totally": {
"necessary": {
"nesting": "Majestic content"
}
}
},
"title": "Amazing title"
}
}
You need to pay attention to the language names in your modules,
the plugin only validates that they match a certain pattern, but not the values.
Import translations:
import lang from './myfile.translations';
t(lang.title);
t(lang.content.totally.necessary.nesting);
When importing your translation modules all of the values will be remapped to lookup strings for your main dictionary.
Referencing bundled modules
Load your main dictionary like usual:
import dictionary from '../dictionaries/de-DE';
The dictionary will contain it's original content and the generated modules.
Options:
keysRoot
(optional) - the keys will be generated relative to this folder. Use this to make generated keys shorter by excluding static parts of the path, e.g. if all your translations are in ./frontend/app/components
pass that as the keysRoot
. Defaults to './'
.moduleEnding
(optional) - the ending that will be used for the translation modules. You can't use :
symbol in file names for these files. It's an OS limitation for MacOS and Windows anyway, but *nix users should be aware. Defaults to '.translations.json'
dictionaryPattern
(required) - the path to your dictionary folder or files. Use [locale_code]
placeholder to get current language name injected into the file name. Try not to name your dictionaries in such a way where part of the name might be confused for a language name. For example this might break the build ./dicts/gr-EAT.[locale_code].json
emitFile
(optional) - determines if the bundled dictionary needs to also be stored on the disk. Set to false
when you prefer to use @gaia-law/i18n-modules
binary to update original dictionaries or don't intend to store module build results anywhere. Defaults to true
.
CLI:
This package comes with a CLI because you might need a way to sync updates from Phraseapp or translations hosting service back into your modules and a simple way to build all languages from modules at once. To use this CLI you must create an RC configuration file.
npx @gaia-law/i18n-modules build
will build all dictionaries from modulesnpx @gaia-law/i18n-modules update
will update all modules from dictionaries. This will not create any new module files from dictionary as this is likely a result of a typo.npx @gaia-law/i18n-modules clean
will remove all generated modules from dictionaries
env:
We support I18N_MODULES_CONTEXT
env variable to set current working directory to something else. It works similarly to webpack's context
option. When set both plugin and @gaia-law/i18n-modules
binary will look for the RC file relative to that context folder. In addition when running the @gaia-law/i18n-modules
binary all relative file paths in the RC file will be relative to that context. I18N_MODULES_CONTEXT
itself accepts paths relative to current working directory, e.g. one could set I18N_MODULES_CONTEXT=./frontend/app
and it would work.
An example with Phraseapp CLI:
npx @gaia-law/i18n-modules build && phraseapp push
will push all generated modules to phraseappphraseapp pull && npx @gaia-law/i18n-modules update
will update all modules with changes from phraseapp
i18n-modules
is also added to your npm env. You could add this to your package.json:
"scripts": {
"lang:push": "i18n-modules build && phraseapp push",
"lang:pull": "phraseapp pull && i18n-modules update",
"lang:clean": "i18n-modules clean",
"lang:sync": "npm run lang:push && npm run lang:pull",
}
NextJS users
NextJS builds in dev mode incrementally. That means with default config you will always have merge conflicts in the dictionaries folder. To avoid this only emit full dictionaries during full build.
This is an example implementation of next.config.js
that achieves this:
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
const I18nModules = require('@gaia-law/i18n-modules');
const getConfig = (phase) => {
const nextConfig = {
webpack: (config) => {
config.plugins.push(
new I18nModules({ emitFile: phase !== PHASE_DEVELOPMENT_SERVER }),
);
return config;
},
};
return nextConfig;
};
module.exports = getConfig;
Debugging
This module uses debug internally with label i18n_modules
. To debug use DEBUG=i18n_modules in your env: DEBUG=i18n_modules npx webpack
.