ngx-easy-i18n-js

December 18, 2023 ยท View on GitHub

Pure version angular for internalization (i18n).

Translations are static. If you change language you must refresh the page or use bootstrap extension.

Use EasyI18 Js library https://github.com/gabrie-allaigre/easy-i18n-js

Download and Installation

Install using npm:

npm install easy-i18n-js @ngx-easy-i18n-js/core --save

Open angular.json and under allowedCommonJsDependencies add:

"allowedCommonJsDependencies": [
"easy-i18n-js"
]

Usage in app.module.ts, add imports

import localeFr from '@angular/common/locales/fr';
import localeEn from '@angular/common/locales/en';

imports: [
  EasyI18nModule.forRoot({
    options: {
      logging: false
    },
    ngLocales: {
      'fr': localeFr,
      'en': localeEn,
    },
    defaultLanguage: 'en-US'
  })
]

Configuration

export interface EasyI18nModuleConfig {
  // Options fo easy i18 js library
  options?: EasyI18nOptions;
  // Use specific loader
  loader?: Provider;
  // Add Angular locale <code>import localeFr from '@angular/common/locales/fr';</code>
  ngLocales?: { [key: string]: any; };
  // Use browser language
  useBrowserLanguage?: boolean;
  // Default fallback language use if current language not found
  defaultLanguage?: string;
  // <code>exact</code> only fr-FR, <code>minimum</code> only fr, <code>all</code> fr-FR and fr
  discover?: 'exact' | 'minimum' | 'all';
}

In child module

imports: [
  EasyI18nModule
]

For change current locale use EasyI18nService

export class MyComponent {

  constructor(
    private easyI18nService: EasyI18nService
  ) {
  }

  public doChangeLanguage(locale: string): void {
    this.easyI18nService.registerCulture('fr');
  }
}

Force reload if change culture

 this.easyI18nService.registerCulture('fr', { reload: true });

Locale pipes

In HTML, uses locales pipes to get dates, numbers in locale format

pipedescriptionexample
localeDateSame as date{{ mydate | localeDate:'short' }}
localeNumberSame as number{{ mydate | localeNumber }}
localeCurrencySame as currency{{ mydate | localeCurrency }}
localePercentSame as percent{{ mydate | localePercent }}

Translate

Main function for translate your language keys

HTML file

In HTML template, with pipe, parameter is TrOptions

{{ 'hello' | tr }}
{{ 'hello_with_genre' | tr: { gender: 'male' } }}
{{ 'My name is {}' | tr: { args: ['Gabriel'] } }}

TrOptions arguments

Nametypeexample
argsstring[]['Gabriel', '20']
namedArgs{ [key: string]: string; } }{ name : 'Gabriel', age : '20' }
namespacestring'common'
gender'male' | 'female' | 'other'gender: 'other'

Directives

There are 2 differents directives

First is simple, translate [tr]

Directivedescriptionexample
trActive directive translate<span tr>hello</span>
trNamespaceAdd namespace<span tr trNamespace="common">hello</span>
trKeySet key (if empty use content)<span tr trKey="hello" trNamespace="common"></span>
trGenderGender<span tr trGender="male">hello_with_genre</span>
trArgsArguments<span tr [trArgs]="['Gabriel']">hello</span>
trNamedArgsNamed arguments<span tr [trNamedArgs]="{ name: 'Gabriel' }">hello</span>

Second, use HTML named arguments [trContent], replace {namedArg} with child element *trElement

Directivedescription
trContentActive content directive translate, get a key
trNamespaceAdd namespace
trGenderGender
trArgsArguments
trNamedArgsNamed arguments
demarcChange token start, end identifier

Examples

<!-- "hello_name": "My name is {name}" -->
<div trContent="hello_name">
    <span *trElement="'name'" style="color: red; font-size: 2rem; font-weight: bold">Gabriel</span>
</div>

<div trContent="My name is {name} and I live in {country}" style="color: blue;">
    <span *trElement="'name'" style="color: red; font-size: 2rem; font-weight: bold">Gabriel</span>
    <span *trElement="'country'">{{ var_country }}</span>
</div>

Typescript file

You can use extension methods of [String], you can also use tr() as a static function.

In typescript file, there is no need to inject EasyI18nService

'hello'.tr();
'hello_with_genre'.tr({ gender: 'male' });
tr('hello');
tr('hello_with_genre', { gender: 'male' });

Translate Plural

You can translate with pluralization. To insert a number in the translated string, use {}.

HTML file

In HTML template, with pipe, first parameter is number and second PluralOptions

{{ 'money' | plural:10 }}
{{ 'money_with_args' | plural:3: { args: ['Gabriel'] } }}

PluralOptions arguments

Nametypeexample
argsstring[]['Gabriel', '20']
namedArgs{ [key: string]: string; } }{ name : 'Gabriel', age : '20' }
namespacestring'common'
namestringmoney
numberFormatterFn(value: number) => string(value) => value.Precision(3)
gender'male' | 'female' | 'other'gender: 'other'

