# Internationalization (i18n)
If you need to support multiple languages, there are some ways to do it in your application.
There are a few commons approaches for translations. One is to put all strings into a separate file and refer to them by their keys. This is a bit cumbersome during normal development, since you can't read the text in the templates; you need to refer to the translation file. Our approach here will be to leave the main language text straight in the file and use tools to generate the translation files. We'll use PO files, which have support over a lot of editors, but in the end of this chapter we'll talk briefly about the alternative.
We'll once again have to write a small wrapper to mix the NativeScript and the web version.
# Key-based approach versus natural language
Using key-based translations instead of text has wide support, for example through vue-i18n or through i18next (which also supports natural language). You do something like this:
<div id="app">
<p>{{ $t("message.hello") }}</p>
<input v-model="name" :placeholder="$t('message.yourname')" />
</div>
Then you define your keys in a separate file like this:
const messages = {
en: {
message: {
hello: "hello world",
yourname: "Your name",
},
},
es: {
message: {
hello: "¡hola!",
yourname: "Su nombre",
},
},
};
This avoids a separate step to generate translations, but it can be hard to make sure that translations are in synchrony. If you change something in English, how to know if it was changed in Japanese? You need a translation management tool for that.
Our approach requires a separate build step to generate the translation files. It uses the gettext system, which is widely used and a traditional open source tool. You get a <translate>
tag, and filters for Vue. It will look like this:
<div id="app">
<!-- basic usage, generates a span tag -->
<translate>Hello!</translate>
<!-- handling plurals -->
<translate :translate-n="count" translate-plural="%{ count } cars">
%{ count } car
</translate>
<!-- using a different tag -->
<translate tag="h1">Hello!</translate>
<!-- interpolation -->
<translate>Hello %{ name }</translate>
<!-- translating parameters with a filter -->
<input v-model="name" :placeholder="'Your name' | translate" />
</div>
Both approaches work and have their pros and cons. Keys can add more flexibility -- they're always plain code after all -- but we'll use natural language because:
- you see it on the code, and people can change it directly there without referring to other files.
- there are tools to find what translations are stale automatically and to update the translation files.
- gettext/PO is a widely used standard, with several editors that support it.
# Tools for i18n
We'll use (easygettext)[https://github.com/polyconseil/easygettext] to extract our translations for the code. It supports Vue and Nativescript vue.
yarn add easygettext vue-gettext nativescript-localize --dev
So let's add a few useful commands to our package.json
:
{
"scripts": {
"translations:make": "make translations"
}
}
TODO
Here's how our flow will work:
- Write your code with the
<translate>
and| translate
calls using always the same base language. - Run
yarn run translations:make
to update the.po
files with your strings. - Edit the
.po
files that are generated. - Run
yarn run translations:make
again to compile the files for NS and Web.
# Web
We'll use the (vue-gettext plugin)[https://github.com/Polyconseil/vue-gettext/] for our translations on the web.
// boot external components
import Vue from "vue";
import GetTextPlugin from "vue-gettext";
import translations from "../locale/translations.json";
const availableLanguages = {
en_US: "English",
es_ES: "Español",
};
const bestLocale = ""; // see below
Vue.use(GetTextPlugin, {
availableLanguages,
defaultLanguage: bestLocale,
translations: translations,
silent: true,
});
Vue.filter("translate", (value) => {
return !value ? "" : Vue.prototype.$gettext(value.toString());
});
Finding what is the best locale to fill bestLocale
for a client is tricky. It can come from many places:
- the URL. You can use a
https://example.com/en/xxxx
URL format, or perhaps add the language in the query withhttps://example.com/xxxx?lang=en_US
. - a previously saved language preference in local storage or cookie.
- from the navigator itself, which presents a lists of languages it supports.
The choice is up to you.
# NativeScript
NS has a simple plugin to implement i18n, and we can get the locale information from the device. We'll add this code to main.native.js
:
<x<x< @ /../client/src/main.native.js#i18n
While we provide an equivalent $gettext()
, at this point TNS does not support plurals. We don't have standalone text in NS templates like in HTML, so we don't have a <translate>
tag. We'll always use the | translate
filter instead.
# Third party code localization
Often you'll use plugins that have text themselves. Many of them have i18n support and often provide translations in many languages. momentjs
is a good example for that.