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
Try adjusting the initializer code, so that you are initializing using provideAppInitializer
and using importProvidersFrom
to add services for the module.
export function appInit() {
const i18next: ITranslationService = inject(I18NEXT_SERVICE);
return i18next.use(HttpApi).use(LanguageDetector).init({
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'],
},
});
}
export function localeIdFactory(i18next: ITranslationService) {
return i18next.language;
}
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideAnimationsAsync(),
{
provide: I18NEXT_SERVICE,
useValue: i18next
},
provideAppInitializer(appInit),
{
provide: LOCALE_ID,
deps: [I18NEXT_SERVICE],
useFactory: localeIdFactory,
},
importProvidersFrom([
I18NextModule.forRoot(),
]),
]
};