Presented by Rahul Doshi | @doshprompt
“i18n or Internationalization is the process of designing a software application so that it can potentially be adapted to various languages and regions without engineering changes.”
“L10n or Localization is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text.”
Localization (which is potentially performed multiple times, for different locales) uses the infrastructure or flexibility provided by internationalization (which is ideally performed only once, or as an integral part of ongoing development).
The terms are frequently abbreviated to the numeronyms i18n (where 18 stands for the number of letters between the first i and last n in internationalization) and L10n respectively, due to the length of the words.
“The next billion will differ in more ways than just geography. Most new Internet users will not speak English as their first language”
It provides localization rules for various Angular components.
It works well for number systems, formatting, grouping and precision as well as decimal marks.
It also does datetime, currency formatting.
Used through the built-in angular filters.
Edition 1 provides most of the services that are similar to $locale along with most notably, Collation, in two scenarios: sorting a set of strings and searching within a set of strings. Collation is parameterized by locale and aware of Unicode.
Intl object Currently supported in IE11, Chrome and Firefox Nightly Builds controlled via a flag. Compatibility Table
Upcoming edition 2 will support some more common use-cases like message formatting: format a string with placeholders, including plural and gender support. However, it will be based on Es6 and will not be backwards compatible anymore.
angular-translate uses ICU's MessageFormat which uses a different kind of "interpolation", e.g. it uses single curly braces instead of double curly braces, which means one misses all features angular built-in interpolation brings (filters etc).
angular-gettext is too much magic, not enough control in the hands of the app developer. also, it bypasses certain useful angularjs functionality like the ngPluralize directive.
Most tutorials available on the web only address a very small part of the problem, some miss the point or don't acknowledge the larger issues.
If you want to know more follow the link from this Google Groups discussion thread, but it looks something like this:
Addressing all aspects of globalization with respect to your app is a large and complex problem. There are a lot of ways of approaching the issues, and no real right or wrong method for any of them.
So it's best to get started early -- the sooner, the better.
EN: desktop
FR:
Ordinateurs de bureau et portables
translates to 'desktop computer' since all French translations of 'desktop' mean 'table' (literal translation)
BUT
DE: Donaudampfschiffahrtsgesellschaftskapitän
EN:
Danube steamship company captain
Imagine the previous example,
what would have happened if each word in the English string was translated separately?
What would have become of 3/4 of them when the user switched to the German version of the page?
Another scenario, what if the order of words is different as per the grammar of different languages?
EN: My name is Rahul Doshi
HI: Mera naam hai Rahul Doshi
HI: Mera naam Rahul Doshi hai
Assuming that you will not be performing resource intensive operations like searching and sorting on the frontend, your API server will have ready to handle requests in multiple languages.
Any third party components that you use or build yourself should also support localization.
AngularJS already provides framework-level support for a lot of things so there is no need to re-invent the wheel, use what you already have and build better, more relevant stuff on top of it instead.
You just need to include the locale-specific add-on files but good to know about these things too.
“Unfortunately, there is no 100% reliable way in the browser of getting the user's locale information - unless you ask the user explicitly.”
You've probably done this hundreds of times:
var date = new Date();
And possibly displayed it in the browser's default locale by calling:
date.toLocaleDateString();
//returns e.g. "Friday, August 24, 2012"
There are some unreliable hacks, like querying navigator.language
in Chrome and Firefox,
or navigator.browserLanguage
in IE,
or looking at the HTTP Accept-Language
header using XHR.
So, just ask if you need to. Or provide a way for the user to select their locale preferences.
Then, convert what we know about the user's locale to a language tag, a simple string that represents the user's preferred language and, optionally, where they are (or wish they were for the purposes of communication).
You can store these choices by setting a cookie or in the browser's local storage as you prefer.
“Language tags are used to help identify languages, whether spoken, written, signed, or otherwise signaled, for the purpose of communication.”
Our solution is based on a simple architecture. We will have a folder for each language that we would like to support. Inside will be localized resource files for each route or module we want to support.
We will also pick a default language that will be used to fall back to the site’s native language if a given user’s language is not supported.
100% of the code is run on the front-end within the browser, and no special server-side support needs to be provided.
The proposed directory structure:
lang
├── en-US
│ ├── common.json
│ ├── login.json
│ ├── home.json
│ └── ...
├── fr-FR
│ ├── common.json
│ ├── login.json
│ ├── home.json
│ └── ...
└── ...
Below is an example of the file format:
{
"cancel": "Cancel",
"no": "No",
"ok": "OK",
"yes": "Yes"
}
A service will form the core of our localization engine, providing an interface that will be responsible for checking the user's language settings and requesting the appropriate resource based on the language.
It will also provide several methods, a lookup method that will return a localized string for a given key from the loaded resource file, a way to get/set the locale, and finally, a way to setup some defaults.
Lastly, it will notify us (via promises) when an individual file or a group of files has been successfully loaded into memory and is ready for use.
JavaScript:
$scope.strings = {};
locale.ready('common').then(function() {
$scope.strings.helloWorld = locale.getString('common.helloWorld');
});
HTML:
<p>{{ strings.helloWorld }}</p>
Edit in Plunker
Output:
Hello World!
Using Fliters:
<p>{{ 'common.helloWorld' | i18n }}</p>
Edit in Plunker
Output:
Hello World!
Improvements with a Directive:
<p data-i18n="common.helloWorld"></p>
Edit in Plunker
Output:
Hello World!
example.json
{
"nameOuput": "My name is %fullname"
}
Filter:
<p>{{ 'example.nameOutput' | i18n:'Rahul Doshi' }}</p>
Directive:
<p data-i18n="example.nameOutput" data-fullname="Rahul Doshi"></p>
Edit in Plunker
Good for when you want to avoid angular's interpolation in setting certain attributes on the DOM element such as placeholder for an input.
<input data-i18n-attr="{placeholder: 'login.emailPrompt'}">
Edit in Plunker
In your controller:
$scope.numCount = 1;
$scope.pluralStrings = {
nobodyIsViewing: 'common.nobodyIsViewing',
onePersonIsViewing: 'common.onePersonIsViewing',
manyPeopleAreViewing: 'common.manyPeopleAreViewing'
};
On the page:
<input type="number" min="0" data-ng-model="personCount">
<span
data-ng-pluralize
data-count="personCount"
data-when="{
'0': '{{ pluralStrings.nobodyIsViewing | i18n }}',
'1': '{{ pluralStrings.onePersonIsViewing | i18n }}',
'other': '{{ pluralStrings.manyPeopleAreViewing | i18n:personCount }}'
}">
</span>
Edit in Plunker
Experimental: using with ng-switch
<div data-ng-switch="user.gender">
<span> data-ng-switch-when="m" data-i18n="common.male"></span>
<span> data-ng-switch-when="f" data-i18n="common.female"></span>
</div>
ng-if also works with filter
Gender:
<span data-ng-if="user.gender == 'm'">{{ 'common.male' | i18n }}</span>
<span data-ng-if="user.gender == 'f'">{{ 'common.female' | i18n }}</span>
Edit in Plunker