9

Im building a small Angular Application with Angular's i18n setup. Everything ist working fine, except for the translations of the url paths and slugs. I tried a possible solution with providing a routing module per language (as described here), but this did not work.

I thought that I could do something like the following, but I can't figure out where to inject LOCALE_ID:

app-routing.module.ts

import { LOCALE_ID, NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MainComponent } from './main/main.component';

const i18nRoutes = {
    de: {
        main: 'inhalte',
        // ...
    }, 
    fr: {
        main: 'contenu',
        // ...
    }
}

const currentLanguage = i18nRoutes[ LOCALE_ID ]; // <-- Apparently not working, since I have to inject LOCALE_ID. But where?

const routes: Routes = [
    {
        path: '',
        redirectTo: currentLanguage.main,
        pathMatch: 'full'
    },
    {
        path: currentLanguage.main + '/:key',
        component: MainComponent
    }
    // ...
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Update for clarification

in angular.json, I set configurations for the building process in each language (taken from here, with a view modifications for 2018)

angular.json

{
    // ...
    "projects": {
        "my-app": {
            // ...
            "architect": {
                "build": {
                    // ...
                    "configurations": {
                        // ...
                        "de": {
                            "i18nFile": "src/i18n/de.xliff",
                            "outputPath": "dist/de",
                            "i18nFormat": "xlf",
                            "i18nLocale": "de",
                            "baseHref": "/de/"
                            // ...
                        },
                        "fr": {
                            "i18nFile": "src/i18n/fr.xliff",
                            "outputPath": "dist/fr",
                            "i18nFormat": "xlf",
                            "i18nLocale": "fr",                            
                            "baseHref": "/fr/",
                            // ...
                        }
                   }
              }
          }
     }
}

For building all apps at once, I then enter npm run buildall, which executes the following in package.json:

package.json

{
    "name": "my-app",
    // ...
    "scripts": {
        // ...
        "buildall": "for lang in de fr;do ng build --configuration=$lang; done"
    }
}

which generates all apps in a subdirectory in the dist folder just fine.

So, to come back to my original question: The provided answer by Exterminator does not fit my needs, since

  • I cannot set a fixed locale while bootstrapping
  • Injecting LOCALE_ID in the constructor is too late since I need the value in app-routing.module.ts

I hope I explained enough. But maybe I misunderstood something completely. In any case, thanks in advance for any help. I am still learning and I must admit that a few concepts are still blurry to me.

uruk
  • 1,094
  • 2
  • 14
  • 26

2 Answers2

19

add this to the app.module

providers: [{provide: LOCALE_ID, useValue: 'fr-FR'}]

then call it using the following method where ever you want

import {LOCALE_ID} from '@angular/core';

  constructor(@Inject(LOCALE_ID) locale: string){
    console.log('locale', locale);
  }

also you can use this method

platformBrowserDynamic([{provide: LOCALE_ID, useValue: 'en-EN'}]).bootstrapModule(AppModule, {providers: [{provide: LOCALE_ID, useValue: 'en-EN'}]});
Exterminator
  • 1,221
  • 7
  • 14
  • 3
    But wouldn't that mean that when building the app, the LOCALE_ID would always be 'fr-FR'? – uruk Sep 14 '18 at 13:53
  • For more reference check [Internationalization (i18n)](https://angular.io/guide/i18n) – Exterminator Sep 14 '18 at 13:55
  • I don't want to set it, but to use the provided value when building the app. – uruk Sep 14 '18 at 17:59
  • I shared both thing, how to set it and use it as well.You can import it and the use it just the way i defined – Exterminator Sep 14 '18 at 18:03
  • Updated my question for clarification – uruk Sep 15 '18 at 08:57
  • Have you read the documentation from the link i provided you? – Exterminator Sep 15 '18 at 18:26
  • If you read the documentation is you should be able to solve the problem, first of all the configuration you have defined is incorrect and try creating the language file using the angular command. – Exterminator Sep 15 '18 at 18:40
  • I think you did not understand me. The apps are built in german and french without problems, the xliff-files are implemented fine, everything is working, except the translations of the routes. But to make sure, I will re-read the documentation once more... Thanks anyway! – uruk Sep 15 '18 at 19:24
  • this `platformBrowserDynamic([{provide: LOCALE_ID, useValue: 'en-EN'}])` did the trick for me, you dont have to add locales in app.module, just in the bootstrap file – Skorunka František Nov 22 '19 at 12:21
3

I had the same exact problem and this is how I solved it.

I created an app-locale.ts file that only had one constant that it exports which can then be used for LOCALE_ID provider to set the locale id.

src/app/app-locale.ts

/*
    WARNING: This file is to help facilitate the auotmation of the build
    process. In order to set the locale during development, change
    the value by hand to the target locale. If the file name or 
    location is changed make sure to update the build scripts
    accordingly.
*/ 
export const APP_LOCALE_ID:string = "es-es";

I used the above constant in my app.module.ts to provide the LOCALE_ID.

src/app/app.module.ts

import { LOCALE_ID, NgModule, TRANSLATIONS, TRANSLATIONS_FORMAT } from "@angular/core";
import { APP_LOCALE_ID } from './app-locale';
...
providers: [{
      provide: TRANSLATIONS_FORMAT,
      useValue: "xlf"
    }, { 
      provide: TRANSLATIONS, 
      useFactory: (locale) => {
        locale = locale || 'en-US'; 
        return require(`raw-loader!../locale/messages.${locale}.xlf`);
      },
      deps: [LOCALE_ID] 
    }, {
      provide: LOCALE_ID,
      useValue: APP_LOCALE_ID
    }
  ],
  ...

Now I need a small script that would run before the build starts which will set the local id in the file app-locale.ts. So I wrote this small node script and put it in a separate "scripts" folder.

scripts/set-app-locale.js

const fs = require('fs');
const argv = require('minimist')(process.argv.slice(2));

var version = '0.0.1',
    appLocale = "en-US",
    appLocaleFile = "./src/app/app-locale.ts",
    help = `
Set the static locale for the app.
Usage: node <path>set-app-locale.js [options]

Options:
  --version         Show version number                                  [boolean]
  --help, -h        Show help                                            [boolean]
  --app-locale      Target locale id                                     [string]
  --app-locale-file                                                      [string]
                    Path and name of app-locale that contains only one constant which
                    is a const string that holds the locale value. 
                    [default: "${__dirname}/src/app/app-locale.ts"]   `;

var setArgs = function(){
    if (argv.version){
        console.log(version);
        return false;
    }
    if (argv.h || argv.help){
        console.log(help);
        return false;
    }
    appLocale = argv["app-locale"] || appLocale;
    appLocaleFile = argv["app-locale-file"] || appLocaleFile;
    return true;
}
var setLocale = function(locale){
    var fileData = 
`/*
    WARNING: This file is to help facilitate the automation of the build
    process. In order to set the locale during development, change
    the value by hand to the target locale. If the file name or 
    location is changed make sure to update the build scripts
    accordingly.
*/ 
export const APP_LOCALE_ID:string = "${locale}";`;
    fs.writeFile(appLocaleFile, fileData, function(err) {
        if(err) {
            return console.log(err);
        }
        console.log(`App locale changed to ${locale}`);
    }); 
}

setArgs() && setLocale(appLocale);

Next I would go ahead and change my package.json to include this to set the locale id.

package.json

....
"config": {"locales": "en-US es-es"},
  "scripts": {
    ...
    "i18n-build": "for lang in $npm_package_config_locales; do node ./scripts/set-app-locale.js --app-locale=$lang && ng build --prod --configuration=$lang; done"
  }
....

I use xliffmerge which is part of @ngx-i18nsupport. This helps me avoid overwriting previously extracted and translated strings every time I extract strings.

So now whenever I build the app for all the supported locales, the LOCALE_ID gets set appropriately. I am sure there must be other ways to solve this problem but this is how I solved it.

Angry Samurai
  • 66
  • 2
  • 7