@nicholasperetti/rollup-plugin-i18n v0.0.5
rollup-plugin-i18n
Use i18n in your app with rollup!
Install
yarn add -D @nicholasperetti/rollup-plugin-i18n
or
npm i -D @nicholasperetti/rollup-plugin-i18n
Usage
In your rollup.config.js
file:
import i18n from '@nicholasperetti/rollup-plugin-i18n'
export default {
plugins: [
i18n({
/* options */
}),
],
}
Your translations.js
file
export const languages = {
EN: 'en'
IT: 'it'
FR: 'fr'
}
export default {
"appName": {
[languages.EN]: 'My App',
[languages.FR]: 'Mon appli',
[languages.IT]: 'La mia App',
}
}
In your code
console.log(__('appName'), availableLanguages)
Globals
It's very likely that you'll need to know which languages are available, which of them is active and more.
In order to provide to you this runtime info, this plugin will allow you to import this informations via the following modules:
i18n:languages
Returns an array of strings. Every element of the array represent one of
the languages that you've set in the translations.js
file.
You can use these values to redirect the user to the bundle you want.
This is useful if you want to show a dropdown with the available languages.
i18n:active-language
Returns a string that represent the active language that has been rendered in the running code.
This is useful if you want to show to the user which language is active
i18n:is-root-bundle
Returns a boolean. This flag will be true
if the user is running the root
bundle instead of the translated one.
For example, the user is surfing on yoursite.com/
instead of yoursite.com/en/
.
How it works
After rollup wrote the files, the plugin reads them, analyzes all the chunks and
replace all the __()
occurences with the respecitve value.
Where does the translated code will be put?
The plugin creates a directory for each language that you specify in the
translations.js
file. The whole build is being kind of copy-pasted in the
language directory and then transformed
So, are my assets beign duplicated for every language?
Nope!
This plugins copies only the chunks to be sure everything has been translated. All the assets and other static files will be symlinked.
This means that you'll have your assets
directory in the root of your
output.dir
, and every build will have a symlink to that.
This solution saves a lot of space on disk when you build your code.
What happens to the original files?
The original files will be translated to your default language. If you don't specify one, the plugin will get the first language available and use it as default language.
You can set your default langauge with the defaultLanguage option
Dynamic translation keys
The plugin will try to replace every __()
occurency with the right translation.
Anyhow, there are cases where you don't know what the translation will be.
Maybe, the translation key is given to you by an API base on a user action.
For those cases the plugin defines the __()
globally and inject in your code
the dictionary of the translations for a specific language.
Note: If you're sure you won't need the dynamic translations feature, make
sure to use the disableDynamicTranslations
option.
The translations.js file
You can store all your translations in the translations.js
file (you can override
this with the translationsPath
option).
This file behaves in a different way compared to similar i18n plugins.
The common approach (and its problems)
It's a common practice to group the translations under a certain languange, like this:
{
"en": {
"Title": "Title"
},
"it": {
"Title": "Titolo"
},
"fr": {
"Title": "Titre"
}
}
Anyhow, this approach make it easy to miss information along the way. Since we need to repeat every key in the config object, we might mispell that key for some language and generate a bug.
We might even forget to translate some keys for some languages and no tool will aler us of the bug.
The solution adopted
In order to solve those issue and ensure consistency within your translations, this plugins organises data by content instead of language, like this:
{
"Title": {
"en": "Title",
"it": "Titolo",
"fr": "Titre"
}
}
Why?
It's more common to change the content of the app instead of add/remove languages. This approach make the maintance of translations way more easy.
Also, since the keys are being written only once, it's impossible to mistype a certain key in your configuration.
This is really easy to use because the human brain oraganise the information by content, not by language. So this schema reflects way more the way humans thinks about translations.
Structure of the file
This file must exports the following things:
languages
This must be an object containing all the languages supported by your app. If you forget to translate some key in yor config, this plugin will make the build fail, warning you about what translation is missing
The keys of the object will be used by you in order to reference the language in your translations object, like this:
export const languages = { EN: 'en' }
// Optional
export const defaultLanguage = languages.EN
export default {
Title: {
[languages.EN]: 'Title',
},
}
default
You must export as default your translations object, like this:
export default {
Title: {
[languages.EN]: 'Title',
},
}
defaultLanguage
Specify wich language will be the default language.
This is useful to set a default langauge when your users visit yourapp.domain/
instead of yourapp.domain/en/
.
Options
translationsPath
Tell the plugin where it can find your translations file. By default, it
will look for translations.js
.
import i18n from '@nicholasperetti/rollup-plugin-i18n'
export default {
plugins: [
i18n({
translationsPath: './i18n/translations.js',
}),
],
}
disableDynamicTranslations
By default the plugin injects the translations of a specific language in your code in order to be able to dynamically translate all the keys you want to.
The downside of this approach is that, as your translations grows, your bundle grows too.
So, if you're sure you don't need to dynamically resolve your translations, you
can set this option to true
and the plugin won't inject neither the translations
dictionary nor the fallback function.
Typescript support
In order to use this within your Typescript codebase you need to add the definition
of the __()
function. If you already have a global .d.ts
file add it in there.
Otherwise, create a global.d.ts
and include it in your tsconfig.json
.
Copy paste this definition in there:
declare function __(translationKey: string): string;
declare module 'i18n:languages' {
const content: Array<string>
export default content
}
declare module 'i18n:active-language' {
const content: string
export default content
}
declare module 'i18n:is-root-bundle' {
const content: boolean
export default content
}
More information about typescript global declarations can be found here