Reputation: 77
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
Upvotes: 1
Views: 36
Reputation: 57986
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(),
]),
]
};
Upvotes: 1