0

I'm using the ngx-translate library to add multilingual support to my lazy loaded Angular website.
I was wondering how to properly add multilingual logics to the angular routing system.

Example

// HOME is my exported variable containing my routes
export const routes: Routes = [{
 path: HOME, 
 loadChildren: () => import('app/pages/home.module').then((m) => m.HomeModule),
}]

Now, when a user comes on my website, he should find the home pages with those URLs

  • English : my-domain/home
  • French : my-domain/acceuil
  • German : my-domain/startseite

I know, I could create multiple app-routing.module.ts files, one for each language, but when the website have many pages and should be translated in many languages, it become overcomplicated to use this technique.

Edit

An other Idea was to reload the page, be even through this solution, I'll then have to change the url, what is also complicated in an nested routing system. like :

  • English : my-domain/en/products/my-product
  • French : my-domain/fr/produits/mon-produit

2 Answers 2

1

First of all, here is the stackblitz of the working code

All the ideas presented in this answer are compiled together in the snippet (stackblitz) bellow:

https://stackblitz.com/edit/angular-ivy-bys583

About changing routes with no need to refresh

In order to change the routes without refreshing the page, you may access the list of routes in route.config and change the property path of the desired route.

Just to show how it works, see the simple example bellow, where the route /home is changed to /accueil, no need to refresh the app:

import { Router } from '@angular/router';

export class AppComponent {

  constructor(private router: Router){}

  changeHomeRoute(){
    for (let route of this.router.config){
      if (route.path == 'home'){
        route.path = 'accueil';
      }
    }
  }
}

Storing language in localStorage and setting default route on application start

Let´s work on a simple application that has three supported languages (en, fr and de) and only one component (HomeComponent).

First of all, as suggested by the OP, one option is to store the language in localStorage.

The paths of the routes may be stored in an const like that:

route-paths.ts

export const DEFAULT_LANGUAGE = "en";

export const ROUTE_PATHS = {
    de: {
        home: 'startseite'
    },
    en: {
        home: 'home'
    },
    fr: {
        home: 'accueil'
    }
};

During application first load the routes may be configured like that:

app-routing.module.ts

import { DEFAULT_LANGUAGE, ROUTE_PATHS } from './route-paths';

const routes: Routes = [
  {
    path: localStorage.getItem('lang') ? ROUTE_PATHS[localStorage.getItem('lang')]['home'] : ROUTE_PATHS[DEFAULT_LANGUAGE]['home'],
    component: HomeComponent
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Changing language triggers update in the routes and in the menu of the app

In the following code you can see examples of

  • Changing the language in the <select> and updating the routes accordingly (see method onChangeLang() that changes the routes using route.config[i].path).
  • Automatically updating the menu links once the routes have changed (see the ngFor and the routes() method).

app.component.html

<div>
  Language:
  <select (change)="onChangeLang($event)">
    <option *ngFor="let lang of languagesAvailable" [value]="lang" [selected]="langSelected == lang">{{lang}}</option>
  </select>

  <div>
    <span>Menu:</span>
    <a *ngFor="let route of routes() | keyvalue" [routerLink]="['/' + route.value]">
      {{route.value}} 
    </a>
  </div>
</div>

<router-outlet></router-outlet>

app.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { DEFAULT_LANGUAGE, ROUTE_PATHS } from './route-paths';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  languagesAvailable = ['de', 'en', 'fr'];
  
  langSelected = null;

  constructor(private router: Router){}

  ngOnInit(){
    this.langSelected = localStorage.getItem('lang');
    if (!this.langSelected){
      this.langSelected = DEFAULT_LANGUAGE;
      localStorage.setItem('lang', this.langSelected);
    }
  }

  onChangeLang(event){
    let previousLanguage = this.langSelected;
    let newLanguage = event.target.value;
    
    console.log('Routes before:');
    this.printRoutes();

    for (let route of this.router.config){
      for(let routeId in ROUTE_PATHS[previousLanguage]){
        let oldRoute = ROUTE_PATHS[previousLanguage][routeId];
        if (oldRoute == route.path){
          let newRoute = ROUTE_PATHS[newLanguage][routeId];
          console.log('old route for ' + routeId + ' is ' + route.path + '. Changing to ' + newRoute);
          route.path = ROUTE_PATHS[newLanguage][routeId];
        }
      }     
    };

    this.langSelected = newLanguage;
    localStorage.setItem('lang', this.langSelected);

    console.log('Routes after:');
    this.printRoutes();
  }

  routes(){
    return ROUTE_PATHS[this.langSelected];
  }

  printRoutes(){
    this.router.config.forEach(function(route, index, routes){
      console.log(route);        
    });
  }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Looks promising, thx a lot! But onChangeLang() only works for a one level arrays, what if I have nested routes? And with nested route, how would you change the actual url ? (I tried with localtion.replaceState() but couldn't manage the recursive function here.) And do you know if using localStorage in the routing, since this is a synchrone, may have any negative side effect?
The code can be adapted to nested routes. basically you may 1) change ROUTE_PATHS into an array for example: ROUTE_PATHS = { de: { home: ["info", "startseite"] } }; where the nested route is /info/startseite. 2) Adapt onChangeLang() so oldRoute is calculated iterating in route.children and then compared to route.path. 3) render <a [routerLink]> using something like route.value.join('/').
about localstorage side effects, I'm not sure.
But when I get this.router.config it only provides me the first route, So I'm not able to changes the children, I tried to look here stackoverflow.com/questions/37569936/… but it seems that you can't find loop through route anymore in from the version 8 of angular...
Your statement is not right. See the official doc of router in angular 10: angular.io/api/router/Router. config is still there and returns Routes. And the stackbltiz I have provided is looping in router.config.
|
0

I'm guessing HOME is a constant.

What if you create a config object that gets initialized / updated when you change language, and it simply changes the values.. for example:

const PATHS = {
  en: {
    home: 'home',
  },
  fr: {
    home: 'acceuil',
  },
  it: {
    home: 'inizio',
  }
}

and then you just substitute HOME with PATHS[language].home, and language is whatever the language is set to, it could even be another property of your config object.

config = {
  currentLanguage: 'en',
  paths: {},
}

as long as it's available to all, it should be fine (create a root config.ts object, and export these variables, and modify them wherever imported as needed)

export const routes: Routes = [{
 path: config.path[config.currentLanguage].home, 
 loadChildren: () => import('app/pages/home.module').then((m) => m.HomeModule),
}]

you could even make this become more specialized by creating a function / generator that retrieves what you want particularly, but that's a little way too much for this example

3 Comments

I tried your solution and come across 2 problem. 1 : I can't use the currentLanguage variable. So I did the same logic but with the localStorage PATH[localStorage.getItem('SELECTED_LANGUAGE')].home 2 : When I change the language, it didn't seems to update the angular route, so I had to reload the page to be able to have the newly translated route... Am I doing something wrong ?
when you change language you could do a reload() or a route to the newlyCalculated function (get route with language you had, then route to same route in the new language), routing won't reload the components so it will just update the route. I think localStorage is fine so it shouldn't be a problem.
It seems like angular does only load the route once, so a route to the newly calculated function won't work, at least not when I tried it. So I'm forced to do a reload() or to use the technique from @francisco neto, which consist of manually changing every route name into the new language. But reloading isn't a valid option for me, since I find it "hacky" and force reloading from the server.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.