Directives

There are 2 different directives

First is simple, translate plural [plural]

Directivedescriptionexample
pluralActive directive translate<span [plural]="10">money</span>
pluralNamespaceAdd namespace<span [plural]="100" pluralNamespace="common">money</span>
pluralKeySet key (if empty use content)<span [plural]="100" pluralKey="money" pluralNamespace="common"></span>
pluralGenderGender<span [plural]="1" pluralGender="male">money_with_genre</span>
pluralArgsArguments<span [plural]="5" [pluralArgs]="['Gabriel']">money</span>
pluralNamedArgsNamed arguments<span [plural]="13" [pluralNamedArgs]="{ name: 'Gabriel' }">money</span>
pluralNameName value<span [plural]="4" pluralName="value">money</span>
pluralNumberFormatterFnFormatter function<span [plural]="10000" [pluralNumberFormatterFn]="myFn">money</span>

Second, use HTML named arguments [pluralContent], replace {namedArg} with child element *pluralElement

Directivedescription
pluralContentActive content directive translate, get a key
pluralValueActive content directive translate, get a value
pluralNamespaceAdd namespace
pluralGenderGender
pluralArgsArguments
pluralNamedArgsNamed arguments
pluralNameName value
pluralNumberFormatterFnFormatter function
demarcChange token start, end identifier

Examples

<!-- "money_content": { 
    "zero": "{name} not have money",
    "one": "{name} have {money} dollar",
    "two": "{name} have {money} dollars",
    "many": "{name} have {money} dollars",
    "other": "{name} have {money} dollars"
} -->
<div pluralContent="money_content" [pluralValue]="var_money" class="fst-italic text-gray-500">
    <span *pluralElement="'name'" style="color: red; font-size: 2rem; font-weight: bold">Gabriel</span>
    <span *pluralElement="'money'" style="color: blueviolet; font-weight: bold"
          [style.font-size]="(var_money / 10) + 'vw'">{{ var_money }}</span>
</div>

Typescript file

You can use extension methods of [String], you can also use plural() as a static function.

In typescript file, there is no need to inject EasyI18nService

'money_args'.plural(0, { args: ['Gabriel'] });
'money_args'.plural(1.5, { args: ['Gabriel'] });
plural('money_args', { args: ['Gabriel'] });

Store

Default store is EmptyEasyI18nStore

Use localStorage store, usage in app.module.ts, add provider

providers: [
  {
    provide: EasyI18nStore,
    useFactory: () => new LocalStorageEasyI18nStore('current-lang')
  }
]

Add HttpLoader

Standard

Load messages with HttpClient

Install using npm:

npm install @ngx-easy-i18n-js/http-loader --save

Usage in app.module.ts, add provider

providers: [
  {
    provide: EasyI18nLoader,
    deps: [HttpClient],
    useFactory: (httpClient: HttpClient) => new HttpEasyI18nLoader(httpClient)
  }
]

Change prefix or suffix with options

new HttpEasyI18nLoader(httpClient, {
  prefix: 'assets/',
  suffix: '.json5'
});
new HttpEasyI18nLoader(httpClient, {
  prefix: ['assets/common/i18n', 'assets/i18n'],
  suffix: '.json5'
});

Scoped loader

Load multiples files with scope

Usage in app.module.ts, add provider

providers: [
  {
    provide: EasyI18nLoader,
    deps: [HttpClient],
    useFactory: (httpClient: HttpClient) => new ScopedHttpEasyI18nLoader(httpClient, [
      { prefix: `/assets/i18n/` },
      { prefix: ['assets/common/i18n', 'assets/i18n/common'], scope: 'common' },
      { prefix: `/assets/i18n/errors/`, scope: 'errors' }
    ])
  }
]
{{ 'common.save' | tr }}
{{ 'errors.internal_server_error' | tr }}

Change suffix

new ScopedHttpEasyI18nLoader(httpClient, [
    { prefix: `/assets/i18n/` }
  ], {
  suffix: '.json5'
});

Append scoped loader for lazy routes

const routes: Routes = [
  {
    path: 'login',
    loadChildren: () => import('./login/login.module').then(m => m.LoginModule),
    canActivate: [
      appendScopedHttpEasyI18nLoader([
        { prefix: `/assets/i18n/login/`, scope: 'login' }
      ])
    ]
  }
];

Add Bootstrap

Bootstrap application, refresh application when culture change without reload page

Install using npm:

npm install @angular/cdk @ngx-easy-i18n-js/bootstrap --save

Usage

imports: [
  EasyI18nBootstrapModule.forRoot({
    bootstrap: AppComponent
  })
]

bootstrap: [EasyI18nBootstrapComponent]

And in index.html, replace <app-root></app-root> by <ngx-easy-i18n></ngx-easy-i18n>

For custom loading component

imports: [
  EasyI18nBootstrapModule.forRoot({
    bootstrap: AppComponent,
    loadingComponent: MyLoadingComponent
  })
]

bootstrap: [EasyI18nBootstrapComponent]