Changelog
2017-02-23
- Upgrade to angular 2.4.8 and webpack 2.2.1
- Production sources compression
- i18n npm tasks added
Overview
Angular2 offers i18n native support. See following angular blog post. They don't provide much info, so let's see how to do it...
Project
Source code
Source code for following article can be found in GitHub:
https://github.com/alber999/angular2-webpack-starter
It's an Angular 2.4.8 webpack starter pack with native i18n support plus custom i18n management tasks
Environment
- node 6.9.2
- npm 3.10.9
- angular 2.4.8
- compiler-cli 2.4.8
- webpack 2.2.1
- karma 1.5.0
- typescript 2.1.5
- gulp 3.9.1
Installation
npm install
HTML Translation markup
Add i18n attribute to any HTML tag in your component templates and its content will be considered to be translated
<h1 i18n>WELCOME</h1>
This is just a simple example, to see more features see: https://github.com/StephenFluin/i18n-sample
Extract i18n messages from templates
i18n labels can be extracted from templates thanks to ng-xi18n task included in compiler-cli. To run this task execute following command from project root:
node_modules/.bin/ng-xi18n
We can create an npm task in package.json to avoid long command above
"scripts": {
"i18n:extract": "ng-xi18n",
...
Then run
npm run i18n:extract
You have to be careful when writing HTML i18n tags content since ng-xi18n works generating a hash id from HTML tag content without trimming. Therefore these two tags will have different hash ids and will be different entries.
<h1 i18n>WELCOME</h1>
<h1 i18n>
WELCOME
</h1>
In my opinion Angular team should fix this behavior by trimming tag content.
Once you run ng-xi18n a messages.xlf XLIFF file will be generated in project root directory. Sample:
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="ae89a08ab9c77434ca7b8b116498317ecac8f2d9" datatype="html">
<source>WELCOME</source>
<target/>
</trans-unit>
<trans-unit id="ecad2c91a275d1ef0b2c85f9cedc80fe2e62e976" datatype="html">
<source>
WELCOME
</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>
You can see above different entries for WELCOME label because of the lack of trimming.
Create custom language i18n XLF files
In following example we want to create translation for "es" and "en"
npm run i18n:create "es, en"
It will create following untranslated files (messages.xlf copies). Only i18n labels, no translations obviously
src/resources/i18n/messages.es.xlf
src/resources/i18n/messages.en.xlf
Then complete translation for each i18n label in those files
Update translations
When you change i18n labels (add, remove, rename) in html templates, you can generate i18n translations XLIFF file again (untranslated).
npm run i18n:extract
Merge new translations with former ones:
npm run i18n:update
- Existent i18n labels and translations in use will be kept
- Unused labels will be removed
- New labels will be added
Then, update translation targets for new labels in language XLIFF files:
src/resources/i18n/messages.es.xlf
src/resources/i18n/messages.en.xlf
Compile translations
Now is time to generate compiled TS translation files:
npm run i18n:compile
TS version will be generated from messages.en.xlf. It will be messages.en.ts
export const TRANSLATION_EN = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="ae89a08ab9c77434ca7b8b116498317ecac8f2d9" datatype="html">
<source>WELCOME</source>
<target>Welcome message 1!</target>
</trans-unit>
<trans-unit id="ecad2c91a275d1ef0b2c85f9cedc80fe2e62e976" datatype="html">
<source>
WELCOME
</source>
<target>Welcome message 2!</target>
</trans-unit>
</body>
</file>
</xliff>`;
Set current language
To configure current language, set Angular2 providers in main.ts file:
import {TRANSLATION_EN} from "./resources/i18n/messages.en";
...
platformBrowserDynamic().bootstrapModule(
AppModule,
{
providers: [
{provide: TRANSLATIONS, useValue: TRANSLATION_EN},
{provide: TRANSLATIONS_FORMAT, useValue: "xlf"},
{provide: LOCALE_ID, useValue: "en"}
]
});
This is just a sample. Of course you can implement you own logic.
Run application
npm start
Then open: http://docker.savethecode.angular2:8080/
Tests
npm run test
Coverage tests
npm run test:coverage
You can find coverage reports in coverage
directory
Production sources
compression-webpack-plugin
is used to compress final production sources. For example, vendor.js
size in dev mode is more than 8 MB, in prod env, without compression 1 MB, with compression 230 KB
npm run build
You can find production sources in dist
directory
For testing purposes, thanks to http-server
we can run following task to start in prod env with production sources:
npm run start:prod
The trick here is to edit dist/index.html
and add to polyfills and vendor js files the extension .gz
. See sample:
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>SaveTheCode Angular2 Webpack Starter</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../public/images/favicon.ico" rel="shortcut icon" type="image/x-icon">
<link href="/app.46eeea679591594be0c5.css" rel="stylesheet"></head>
<body>
<my-app>Loading...</my-app>
<script type="text/javascript" src="/polyfills.46eeea679591594be0c5.js.gz"></script>
<script type="text/javascript" src="/vendor.46eeea679591594be0c5.js.gz"></script>
<script type="text/javascript" src="/app.46eeea679591594be0c5.js"></script>
</body>
</html>
Tips
Compiled files dir
If you want to avoid messing with JS compiled files, configure outDir in tsconfig.json pointing out to a temporary directory:
{
"compilerOptions": {
...
"outDir": "./tmp"
}
}
Ignore XLF files
To avoid XLIFF files on compilation, add following entry in webpack.common.js:
module: {
rules: [
...
{
test: /\.xlf$/,
loader: 'ignore-loader'
}
]
}
Can be translation changed at runtime?
Unfortunately this feature is not available. Translations are loaded at bootstrap, not at runtime, so you cannot change language unless you re-bootstrap again by reloading page. I18n support is still tagged as "experimental" in angular2 core. It will be improved in next releases hopefully