Skip to content

Romanchuk/angular-i18next

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

npm version Downloads Build Status Coverage Status Commitizen friendly paypal GitHub stars

angular-i18next

!!! NEW release 19.1.0 with angular 19 codestyle. Try provideI18Next() and give me your feedback

Best i18next integration with angular

Live DEMO

Features

  • Native i18next options
  • Promise initialization
  • i18next plugin support
  • Events support
  • Namespaces lazy load
  • i18next native format support
  • document.title localization
  • Error handling strategies
  • i18next namespaces and scopes (prefixes) for angular modules and components
  • AOT support
  • SSR support
  • Providers for unit testing
  • Angular Package Format support
  • Zoneless compatible

Cheers

Star this project

Hey dude! Help me out for a couple of 🍻!

Поддержи проект - угости автора кружечкой пива!

paypal

Available Submodules (optional)

  • angular-i18next/ssr: Adds Server Side Rendering support.
  • angular-i18next/forms: Provides localization for @angular/forms.
  • angular-i18next/testing: Offers features for testing.

Installation

1. Install package

 npm install i18next --save
 npm install angular-i18next --save

2. Initialize i18next before angular application and provide

Angular would not load until i18next initialize event fired

export function appInit(i18next: ITranslationService) {
    return () => i18next.init();
}
  providers: [
    provideAppInitializer(appInit()),
    provideI18Next(
      withCustomErrorHandlingStrategy(StrictErrorHandlingStrategy)
    )
  ] 

Usage

Pipes

Use "i18next" pipe to translate key:

  <div>{{ 'test' | i18next }}</div>

Passing "t options":

    <div>{{ 'test' | i18next: { count: 5, nsSeparator: '#' } }}</div>

Trigger native i18next format method by using I18NextFormatPipe or I18NextPipe with option 'format':

{{ 'any_key' | i18next | i18nextFormat }}

{{ 'any_key' | i18next: { format: 'cap' } }}

{{ 'any_key' | i18nextCap }}

Note: Using "i18nextCap" you will get the same result as i18next: { format: 'cap' }

REMEMBER that format will not work until you set "interpolation.format" function in i18next options.

angular-i81next has static method static interpolationFormat(customFormat: Function = null): Function that can be used as default interpolation format function (it provides 'upper', 'cap' and 'lower' formatters). You also can pass your custom function to be called after library formatters:

import { defaultInterpolationFormat, interpolationFormat } from "angular-i18next";


const i18nextOptions = {
  supportedLngs: ['en', 'ru'],
  ns: [
    'translation',
    'validation',
    'error',
  ],
  interpolation: {
    format: interpolationFormat((value, format, lng) => {
      // extend interpolation format
      if(value instanceof Date)
        return moment(value).format(format);
      return value;
    });
    // format: interpolationFormat(defaultInterpolationFormat)
  }
};

i18nextEager pipe

This is the impure analog of i18next pipe that is subscribed to language change, it will change string right away to choosen language (without reloading page).

Warning!: Use i18nextEager only in combine with OnPush change detection strategy, or else (default change detection) each pipe will retrigger more than one time (cause of performance issues).

Subscribing to event observables:

this.i18NextService.events.languageChanged.subscribe(lang => {
  // do something
})

Add a provider to module/component if you want to prefix child i18next keys:

{
  provide: I18NEXT_NAMESPACE,
  useValue: 'feature' // set 'feature:' prefix 
}
{
  provide: I18NEXT_SCOPE,
  useValue: 'person' // set 'person.' prefix 
}

Since v3.1.0+ it is possible to pass array of namespaces (or scopes). Key would fallback to next namespace in array if the previous failed to resolve.

[feature_validators:key, validators:key]

{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature_validators', 'validators']
}

_NOTE:* Do NOT use default (or custom) i18next delimiters in namespace names.

Document title

If you want to turn on document title localization resolve Title as I18NextTitle imported from 'angular-i18next':

  providers: [provideI18Next(withTitle())]

Routes example:

const appRoutes: Routes = [
  { 
    path: 'error',
    component: AppErrorComponent,
    data: { title: 'error:error_occured' }
  },
  { 
    path: 'denied',
    component: AccessDeniedComponent,
    data: { title: 'error:access_denied' }
  }
];

