3
votes

from this link I tried to implement the i18 internationalization in my Angular 6 app in order to translate in english and italian some text and datetime in my html template.

https://next.angular.io/guide/i18n

I understood that this function consist in 3 phases:

1) Define translation text. No problem, I created a folder src/locale folder with 2 files messages.en.xlf (and message.it.xlf); here is an example for en version.

<trans-unit id="introductionHeader" datatype="html">
  <source>Hello i18n! (en-EN)</source>
  <note priority="1" from="description">An introduction header for this sample</note>
  <note priority="1" from="meaning">User welcome</note>
</trans-unit>

2) link this text to html pages with appropriates tag, and also here no problem, there is my example tag in my app.translations.html.

<h1 i18n="User welcome|An introduction header for this sample">
    Hello i18n!
</h1>

Now, I read the way (3. phase) to localize the app during startup (some editing in Angular.json file and start ng serve with configuration option); but, instead of this, I would change the language of the app programmatically. In other words, I want a command such as

SwitchAppLanguage('en')

for example, so that the user can change it self by a button or the app can do this reading the browser default language. How can I do this?

Thank you very much!

Edit: I tried to edit my angular.json file in this way

"configurations": {
  "production": {
     "i18nFile": "src/locale/messages.it.xlf",
     "i18nFormat": "xlf",
     "i18nLocale": "it",
(...)

And, after ng build and ng serve, I expect to see the italian text but it doesn't happens (and the app start and serve correctly). What do I wrong? Thanks!

2
There is no such thing. The app is localized at build time. If you want two languages, you need to build it twice, and to serve two different apps from your web server. To change the language, you need to load the other app. - JB Nizet
Is impossibile define 2 languages and tell to Angular "switch to this language"? It seems me very strange, are you sure? In this case,there are no ways for an user to choose a language during the app use, correct? - Archimede
Yes, I am sure. Yes, correct. It's written in the page you linked to, verbatim: When you internationalize with the AOT compiler, you must pre-build a separate application package for each language and serve the appropriate package based on either server-side language detection or url parameters. - JB Nizet
Once Ivy is there, you will be able to have a single app for all languages, but you'll still need to reload the app to switch the language. You can use ngx-translate, which has this feature (but is less efficient, and has other limitations) - JB Nizet
window.location.href = urlOfTheOtherApp;. - JB Nizet

2 Answers

2
votes

I solved this issue for dev-mode this way. maybe someone could benefit from this.

This is just a temporary solution untill Angular provides this feature in ivy i18n.

Drawback: can only be done when language files are generated to JSON :-(

polyfills.ts:

import { loadTranslations } from '@angular/localize';
import { isDevMode } from '@angular/core';

// All translation objects: must be json files for runtime-conditions! (XLF not supported)
import * as fr from "./i18n/messages.fr.json";
import * as nl from "./i18n/messages.nl.json";
import * as en from "./i18n/messages.json";


// Non-official translation loader to switch languages at runtime in development mode
if(isDevMode()){
  let translations = {};
  let lang = localStorage.getItem("lang");
  if(lang === "fr"){
    translations = fr;
  } else if (lang === "nl"){
    translations = nl;
  } else{
    translations = en;
  }

  // load translations at runtime
  loadTranslations(translations['default']['translations']);
}
0
votes

So if you use JIT, you can use webpacks loader to hotswap language files:

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { getTranslationProviders } from './app/providers/i18n.provider';
import { LocaleService } from './app/services/locale.service';

if ( environment.production ) {
  enableProdMode();
}

const locale = new LocaleService();
locale.getLocale().then( ( localeValue ) => {
  getTranslationProviders( localeValue ).then( providers => {
    platformBrowserDynamic().bootstrapModule( AppModule, { providers } )
      .catch( err => console.log( err ) );
  } );
} );

i18n.provider

import { TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core';
declare const require;

export function getTranslationProviders( locale: string ): Promise<any[]> {

  // Get the locale id as arugment or from the global
  //
  const localeValue = locale || document[ 'locale' ] as string;


  // return no providers if fail to get translation file for locale
  //
  const noProviders: Object[] = [];

  // No locale or AU, doesn't require translation
  //
  if ( !localeValue || localeValue === 'au' ) {
    return Promise.resolve( noProviders );
  }
  try {
    const translations = require( `raw-loader!../../locale/messages.${ localeValue }.xlf` );
    return Promise.resolve( [
      { provide: TRANSLATIONS, useValue: translations },
      { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }
    ] );
  } catch ( error ) {
    console.error( error );
    return Promise.resolve( noProviders );
  }

}

and the local.service

import { WindowRef } from 'app/services/windowRef.service';
import { environment } from 'environments/environment';


@Injectable()

export class LocaleService {
  _locale: string;
  set locale( val: string ) {
    this._locale = val;
  }
  get locale() {
    return this._locale;
  }


  constructor() {
    this.setLocale();
  }

  setLocale() {
    const winRef = new WindowRef();

    this.locale = ( environment.countryLookup[ document.location.hostname ] || 'au' ).toLowerCase();

    const match = document.location.search.match( /au|nz/ );

    if ( match ) {
      this.locale = match.shift();
    }
    // store locale in document
    //
    winRef.nativeWindow.document.locale = this.locale;
  }

  getLocale(): Promise<string> {
    return Promise.resolve( this.locale );
  }

}

You will have to refactor it a bit to match your project, but I hope you get the gist. This might be a solution until Ivy supports proper language switching.