8

I want to build a web-application with angular2 and bundle this with webpack. What is the best way for providing multiple languages:

i18n-plugin: https://github.com/webpack/i18n-webpack-plugin

or

ng2-translate: https://github.com/ocombe/ng2-translate

Yves M.
  • 29,855
  • 23
  • 108
  • 144
circy
  • 199
  • 1
  • 3
  • 10
  • I've wrote a blogpost about this: https://lingohub.com/blog/2016/10/i18n-l10n-angularjs-apps-development-deployment/ comparing angular-translate with the Angular 2 approach – Betty St Oct 20 '16 at 09:20

5 Answers5

9

I got it working with webpack using the cookbook. The xliff file has to be converted to ts like so:

export const TRANSLATION_SV = `<?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="a73e2898b9e1126ed19dbabe4b5c3715a84db61a" datatype="html">
      <source>Category</source>
      <target>Kategori</target>
    </trans-unit>
    </body>
  </file>
</xliff>`;

Then in the main.ts it has to be added

import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID  } from '@angular/core';
import { TRANSLATION_SV } from './locale/messages.sv';

and inserted to the bootstrap step:

platformBrowserDynamic().bootstrapModule(AppModule, {
    providers: [
      {provide: TRANSLATIONS, useValue: TRANSLATION_SV},
      {provide: TRANSLATIONS_FORMAT, useValue: "xlf"},
      {provide: LOCALE_ID, useValue:'sv'}
    ];
});
  • IMHO that's a simpler approach than what the cookbook suggests. The only drawback being that you have to wrap the translation file manually in a TypeScript file. – Stephan Rauh Jan 06 '17 at 15:13
  • 2
    very nice answer! it helped a lot to understand whats going on. The converting of the xlf file can be avoided with using the raw-loader. – Simon Zyx Jan 10 '17 at 15:01
  • 1
    something like: ``{ test: /\.xlf$/, include: helpers.root('locale'), loader: ['raw-loader'] }`` – Simon Zyx Jan 10 '17 at 15:02
  • 1
    and than use ``import * as translationText from './locale/messages.xlf'`` – Simon Zyx Jan 10 '17 at 15:04
  • 1
    @SimonSeyock how exactly do you import the text as messages.xlf when you have multiple files in your locale directory (ie: messages.en.xlf, messages.de.xlf, etc.)? The import doesn't seem known until runtime? – Shawn May 09 '17 at 18:51
  • 1
    You can import multiple files and use the one you need. Another option is to use AOT compiler (https://angular.io/docs/ts/latest/cookbook/i18n.html#!#aot) and compile different packages for each language and then decide inside of the .html file which file to load. – Simon Zyx May 10 '17 at 18:25
  • I opted to use different bundles for translations as you mentioned with AOT. I am also using dynamic imports via webpack to conditionally load any additional locale specific assets I need (in my case moment js locale file). – Shawn May 16 '17 at 02:03
  • @Shawn How did you realized the conditional load together with AOT? Can you post an example or a plunkr, please. – CSchulz Jun 13 '17 at 13:54
  • @CSchulz that is probably a different question to post but from a high-level here is what's involved: * webpack config file using string-replace-loader and a compiler directive like comment (ex: // #DynamicImport-Locale can be whatever you want though) * A function in your main.ts or bootstrapper that returns a promise and then calls the platformBrowserDynamic().boostrapModule(module) method * Function that returns a resolved promise that uses the compiler directive you defined and webpack should replace it with result = System.import(''); or the like for your scenario And... – Shawn Jun 15 '17 at 15:02
  • * Probably need systemjs type file until ts adds support for dynamic imports – Shawn Jun 15 '17 at 15:03
  • I avoided compiling multiple packages with provider factories: `{provide: TRANSLATIONS, useFactory: (locale) => locale==='en'?TRANSLATION_EN:TRANSLATION_PL, deps:[LOCALE_ID]}` and `{provide: LOCALE_ID, useFactory: () => { ... some logic to find out browser language and store in cookie ... return locale;} }` – m4js7er Aug 02 '17 at 12:58
8

If you are using angular-cli you can follow these steps:

Be aware that your app must be AOT compatibe, so you should be able to build it with --aot switch:

ng build --aot
  1. Extract messages with ng xi18n command with location for translation file given:

    ng xi18n --output-path src/i18n
    
  2. You will get src/i18n/messages.xlf file. Copy this file and translate messages to languages you need:

    src/i18n/messages.en.xlf
    src/i18n/messages.pl.xlf
    
  3. Serve/build your app with ng serve / ng build command (change locale accordingly):

    ng serve --i18n-file=src/i18n/messages.en.xlf --locale=en --i18n-format=xlf --aot
    
Community
  • 1
  • 1
lukk
  • 3,164
  • 1
  • 30
  • 36
  • What is the most profitable difference to the i18n plugin from webpack? – circy Mar 05 '17 at 21:00
  • and how can you change the language of the app within the app? This is my main problem and the reason why I want to do this with JIT instead of AOT, but I can find no help for this – gkri Jun 23 '17 at 12:21
  • unfortunately, you have to make separate build for each language and provide correct routing on the server side eg. using custom url `localhost:8080/webapp/en` – lukk Jun 26 '17 at 09:45
  • 2
    @lukk - holy shit, that's such a bad design. Who in the world decided that, was everyone in Google high that day? I think I'll try my luck with ng2-translate first... – Davor Jun 26 '17 at 14:26
6

in case someone still wondering how to use angular 2 localization with webpack loader, i have modified the provider angular 2 cookbook provided;

First create i18n.provider.ts

import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID } from '@angular/core';
import { Observable } from "rxjs/Rx";
export function getTranslationProviders(): Promise<Object[]> {


  // Get the locale id from the global
  const locale = document['locale'] as string;
  // return no providers if fail to get translation file for locale
  const noProviders: Object[] = [];
  // No locale or U.S. English: no translation providers
  if (!locale || locale === 'en') {
    return Promise.resolve(noProviders);
  }
  // Ex: 'locale/messages.fr.xlf`
  const translationFile = `./src/app/localezation/messages.${locale}.xlf`;
  var provider = getTranslationsWithSystemJs(translationFile)
    .then((translations: string) => [
      { provide: TRANSLATIONS, useValue: translations },
      { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
      { provide: LOCALE_ID, useValue: locale }
    ])
    .catch(() => noProviders); // ignore if file not found

  debugger;
  return provider;
}
declare var System: any;
function getTranslationsWithSystemJs(file: string) {
// changes Start here 
  var text = "";
  var fileRequest = new XMLHttpRequest();
  fileRequest.open("GET", file, false);
  fileRequest.onerror = function (err) {
    console.log(err);
  }
  fileRequest.onreadystatechange = function () {
    if (fileRequest.readyState === 4) {
      if (fileRequest.status === 200 || fileRequest.status == 0) {
        text = fileRequest.responseText;
      }
    }
  }
  fileRequest.send()
  var observable = Observable.of(text);
  var prom = observable.toPromise();
  return prom; 
}

notice the changes are :

  1. i used jxjs library to create observable/ promise
  2. i used XMLHttpRequest to get the localezation files

Second in the main.ts files change the bootstrapping the same as mentioned in angular cookbook

getTranslationProviders().then(providers => {
  const options = { providers };
  platformBrowserDynamic().bootstrapModule(AppModule, options);
});
Nour Berro
  • 550
  • 5
  • 14
3

Angular 2 Final release has i18n native support https://angular.io/docs/ts/latest/cookbook/i18n.html

Check another answer https://stackoverflow.com/a/39004058/1267942 with example and some details about usage.

ng2-translate is too verbose comparing to the native implementation. Also the author of ng2-translate is a big contributor to angular 2 i18n documentation

Community
  • 1
  • 1
Andrei Zhytkevich
  • 8,039
  • 2
  • 31
  • 45
  • 14
    But this cookbook still refers to SystemJS as an integration mechanism instead of Webpack. How do we integrate the XLIFF file into compilation with Webpack? – Sebastien Oct 13 '16 at 13:51
  • @Sebastien If you don't want to do it in a separate process, you can use the AoT Webpack plugin from https://www.npmjs.com/package/@ngtools/webpack – Spock May 16 '17 at 08:28
  • For anyone who has come from AngularJS's angular-translate, the i18n native support seems like a bizarre approach. It requires the app to be completely rebuilt and separately deployed for each language. I can't imagine what the admin overhead on this is like when you get into 4/5/6 languages which would be typical on an internal business app. At first glance it looks like ngx-translate is closer to angular-translate in that it can set up language from the server on startup, switch languages on the fly etc. – IrishDubGuy Mar 13 '18 at 13:37
0

A slightly modified version of @M.Nour Berro's answer.

I made this change as synchronous xhr's are deprecated and possibly support might be removed later.

function getTranslations(filePath: string): Promise<string> {
  var text = '';
  return new Promise<string> ((resolve, reject) => {
    const fileRequest = new XMLHttpRequest();
    fileRequest.open('GET', filePath, true);
    fileRequest.onerror = function (err) {
      console.log(err);
      reject(err);
    };
    fileRequest.onreadystatechange = function () {
      if (fileRequest.readyState === 4) {
        if (fileRequest.status === 200 || fileRequest.status === 0) {
          text = fileRequest.responseText;
          resolve(text);
        }
      }
    };
    fileRequest.send();
  });
}
M22an
  • 1,242
  • 1
  • 18
  • 35