Is it possible have working Angular i18next multilingual translations after page reload and...

Discussão em 'Angular' iniciado por Orist Timemaker, Fevereiro 2, 2025.

  1. I have installed angular-i18next library and I want to have working multilingual translations after I going to change language and reload the page. Now its working, but I need to have implemented lazy loading at Angular current component, now I want rid of lazy loading, because it's redudant for this time. Here is my code:

    app.config.ts

    import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
    import { provideRouter } from '@angular/router';
    import { routes } from './app.routes';
    import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
    import { I18NEXT_SERVICE, I18NextModule, defaultInterpolationFormat } from 'angular-i18next';
    import i18next from 'i18next';
    import HttpApi from 'i18next-http-backend';
    import LanguageDetector from 'i18next-browser-languagedetector';

    const i18nextOptions = {
    debug: true,
    fallbackLng: 'en',
    supportedLngs: ['en', 'de', 'cz'],
    ns: ['translation', 'validation', 'error'],
    interpolation: {
    format: I18NextModule.interpolationFormat(defaultInterpolationFormat)
    },
    backend: {
    loadPath: '/assets/i18n/{{lng}}.json',
    },
    detection: {
    order: ['localStorage', 'navigator', 'htmlTag'],
    caches: ['localStorage'],
    },
    };

    // Initialize i18next before providing it
    i18next
    .use(HttpApi)
    .use(LanguageDetector)
    .init(i18nextOptions);

    export const appConfig: ApplicationConfig = {
    providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideAnimationsAsync(),
    {
    provide: I18NEXT_SERVICE,
    useValue: i18next
    },
    I18NextModule.forRoot({}).providers!
    ]
    };


    app.routes.ts

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

    export const routes: Routes = [
    {
    path: '',
    loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent),
    title: 'App Home'
    },
    {
    path: 'languages',
    loadComponent: () => import('./pages/languages/languages.component').then(m => m.LanguagesComponent),
    title: 'App languages'
    }
    ];


    app.component.ts

    import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
    import { Router, RouterOutlet } from '@angular/router';
    import { CommonModule } from '@angular/common';
    import { I18NextModule } from 'angular-i18next';

    @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
    standalone: true,
    imports: [
    RouterOutlet,
    CommonModule,
    I18NextModule
    ]
    })
    export class AppComponent implements OnInit{

    constructor(private router: Router) {}

    ngOnInit(): void {
    const setup = localStorage.getItem('setup');
    if (!setup) {
    this.router.navigate(['/languages']);
    }
    }
    }


    language-picker.component.ts

    import { ChangeDetectionStrategy, signal, Component, EventEmitter, Inject, OnInit, Output } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { MatButtonModule } from '@angular/material/button';
    import { MatIconModule } from '@angular/material/icon';
    import { I18NextModule, ITranslationService, I18NEXT_SERVICE } from 'angular-i18next';
    import { Language } from '@app/enums/language.enum';

    @Component({
    selector: 'app-language-picker',
    standalone: true,
    imports: [
    CommonModule,
    MatButtonModule,
    MatIconModule,
    I18NextModule
    ],
    templateUrl: './language-picker.component.html',
    styleUrls: ['./language-picker.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class LanguagePickerComponent implements OnInit {
    @Output() nextStep = new EventEmitter<void>();
    languageEnum: typeof Language = Language;
    language = signal<string>(Language.NONE);

    constructor(@Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) {
    const savedLanguage = window.sessionStorage.getItem('selectedLanguage') as Language;
    if (savedLanguage) {
    this.language.set(savedLanguage);
    }
    }

    async ngOnInit(): Promise<void> {
    // Subscribe to language changes
    this.i18NextService.events.initialized.subscribe((initialized) => {
    if (initialized) {
    this.language.set(this.i18NextService.language);
    }
    });
    }

    selectLanguage(lang: Language) {
    window.sessionStorage.setItem('selectedLanguage', lang);

    if (lang !== this.i18NextService.language) {
    this.i18NextService.changeLanguage(lang).then(() => {
    this.language.set(lang);
    });
    }
    this.nextStep.emit();
    }
    }


    language-picker.component.scss

    .language-select {
    .row {
    .col-lg-4 {
    img {
    cursor: pointer;
    height: 125px;

    &:hover {
    transform: scale(1.1);
    transition: transform 0.3s ease;
    }

    &:not:)hover):not(.selected) {
    transform: scale(1);
    transition: transform 0.3s ease;
    }

    &.selected {
    border-radius: 5px;
    transform: scale(1.3) !important;
    transition: transform 0.3s ease;
    }
    }
    }
    }
    }


    language-picker.component.html

    <div class="px-xs-2 px-sm-2 px-md-2 px-lg-5 py-3 py-sm-3 py-md-3 py-lg-3 d-flex flex-column row-gap-2 align-items-center column-gap-3">
    <div class="d-flex flex-column text-center">
    <h1 class="text-center">{{ 'Select language' | i18next }}</h1>
    <p>{{ 'Selected language will be used for the setup' | i18next }}</p>
    </div>
    <section class="language-select">
    <div class="row g-5 flex-lg-row flex-column align-items-center py-2">
    <div class="col-lg-4 col-12 text-center">
    <img
    width="200"
    height="125"
    src="assets/images/de.webp"
    class="img-fluid"
    [class.selected]="language() === languageEnum.DE"
    (click)="selectLanguage(languageEnum.DE)"
    >
    </div>
    <div class="col-lg-4 col-12 text-center">
    <img
    width="200"
    height="125"
    src="assets/images/cz.webp"
    class="img-fluid"
    [class.selected]="language() === languageEnum.CZ"
    (click)="selectLanguage(languageEnum.CZ)"
    >
    </div>
    <div class="col-lg-4 col-12 text-center">
    <img
    width="200"
    height="125"
    src="assets/images/gb.webp"
    class="img-fluid"
    [class.selected]="language() === languageEnum.EN"
    (click)="selectLanguage(languageEnum.EN)"
    >
    </div>
    </div>
    </section>
    </div>


    Now I need this app.routes.ts looks like this (without lazy loading):

    import { Routes } from '@angular/router';
    import { HomeComponent } from './pages/home/home.component';
    import { LanguagesComponent } from './pages/languages/languages.component';

    export const routes: Routes = [
    {
    path: '',
    component: HomeComponent,
    title: 'App home'
    },
    {
    path: 'languages',
    component: LanguagesComponent,
    title: 'App languages'
    }
    ];


    And not getting this warnings and have working current translations where is current language stored in session storage after page reload. How can I achieve this?

    Console warnings and logs (when I not using lazy loading):

    language-picker.component.html:3 i18next: hasLoadedNamespace: i18next was not initialized undefined

    language-picker.component.html:3 i18next::translator: key "Select language" for languages "en" won't get resolved as namespace "translation" was not yet loaded This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!

    language-picker.component.html:3 i18next::translator: missingKey undefined translation Select language Select language

    language-picker.component.html:4 i18next::translator: missingKey undefined translation Selected language will be used for the setup Selected language will be used for the setup

    language-picker.component.html:3 i18next: hasLoadedNamespace: i18next was not initialized (2) ['de', 'en']

    language-picker.component.html:3 i18next::translator: key "Select language" for languages "de, en" won't get resolved as namespace "translation" was not yet loaded This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!

    language-picker.component.html:3 i18next::translator: missingKey de translation Select language Select language

    language-picker.component.html:4 i18next::translator: missingKey de translation Selected language will be used for the setup Selected language will be used for the setup

    Continue reading...

Compartilhe esta Página