i18n Tagged Template Literals



Table of Contents
Overview
This template literal tag adds support for i18n and l10n (translation and internationalization) to your JavaScript project.
It provides the following benefits:
Features
- Translate and internationalize your JavaScript project
- Translate your JavaScript library at buildtime
- Generate a schema of all i18n tagged template literals in your project for easy JSON based translations
- Export translations as CommonJS modules
- Validate translations and track translation coverage of your project
Compatibility
This library is using the ECMAScript Internationalization API. ES Internationalization API is implemented in all modern Browsers. For node.js projects you can use Andy Earnshaw's excellent Intl.js polyfill [#34]:
Examples
Installation
$ npm install es2015-i18n-tag --save
Usage
This library is available as CommonJS module and as UMD module that is consumable in CommonJS, AMD and with script tags.
The UMD module can be used in environments that don't support CommonJS modules. It is highly recommended to use es2015-i18n-tag as CommonJS module with babel and webpack or browserify. In a Node.js environment you have to use the Intl Polyfill to add support for additional locales [#34].
UMD module on unpkg.com
https://unpkg.com/es2015-i18n-tag/dist/lib/index.umd.min.js
Import and Configuration
import i18n, { i18nConfig } from 'es2015-i18n-tag'
i18nConfig({
locales: 'de-DE',
group: 'my-lib',
translations: {
"Hello ${0}, you have ${1} in your bank account.": "Hallo ${0}, Sie haben ${1} auf Ihrem Bankkonto."
},
number: {
[...options]
},
date: {
[...options]
}
})
const name = 'Steffen'
const amount = 1250.33
console.log(i18n`Hello ${ name }, you have ${ amount }:c in your bank account.`)
Currency formatting
i18nConfig({
number: {
currency: 'EUR'
}
})
console.log(i18n`Hello ${ name }, you have ${ amount }:c in your bank account.`)
console.log(i18n`Hello ${ name }, you have ${ amount }:c(USD) in your bank account.`)
Date formatting
i18nConfig({
locales: 'en-US',
date: {
hour12: false
}
})
console.log(i18n`Hello ${name}, the date is ${new Date(2012, 11, 20, 19, 0, 0)}:t.`)
Standard format strings
const date = new Date(2012, 11, 20, 19, 0, 0);
i18n`The date is ${date}:t(D).`
The following standard format strings can be applied to a template expression of type Date:
Format specifier | Description | Examples |
---|
"d" | Short date pattern | 12/20/2012 |
"D" | Long date pattern | Thursday, December 20, 2012 |
"f" | Full date/time pattern (short time) | Thursday, December 20, 2012, 7:00 PM |
"F" | Full date/time pattern (long time) | Thursday, December 20, 2012, 7:00:00 PM |
"g" | General date/time pattern (short time) | 12/20/2012, 7:00 PM |
"G" | General date/time pattern (long time) | 12/20/2012, 7:00:00 PM |
"M", "m" | Month/day pattern | December 20 |
"O", "o" | ISO 8601 pattern | 2012-12-20T18:00:00.000Z |
"R", "r" | RFC 1123 pattern | Thu, 20 Dec 2012 18:00:00 GMT |
"t" | Short time pattern | 7:00 PM |
"T" | Long time pattern | 7:00:00 PM |
"Y", "y" | Year month pattern | December 2012 |
Percentage formatting
console.log(i18n`Hello ${name}, the percentage is ${0.01}:p.`)
console.log(i18n`Hello ${name}, the percentage is ${0.005}:p(1).`)
i18nConfig({
locales: 'de-DE'
})
console.log(i18n`Hello ${name}, the percentage is ${0.01}:p.`)
Number formatting
console.log(i18n`Hello ${name}, the number is ${12345.678}:n(2).`)
i18nConfig({
locales: 'de-DE'
})
console.log(i18n`Hello ${name}, the number is ${12345.678}:n(2).`)
Pluralization
You can use nested templates for pluralization as shown in this example.
Nested templates
let hello = [
{ name: "Steffen", percentage: 0.2 },
{ name: "Jack", percentage: 0.8 }
]
console.log(i18n`
<users>
${hello.map((item) => i18n`
<user name="${item.name}">${item.percentage}:p</user>
`).join('')}
</users>
`)
NOTE: For easy translation of multiline template literals use one of the following json schema generators.
Standard format strings
You can add custom standard formatters similar to the predefined DateTime formatters. Valid types are date
, number
and string
.
i18nConfig({
standardFormatters: {
number: {
x: (locales, numberOptions, value) => value.toLocaleString(locales, Object.assign({}, numberOptions, { style: 'percent' }))
}
}
})
console.log(i18n`${0.77}:n(x)`)
Translation Groups
Translation groups can be very useful to group translations by context. It can also be useful to avoid key duplicates in larger projects.
You can use babel-plugin-i18n-tag-translate to inject the relative path of your module as group name. Babel will inject const __translationGroup = 'relative/path/to/module.ext'
into each module.
Babel generated file module groups
Example
.babelrc
{
"plugins": [
["i18n-tag-translate", {
"groupDir": "./src"
}]
]
}
Project Structure
.
├── src
| └── components
| ├── App.js
| └── Clock.js
├── .babelrc
translations.de.json
{
"components/App.js": {
"Welcome": "Willkommen"
},
"components/Clock.js": {
"Time": "Zeit"
}
}
App.js
i18n(__translationGroup)`Welcome`
i18n('components/Clock.js')`Time`
i18nGroup Class Decorator
import { i18nGroup } from 'es2015-i18n-tag'
class Clock {
tick() {
return this.i18n`Time: ${new Date()}:t(T)`
}
}
export default i18nGroup(__translationGroup)(Clock)
@i18nGroup(__translationGroup)
class Clock {
tick() {
return this.i18n`Time: ${new Date()}:t(T)`
}
}
export default Clock
Configuration Groups
Configuration groups should be used by libraries to avoid configuration overrides. Configuration groups are like namespaces and only applied if the group name is set via i18n tag or i18nGroup decorator.
i18n Option
i18n(__translationGroup, 'my-lib')`Welcome`
i18n('components/Clock.js', 'my-lib')`Time`
i18nGroup Class Decorator
import { i18nGroup, i18nConfig } from 'es2015-i18n-tag'
i18nConfig({
locales: 'de-DE',
group: 'my-lib'
translations: {
"components/App.js": {
"Welcome": "Willkommen"
},
"components/Clock.js": {
"Time": "Zeit"
}
}
})
class Clock {
tick() {
return this.i18n`Time: ${new Date()}:t(T)`
}
}
export default i18nGroup(__translationGroup, 'my-lib')(Clock)
@i18nGroup(__translationGroup, 'my-lib')
class Clock {
tick() {
return this.i18n`Time: ${new Date()}:t(T)`
}
}
export default Clock
Translating without template literals
If you have to translate variables that cannot be put into a template literal, you can use the i18n.translate()
helper function.
Simple string translation
i18n.translate('Welcome')
var somVar = 'translationkey'
i18n.translate(somVar)
Using formatters
const name = 'Steffen'
const amount = 1250.33
i18n.translate('Hello ${0}, you have ${1} in your bank account.', name, { value: amount, formatter: 'c'})
i18n.translate('Total: ${0}', { value: amount, formatter: 'd', format: 2})
Using config and translation groups
i18n(__translationGroup, 'my-lib').translate('Welcome')
i18n('components/Clock.js', 'my-lib').translate('Time')
class Clock {
tick() {
return this.i18n.translate('Time: ${0}', { value: new Date(), formatter: 't', format: 'T' })
}
}
export default i18nGroup(__translationGroup, 'my-lib')(Clock)
Translations as CommonJS Modules
If you are working on a multilingual library it might be useful to export i18n settings and translations as CommonJS modules. This can be easily accomplished with webpack and json-loader as shown in this example:
Example
./my-lib/de/index.js
import translations from 'json!../../translations/translation.de.json'
i18nConfig({
locales: 'de-DE',
group: 'my-lib'
number: {
currency: 'EUR'
},
translations
})
./my-lib/index.js
@i18nGroup('', 'my-lib')
class Clock {
tick() {
return this.i18n`Time: ${new Date()}:t(T)`
}
}
Import library with german translations into an app
import my-lib from 'my-lib'
import 'my-lib/de'
Tools
Build time translation
JSON Schema
Credits
Thanks to Jack Hsu for his initial draft of an i18n template literal.
License
Copyright (c) 2016-2019 Steffen Kolmer
This software is licensed under the MIT license. See the LICENSE
file
accompanying this software for terms of use.
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!