<p align="center">
<strong style="font-size: 36px;">i18n-js</strong>
</p>
<p align="center">
A small library to provide the <a href="https://rubygems.org/gems/i18n">i18n</a> translations on the JavaScript.
</p>
<p align="center">
<a href="https://github.com/fnando/i18n/actions?query=workflow%3Atests"><img src="https://github.com/fnando/i18n/workflows/tests/badge.svg" alt="Tests"></a>
<a href="https://www.npmjs.com/package/i18n-js"><img src="https://img.shields.io/npm/v/i18n-js/next.svg" alt="npm version"></a>
<a href="https://www.npmjs.com/package/i18n-js"><img src="https://img.shields.io/npm/dt/i18n-js.svg" alt="npm downloads"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
## Installation
- Yarn: `yarn add i18n-js@next`
- NPM: `npm install i18n-js@next`
## Usage
### Setting up
First, you need to instantiate `I18n` with the translations' object, the main
class of this library.
```js
import { I18n } from "i18n-js";
import translations from "./translations.json";
const i18n = new I18n(translations);
```
The `translations` object is a direct export of translations defined by
[Ruby on Rails](https://guides.rubyonrails.org/i18n.html). To export the
translations, you can use [i18n-js](https://github.com/fnando/i18n-js), a Ruby
gem that's completely disconnected from Rails and that can be used for the
solely purpose of exporting the translations, even if your project is written in
a different language. If all you care about is some basic translation mechanism,
then you can set the object like this:
```js
const i18n = new I18n({
en: {
hello: "Hi!",
},
"pt-BR": {
hello: "Olá!",
},
});
```
Each root key is a different locale that may or may not have the script code.
This library also supports locales with region code, like `zh-Hant-TW`.
Once everything is set up, you can then define the locale. `en` is both the
current and default locale. To override either values, you have to use
`I18n#defaultLocale` and `I18n#locale`.
```js
i18n.defaultLocale = "pt-BR";
i18n.locale = "pt-BR";
```
#### Base translations
This library comes bundled with all base translations made available by
[rails-i18n](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale).
Base translations allow formatting date, numbers, and sentence connectors, among
other things.
To load the base translations, use something like the following:
```js
import { I18n } from "i18n-js";
import ptBR from "i18n-js/json/pt-BR.json";
import en from "i18n-js/json/en.json";
const i18n = new I18n({
...ptBR,
...en,
});
```
### Updating translation store
Updating the translation store is trivial. All you have to do is calling
`I18n#store` with the translations that need to be merged. Let's assume you've
exported all your app's translations using
[i18n-js](https://github.com/fnando/i18n-js) CLI, using a separate file for each
language, like this:
- `translations/en.json`
- `translations/pt-BR.json`
This is how you could update the store:
```js
import { I18n } from "i18n-js";
import ptBR from "translations/pt-BR.json";
import en from "translations/en.json";
const i18n = new I18n();
i18n.store(en);
i18n.store(ptBR);
```
This method will allow you to lazy load translations and them updating the store
as needed.
```js
import { I18n } from "i18n-js";
async function loadTranslations(i18n, locale) {
const response = await fetch(`/translations/${locale}.json`);
const translations = await response.json();
i18n.store(translations);
}
const i18n = new I18n();
loadTranslations(i18n, "es");
```
### Translating messages
To translate messages, you have to use the `I18n#translate`, or its `I18n#t`
alias.
```js
i18n.locale = "en";
i18n.t("hello"); //=> Hi!
i18n.locale = "pt-BR";
i18n.t("hello"); //=> Olá!
```
You can also provide an array as scope. Both calls below are equivalent.
```js
i18n.t(["greetings", "hello"]);
i18n.t("greetings.hello");
```
Your translations may have dynamic values that should be interpolated. Here's a
greeting message that takes a name:
```js
const i18n = new I18n({
en: { greetings: "Hi, %{name}!" },
"pt-BR": { greetings: "Olá, %{name}!" },
});
i18n.t("greetings", { name: "John" });
```
#### Missing translations
A translation may be missing. In that case, you may set the default value that's
going to be returned.
```js
i18n.t("missing.scope", { defaultValue: "This is a default message" });
```
Default messages can also have interpolation.
```js
i18n.t("noun", { defaultValue: "I'm a {{noun}}", noun: "Mac" });
```
Alternatively, you can define a list of scopes that will be searched instead.
```js
// As a scope
i18n.t("some.missing.scope", { defaults: [{ scope: "some.existing.scope" }] });
// As a simple translation
i18n.t("some.missing.scope", { defaults: [{ message: "Some message" }] });
```
Default values must be provided as an array of objects where the key is the type
of desired translation, a `scope` or a `message`. The returned translation will
be either the first scope recognized, or the first message defined.
The translation will fall back to the `defaultValue` translation if no scope in
`defaults` matches and if no `message` default is found.
You can enable translation fallback with `I18n#enableFallback`.
```js
i18n.enableFallback = true;
```
By default missing translations will first be looked for in less specific
versions of the requested locale and if that fails by taking them from your
`I18n#defaultLocale`.
```js
// if i18n.defaultLocale = "en" and translation doesn't exist
// for i18n.locale = "de-DE" this key will be taken from "de" locale scope
// or, if that also doesn't exist, from "en" locale scope
i18n.t("some.missing.scope");
```
Custom fallback rules can also be specified for a specific language. There are
three different ways of doing it so. In any case, the locale handler must be
registered using `i18n.locales.register()`.
```js
// Using an array
i18n.locales.register("no", ["nb", "en"]);
// Using a string
i18n.locales.no.register("nb");
// Using a function.
i18n.locales.no.register((locale) => ["nb"]);
```
By default a missing translation will be displayed as
`[missing "name of scope" translation]`. You can override this behavior by
setting `i18n.missingBehavior` to `"guess"`.
```js
i18n.missingBehavior = "guess";
```
The "guess" behavior will take the last section of the scope and apply some
replace rules; camel case becomes lower case and underscores are replaced with
space. In practice, it means that a scope like
`questionnaire.whatIsYourFavorite_ChristmasPresent` becomes
`what is your favorite Christmas present`.
There's also a strategy called `error`, which will throw an exception every time
you fetch a missing translation. This is great for development. It'll even end
up on your error tracking!
```js
i18n.missingBehavior = "error";
```
To detect missing translations, you can also set
`i18n.missingTranslationPrefix`.
```js
i18n.missingTranslationPrefix = "EE: ";
```
The same `questionnaire.whatIsYourFavorite_ChristmasPresent` scope would
converted into `EE: what is your favorite Christmas present`. This is helpful if
you want to add a check to your automated tests.
Finally, you can completely override the missing translation strategy by setting
it to a function. The following example will return `null` for every missing
translation.
```js
i18n.missingTranslation = () => null;
```
#### Pluralization
This library has support for pluralization and by default works with English,
and similar pluralized languages like Portuguese.
First, you have to define your translations with special keywords defined by the
pluralization handler. The default keywords are `zero`, `one`, and `other`.
```js
const i18n = new I18n({
en: {
inbox: {
zero: "You have no messages",
one: "You have one message",
other: "You have %{count} messages",
},
},
"pt-BR":
评论0