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
{
"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} }})"
}
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
.
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{ "include": "i18n/**/*", "watchAssets": true }
]
}
}
When using a monorepo structure don't forget to set the outDir
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{
"include": "i18n/**/*",
"watchAssets": true,
+ "outDir": "dist/apps/api"
}
]
}
}
Module setup
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
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;
}
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.
Setting the watch
option to true
in the loaderOptions
enables live reloading 🎉.
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.
Name | Default value |
---|---|
QueryResolver | ['lang'] |
HeaderResolver | [] |
AcceptLanguageResolver | {matchType: 'strict-loose' |
CookieResolver | lang |
GraphQLWebsocketResolver | N/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.
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
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 {}
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
.
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:
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.