Skip to main content

Quickstart

In this quick start you'll learn how to do a basic i18n setup.

Installation

npm install --save nestjs-i18n

Setup translation files

By default nestjs-i18n uses the I18nJsonLoader loader class. This loader reads translations from json files. Create a folder named i18n in the src folder of your project.

package.json
package-lock.json
...
src
└── i18n
├── en
│   ├── events.json
│   └── test.json
└── nl
   ├── events.json
   └── test.json
src/i18n/en/test.json
{
"HELLO": "Hello",
"PRODUCT": {
"NEW": "New Product: {name}"
},
"ENGLISH": "English",
"ARRAY": ["ONE", "TWO", "THREE"],
"cat": "Cat",
"cat_name": "Cat: {name}",
"set-up-password": {
"heading": "Hello, {username}",
"title": "Forgot password",
"followLink": "Please follow the link to set up your password"
},
"day_interval": {
"one": "Every day",
"other": "Every {count} days",
"zero": "Never"
},
"nested": "We go shopping: $t(test.day_interval, {{\"count\": {count} }})"
}
caution

The i18n folder isn't automatically copied to your dist folder during the build process. To enable nestjs to do this modify the compilerOptions inside nest-cli.json.

nest-cli.json
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{ "include": "i18n/**/*", "watchAssets": true }
]
}
}
caution

When using a monorepo structure don't forget to set the outDir

nest-cli.json
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{
"include": "i18n/**/*",
"watchAssets": true,
+ "outDir": "dist/apps/api"
}
]
}
}

Module setup

src/app.module.ts
import { Module } from '@nestjs/common';
import * as path from 'path';
import { I18nModule } from 'nestjs-i18n';

@Module({
imports: [
I18nModule.forRoot({
fallbackLanguage: 'en',
loaderOptions: {
path: path.join(__dirname, '/i18n/'),
watch: true,
},
}),
],
controllers: [],
})
export class AppModule {}

The async way nestjs-i18n is to use I18nModule.forRootAsync

src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as path from 'path';
import { I18nModule, AcceptLanguageResolver, QueryResolver, HeaderResolver } from 'nestjs-i18n';

@Module({
imports: [
I18nModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
fallbackLanguage: configService.getOrThrow('FALLBACK_LANGUAGE'),
loaderOptions: {
path: join(__dirname, '/i18n/'),
watch: true,
},
}),
resolvers: [
{ use: QueryResolver, options: ['lang'] },
AcceptLanguageResolver,
new HeaderResolver(['x-lang']),
],
inject: [ConfigService],
}),
],
controllers: [],
})
export class AppModule {}

I18nOptions


export interface I18nOptions {
// The default language to use as a fallback if a translation is not available in the requested language.
fallbackLanguage: string;

// An optional dictionary of fallback languages for specific keys or phrases.
fallbacks?: { [key: string]: string };

// An array of resolvers used to resolve the requested translation.
resolvers?: I18nOptionResolver[];

// The loader type to use for loading translation data.
loader?: Type<I18nLoader>;

// Configuration options for the loader.
loaderOptions: any;

// A formatter for formatting translations (e.g., for date or number formatting).
formatter?: Formatter;

// Whether or not to enable logging for i18n operations.
logging?: boolean;

// The view engine to use for rendering templates (if applicable).
viewEngine?: 'hbs' | 'pug' | 'ejs';

// Whether to disable any middleware related to i18n.
disableMiddleware?: boolean;

// Whether to skip asynchronous hooks related to i18n.
skipAsyncHook?: boolean;

// Configuration options for the i18n validator.
validatorOptions?: I18nValidatorOptions;

// Whether to throw an error when a translation key is missing.
throwOnMissingKey?: boolean;

// The output path for generated types (if any).
typesOutputPath?: string;
}

caution

The I18nModule is a global module. This means you'll only need to register the module once (in the root module). After that it's accessible throughout the whole application.

Live reloading 🎉

Setting the watch option to true in the loaderOptions enables live reloading 🎉.

Type safety 🎉

nestjs-i18n now comes with type safety as well! Click here to see how 🎉.

Add resolvers

Resolvers are used for getting the current language of our request. In basic web applications this is done via the Accept-Language header. But in many cases you want to override this language by your logged in user settings, or some header you define yourself.

nestjs-i18n comes with a set of built-in resolvers.

NameDefault value
QueryResolver['lang']
HeaderResolver[]
AcceptLanguageResolver{matchType: 'strict-loose'
CookieResolverlang
GraphQLWebsocketResolverN/A
GrpcMetadataResolver['lang']

To add resolvers add them to the resolvers array in your I18nModule options. The way nestjs-i18n works it's going to resolve the language in order. So in this case it tries the QueryResolver first, if it can't resolve a language it'll jump to the next one.

src/app.module.ts
import { Module } from '@nestjs/common';
import * as path from 'path';
import {
AcceptLanguageResolver,
I18nJsonLoader,
I18nModule,
QueryResolver,
} from 'nestjs-i18n';

@Module({
imports: [
I18nModule.forRoot({
fallbackLanguage: 'en',
loaderOptions: {
path: path.join(__dirname, '/i18n/'),
watch: true,
},
resolvers: [
{ use: QueryResolver, options: ['lang'] },
AcceptLanguageResolver,
],
}),
],
controllers: [],
})
export class AppModule {}

or in forRootAsync

src/app.module.ts
import { Module } from '@nestjs/common';
import * as path from 'path';
import {
AcceptLanguageResolver,
QueryResolver,
HeaderResolver,
CookieResolver,
I18nJsonLoader,
I18nModule,
} from 'nestjs-i18n';

@Module({
imports: [
I18nModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
fallbackLanguage: "en",
loaderOptions: {
path: join(__dirname, "/i18n/"),
watch: true,
},
}),
resolvers: [
new QueryResolver(["lang", "l"]),
new HeaderResolver(["x-custom-lang"]),
new CookieResolver(),
AcceptLanguageResolver,
],
inject: [ConfigService],
}),
],
controllers: [],
})
export class AppModule {}
tip

It's possible to create your own resolvers! For example if you want to resolve the language from your logged-in user's settings. Please see the resolvers page for instructions.

Translate stuff 🎉

Now that we've setup everything we can start to do translations! The easiest way to do this is in your controller.

src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { I18n, I18nContext } from 'nestjs-i18n';

@Controller()
export class AppController {
@Get()
async getHello(@I18n() i18n: I18nContext) {
return await i18n.t('test.HELLO');
}
}

You can also do translation on your service as:

src/app.service.ts
import { Injectable } from '@nestjs/common';
import { I18nContext, I18nService } from 'nestjs-i18n';


@Injectable()
export class AppService {
constructor(private readonly i18n: I18nService) {}
getHello(): string {
return this.i18n.t('test.HELLO',{ lang: I18nContext.current().lang });
}
}

Translate options


export type TranslateOptions = {

/**
* Language to translate to
*/

lang?: string;

/**
* Arguments to pass to the translation
*/

args?: ({ [k: string]: any } | string)[] | { [k: string]: any };

/**
* Default value to return when no translation is found
*/

defaultValue?: string;

/**
* Debug mode
*/

debug?: boolean;
};

Example

A working example is available here.