ngx-translate/core

Correct implementation in angular 18.1

mzeromski opened this issue · 1 comments

So I spent some time doing a correct implementation in angular18.1, here you have my code, with some service implementation, copy paste and it works

Below you can fetch code from git, and see working example.

app/config/httpLoaderFactory.ts

all required features in one file
app/config/httpLoaderFactory.ts

import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { LanguageSwitchService } from '../core/language-switch/language-switch.service';

const httpLoaderFactory: (http: HttpClient) => TranslateHttpLoader = (http: HttpClient) =>
  new TranslateHttpLoader(http, './assets/i18n/', '.json?v=v' + Math.random());

export const provideTranslation: () => {
  loader: {
    provide: typeof TranslateLoader;
    useFactory: (http: HttpClient) => TranslateHttpLoader;
    deps: [typeof HttpClient];
  };
} = () => ({
  loader: {
    provide: TranslateLoader,
    useFactory: httpLoaderFactory,
    deps: [HttpClient],
  },
});

export function initializeTranslation(): () => void {
  const translateService: TranslateService = inject(TranslateService);
  const languageSwitchService: LanguageSwitchService = inject(LanguageSwitchService);
  return () => {
    translateService.addLangs(LanguageSwitchService.availableLanguages);
    translateService.setDefaultLang(LanguageSwitchService.defaultLanguage);
    languageSwitchService.initLanguageFromLocalStorage();
  };
}

main.js

then in main.js execute it (remove others stuff)

export const appConfig: ApplicationConfig = {
  providers: [
    ...
    importProvidersFrom([TranslateModule.forRoot(provideTranslation())]),
    {
      provide: APP_INITIALIZER,
      useFactory: initializeTranslation,
      multi: true,
      deps: [TranslateService],
    },
  ],
};

LanguageSwitchService

now add LanguageSwitchService

@Injectable({
  providedIn: 'root',
})
export class LanguageSwitchService {
  public static readonly availableLanguages: string[] = ['en', 'pl'];

  public static readonly defaultLanguage: string = LanguageSwitchService.availableLanguages[0];

  private translateService: TranslateService = inject(TranslateService);

  public initLanguageFromLocalStorage(): void {
    const languageFromStorage: string | null = localStorage.getItem('language');
    if (
      languageFromStorage &&
      LanguageSwitchService.availableLanguages.includes(languageFromStorage)
    ) {
      this.setLanguage(languageFromStorage);
    }
  }

  public getCurrentLanguage(): string {
    if (
      this.translateService.currentLang &&
      this.translateService.currentLang !== LanguageSwitchService.defaultLanguage
    ) {
      return this.translateService.currentLang;
    }
    return LanguageSwitchService.defaultLanguage;
  }

  public setLanguage(languageId: string): void {
    if (LanguageSwitchService.availableLanguages.includes(languageId)) {
      this.translateService.use(languageId);
      localStorage.setItem('language', languageId);
    }
  }
}

component

then add some component to handle switcher, here is example with primeng, key is to call

this.languageSwitchService.setLanguage("en");

export class LanguageSwitchComponent {
  private languageSwitchService: LanguageSwitchService = inject(LanguageSwitchService);

  private flagConverter: Record<string, string> = { en: 'gb' };

  public readonly languages: MenuItem[] = LanguageSwitchService.availableLanguages.map(language => {
    return {
      label: `language-switch.${language}`,
      id: language,
      flag: this.flagConverter[language] ?? language,
    };
  });

  public currentLanguage: MenuItem = this.languages.filter(
    i => i.id === this.languageSwitchService.getCurrentLanguage()
  )![0];

  changeLanguage(): void {
    this.languageSwitchService.setLanguage(this.currentLanguage.id!);
  }
}

and html (quite complicated becouse Im using some flags

<p-dropdown
  [options]="languages"
  optionLabel="label"
  styleClass="w-30"
  [(ngModel)]="currentLanguage"
  (onChange)="changeLanguage()">
  <ng-template pTemplate="selectedItem">
    <div class="align-items-center flex content-center gap-2" *ngIf="currentLanguage">
      <div class="flex content-center">
        <span
          class="flag flag:{{
            currentLanguage['flag']!.toUpperCase()
          }} mt-1 rounded-lg scale-150 mr-1"></span>
      </div>

      <div>{{ currentLanguage.label! | translate }}</div>
    </div>
  </ng-template>

  <ng-template let-language pTemplate="item">
    <div class="align-items-center flex gap-2">
      <span
        class="flag flag:{{
          language['flag']!.toUpperCase()
        }} mt-1 rounded-lg scale-150 mr-1"></span>
      <div>{{ language.label | translate }}</div>
    </div>
  </ng-template>
</p-dropdown>

Download code and example how it works
https://github.com/mzeromski/angular-starter (you can give a star ;)
https://angular-starter.com/starter

Thanks for providing this code.

I've updated our tutorial.
https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate

I'll also update the ngx-translate documentation soon.