Ways to use I18NextService in your code:

Warning: Injection of I18NextService is possible, but it would not consider I18NEXT_NAMESPACE and I18NEXT_SCOPE providers. There are 2 possible reasons to inject I18NextService: initialization and subscription to its events. In all other cases inject I18NextPipe.

  1. Recommended way: Inject via I18NEXT_SERVICE token. By default it will inject instance of I18NextService.
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) 
  1. Legacy way: Inject via type
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              private i18NextService: I18NextService) 

Error handling

Error handling is now configurable:

  1. By default i18next promise will use NativeErrorHandlingStrategy. I18Next would be always resolve successfully. Error could be get from 'then' handler parameter.
  2. Set StrictErrorHandlingStrategy to reject load promises (init, languageChange, loadNamespaces) on first load fail (this was default in v2 but changed to fit native i18next behavior:
  providers: [
    provideI18Next(
      withCustomErrorHandlingStrategy(StrictErrorHandlingStrategy)
    )
  ]    

Lazy loading

Use i18NextNamespacesGuard in your routes to to load i18next namespace.

Note: It is not necessary to register lazy loading namespaces in global i18next options.

{
    path: 'rich_form',
    loadComponent: () => RichFormComponent,
    providers: [
      {
          provide: I18NEXT_NAMESPACE, // namespace to start in component
          useValue: 'feature.rich_form',
      },
    ],
    canActivate: [i18NextNamespacesGuard('feature.rich_form')]
 }

Use I18NextService.loadNamespaces() method to load namespaces in code.

Cookbook

i18next plugin support

import { I18NextModule, ITranslationService, I18NEXT_SERVICE } from 'angular-i18next';
//  import Backend from 'i18next-xhr-backend'; //for i18next < 20.0.0
import HttpApi from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

...

i18next.use(HttpApi)
       .use(LanguageDetector)
       .init(i18nextOptions)

Server side rendereng (SSR)

  1. Provide for server:
import { provideI18Next, withTitle } from 'angular-i18next';
import { withSSR } from 'angular-i18next/ssr';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    provideServerRouting(serverRoutes),
    provideI18Next(withTitle(), withSSR()),
  ],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
  1. Configure i18next in server.ts (Example):

Auto error message for @angular/forms

Use i18nextValidationMessage directive with formControlName

import { I18NextValidationMessageDirective } from 'angular-i18next/forms'

@Component({
  imports: [I18NextValidationMessageDirective]
})

<input i18nextValidationMessage class="form-control" type="text" formControlName="lastName"/>

There is priority order for validation messages:

  1. namespace + control_specific with form hierarchy
  2. namespace + Common validation key(like required)
  3. control_specific with form hierarchy
  4. Common validation key like required

Also you can interpolate control.error values. For example: For validator Validators.min(1)

"min": "Minimal {{min}}. Actual: {{actual}}."

en.validation.json

{
    "required": "Field is required.",
    "pattern": "$t(validation:_fill) valid value.",
    "_fill": "Please fill in",
    "control_specific": {
        "technicalContact": {
            "firstName": {
                "required": "$t(validation:_fill) technical specialist's first name."
            },
            "lastName": {
                "required": "$t(validation:_fill) technical specialist's last name."
            },
            "middleName": {
                "required": "$t(validation:_fill) technical specialist's patronymic."
            }
        }
    }
}

Testing

  import { withSSR } from 'angular-i18next/testing';

  TestBed.configureTestingModule({
      imports: [ProjectComponent],
      providers: [
        provideI18NextMockAppInitializer(),
        provideI18Next(withMock())
      ]
  });

What to do if... ?

New angular version released, but angular-i18next is not released YET

Angular releases mostly don't break angular-i18next, but we cannot tell ahead that current version of angular-i18next will work correctly with latest angular version.

You can override an angular-i18next peerDependencies in your package.json on your own risk:

"overrides": {
  "angular-i18next": {
    "@angular/common": "*",
    "@angular/core": "*",
    "@angular/platform-browser": "*"
  }
}

In-project testing

You might want to unit-test project components that are using i18next pipes

Example tests setup: libs/angular-i18next/src/tests/projectTests

Demo

Live DEMO Demo app source code available here: https://github.com/Romanchuk/angular-i18next-demo

Articles