Fallback
Doing graceful fallbacks is a core principle of i18next. This enables you to display the most accurate content possible, while not repeating content over and over.
By default, if a variant (containing region, script, etc) is not found, i18next will look for the same key in the broader version of that language. With this in mind, a common strategy if you're supporting language variants is to write common text inside the pure language, specifying only what differs in the variants.
Example:
// fallback to one language
i18next.init({
lng: "en-GB",
resources: {
"en-GB": {
"translation": {
"i18n": "Internationalisation"
}
},
"en": {
"translation": {
"i18n": "Internationalization",
"i18n_short": "i18n"
}
}
}
}, () => {
i18next.t('i18n'); // -> finds "Internationalisation"
i18next.t('i18n_short'); // -> falls back to "en": "i18n"
// force using en
i18next.t('i18n', { lng: 'en' }); // -> "Internationalization"
});
If you can not provide the preferred language for a user, you can specify another language as fallback. This is useful to indicate the main language or, for instance, if you want to keep the fallbacks different per region.
// fallback to one language
i18next.init({
fallbackLng: 'en'
});
// fallback ordered
i18next.init({
fallbackLng: ['fr', 'en']
});
// fallback depending on user language
i18next.init({
fallbackLng: {
'de-CH': ['fr', 'it'], //French and Italian are also spoken in Switzerland
'zh-Hant': ['zh-Hans', 'en'],
'es': ['fr'],
'default': ['en']
}
});
// function that returns an array of fallbacks
// your function may also return a string or object as above
i18next.init({
fallbackLng: (code) => {
if (!code || code === 'en') return ['en-US'];
const fallbacks = [code];
// We maintain en-US and en-AU. Some regions will prefer en-AU.
if (code.startsWith('en-') && !['en-US', 'en-AU'].includes(code)) {
if (['en-GB', 'en-NZ', 'en-IR'].includes(code)) fallbacks.push('en-AU');
else fallbacks.push('en-US');
return fallbacks;
}
// add pure lang
const langPart = code.split('-')[0];
if (langPart !== code) fallbacks.push(langPart);
// finally, developer language
fallbacks.push('dev');
return fallbacks;
}
});
The default is set to
dev
which means developer language. At first this might look strange to set the default to a language, but this enables to set the saveMissing
feature to send new keys to that developer specific language. From there your translators can modify the texts to a translation file containing, for instance, proper English, including defined terminology. For production use, just set fallbackLng
to an existing language.i18next by default loads its translations from one file named
translation
. However, you can configure it to load from multiple files, called namespaces.Besides defining multiple namespaces to load from, you also can set fallback namespaces. Thus, if a key to translate isn't found in the given namespace, it will look it up in the indicated fallbacks.
app.json
{
"title": "i18next"
}
common.json
{
"button": {
"save": "save"
}
}
Sample
i18next.init({
// files to load
ns: ['app', 'common'],
// default namespace (needs no prefix on calling t)
defaultNS: 'app',
// fallback, can be a string or an array of namespaces
fallbackNS: 'common'
}, () => {
i18next.t('title') // -> "i18next"
i18next.t('button.save') // -> "save" (fallback from common)
// without fallbackNS you would have to prefix namespace
// to access keys in that namespace
// and this is not recommended when used in combination with natural language keys
i18next.t('common:button.save') // -> "save"
// better use the ns option:
i18next.t('button.save', { ns: 'common' }) // -> "save"
});
If a key does not return a value the key acts as fallback:
i18next.t('notExistingKey'); // -> "notExistingKey"
So you could configure i18next to have the key being the fallback instead of loading a fallback language:
de.json
{
"No one says a key can not be the fallback.": "Niemand sagt ein key kann nicht als Ersatz dienen."
}
i18next.init({
lng: 'de',
// allow keys to be phrases having `:`, `.`
nsSeparator: false,
keySeparator: false,
// do not load a fallback
fallbackLng: false
});
i18next.t('No one says a key can not be the fallback.')
// -> "Niemand sagt ein key kann nicht als Ersatz dienen."
i18next.t('This will be shown if the current loaded translations do not have this.');
// -> "This will be shown if the current loaded translations do not have this."
While this works and might reduce files to load it makes the management of translations a lot harder as you will need to update changes to fallback values in code and JSON files.
Possible - but not recommended.
In addition to the above, if you want missing values to fallback to the key in cases where the keys (e.g. got extracted by a code parser) exist in your JSON translation file with empty string as value, you also need this setting:
// allow an empty value to count as invalid (by default is true)
returnEmptyString: false
Calling the t function with an array of keys enables you to translate dynamic keys providing a non specific fallback value.
As a sample think of an error code you get and you like to show a specific warning in some cases:
translation.json
{
"error": {
"unspecific": "Something went wrong.",
"404": "The page was not found."
}
}
Sample
// const error = '404';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "The page was not found"
// const error = '502';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "Something went wrong"
Last modified 1yr ago