Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
nestjs-i18n
Advanced tools
[![Build Status](https://travis-ci.org/ToonvanStrijp/nestjs-i18n.svg?branch=master)](https://travis-ci.org/ToonvanStrijp/nestjs-i18n) [![Coverage Status](https://coveralls.io/repos/github/ToonvanStrijp/nestjs-i18n/badge.svg?branch=master)](https://covera
nestjs-i18n is a powerful internationalization (i18n) module for NestJS applications. It provides a comprehensive set of tools to manage translations, handle localization, and support multiple languages in a NestJS application.
Basic Setup
This code sets up the basic configuration for nestjs-i18n. It initializes the I18nModule with a fallback language and specifies the parser and the path to the translation files.
```typescript
import { Module } from '@nestjs/common';
import { I18nModule, I18nJsonParser } from 'nestjs-i18n';
import * as path from 'path';
@Module({
imports: [
I18nModule.forRoot({
fallbackLanguage: 'en',
parser: I18nJsonParser,
parserOptions: {
path: path.join(__dirname, '/i18n/'),
},
}),
],
})
export class AppModule {}
```
Translation Service
This code demonstrates how to use the I18nService to translate a message. The `translate` method is used to fetch the translation for the given key ('HELLO_MESSAGE').
```typescript
import { Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';
@Injectable()
export class AppService {
constructor(private readonly i18n: I18nService) {}
async getHello(): Promise<string> {
return this.i18n.translate('HELLO_MESSAGE');
}
}
```
Request Scoped Translations
This code shows how to use request-scoped translations. By injecting the I18nContext into the controller method, you can translate messages based on the current request's language.
```typescript
import { Controller, Get } from '@nestjs/common';
import { I18n, I18nContext } from 'nestjs-i18n';
@Controller()
export class AppController {
@Get()
async getHello(@I18n() i18n: I18nContext): Promise<string> {
return i18n.translate('HELLO_MESSAGE');
}
}
```
i18next is a popular internationalization framework for JavaScript applications. It provides a wide range of features for managing translations and supports various backends for loading translation files. Compared to nestjs-i18n, i18next is more general-purpose and can be used with any JavaScript framework, not just NestJS.
nestjs-i18n is another internationalization module for NestJS. It offers similar functionalities to nestjs-i18n, such as managing translations and supporting multiple languages. However, it may have different configuration options and features.
react-i18next is a powerful internationalization library specifically designed for React applications. It builds on top of i18next and provides React-specific bindings. While it offers similar translation management features, it is tailored for use with React rather than NestJS.
The i18n module for Nest.
$ npm i --save nestjs-i18n
To keep your setup working use the correct version of nestjs-i18n
.
nestjs-i18n version | nestjs version |
---|---|
v7.0.0 or greather | v7.0.0 |
v6.0.0 or below | v6.0.0 |
Build in we have a JSON parser (I18nJsonParser
) this parser handles to following structure
create a directory and in it define your language keys as directories.
i18n
├── en
│ ├── category.json
│ └── auth.json
└── nl
├── category.json
└── auth.json
The format of a translation file could look like this:
{
"HELLO_MESSAGE": "Hello {username}",
"GOODBYE_MESSAGE": "Goodbye {username}",
"USER_ADDED_PRODUCT": "{0.username} added {1.productName} to cart",
"SETUP": {
"WELCOME": "Welcome {0.username}",
"GOODBYE": "Goodbye {0.username}"
},
"ARRAY": ["ONE", "TWO", "THREE"]
}
To get a specific translation the JSON gets flattened by: flat. All the translations are prefixed with the file name (to prevent collisions). Let's say the filename of the translation file is: user.json
. To use the HELLO_MESSAGE
translation you would use the following key: user.HELLO_MESSAGE
.
String formatting is done by: string-format
If you've created your project using the @nestjs/cli
you can edit the nest-cli.json
to automatically copy your i18n
folder to your output (dist
) folder.
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": ["i18n/**/*"]
}
}
To use the translation service we first add the module. The I18nModule
has a @Global()
attribute so you should only import it once.
import { Module } from '@nestjs/common';
import * as path from 'path';
import { I18nModule, I18nJsonParser } from 'nestjs-i18n';
@Module({
imports: [
I18nModule.forRoot({
fallbackLanguage: 'en',
parser: I18nJsonParser,
parserOptions: {
path: path.join(__dirname, '/i18n/'),
},
}),
],
controllers: [],
})
export class AppModule {}
import { Module } from '@nestjs/common';
import * as path from 'path';
import { I18nModule, I18nJsonParser } from 'nestjs-i18n';
@Module({
imports: [
I18nModule.forRootAsync({
useFactory: (configService: ConfigurationService) => ({
fallbackLanguage: configService.fallbackLanguage, // e.g., 'en'
parserOptions: {
path: path.join(__dirname, '/i18n/'),
},
}),
parser: I18nJsonParser,
inject: [ConfigurationService],
}),
],
controllers: [],
})
export class AppModule {}
To use live reloading use the watch
option in the I18nJsonParser
. The I18nJsonParser
watches the i18n
folder for changes and when needed updates the translations
or languages
.
I18nModule.forRoot({
fallbackLanguage: 'en',
parser: I18nJsonParser,
parserOptions: {
path: path.join(__dirname, '/i18n/'),
// add this to enable live translations
watch: true,
},
});
To refresh your translations and languages manually:
await this.i18nService.refresh();
A default JSON parser (I18nJsonParser
) is included.
To implement your own I18nParser
take a look at this example i18n.json.parser.ts.
To provide live translations you can return an observable within the extended I18nParser
class. For and implementation example you can take a look at the i18n.json.parser.ts.
export class I18nMysqlParser extends I18nParser {
constructor(
@Inject(I18N_PARSER_OPTIONS)
private options: I18nJsonParserOptions,
) {
super();
}
async languages(): Promise<string[] | Observable<string[]>> {
// for example do a database call here
return observableOf(['nl', 'en']);
}
async parse(): Promise<I18nTranslation | Observable<I18nTranslation>> {
// for example do a database call here
return observableOf({
nl: {
HELLO: 'Hallo',
},
en: {
HELLO: 'Hello',
},
});
}
}
To make it easier to manage in what language to respond you can make use of resolvers
(note: When using
forRootAsync
you don't return theresolvers
with the rest of the config. You'll need to provide theresolvers
like this: example)
@Module({
imports: [
I18nModule.forRoot({
fallbackLanguage: 'en',
parser: I18nJsonParser,
parserOptions: {
path: path.join(__dirname, '/i18n/'),
},
resolvers: [
{ use: QueryResolver, options: ['lang', 'locale', 'l'] },
new HeaderResolver(['x-custom-lang']),
AcceptLanguageResolver,
new CookieResolver(['lang', 'locale', 'l']),
],
}),
],
controllers: [HelloController],
})
export class AppModule {}
Currently, there are four built-in resolvers
Resolver | Default value |
---|---|
QueryResolver | none |
HeaderResolver | none |
AcceptLanguageResolver | N/A |
CookieResolver | lang |
To implement your own resolver (or custom logic) use the I18nResolver
interface. The resolvers are provided via the nestjs dependency injection, this way you can inject your own services if needed.
@Injectable()
export class QueryResolver implements I18nResolver {
constructor(@I18nResolverOptions() private keys: string[]) {}
resolve(context: ExecutionContext) {
let req: any;
switch (context.getType() as string) {
case 'http':
req = context.switchToHttp().getRequest();
break;
case 'graphql':
[, , { req }] = context.getArgs();
break;
}
let lang: string;
if (req) {
for (const key of this.keys) {
if (req.query != undefined && req.query[key] !== undefined) {
lang = req.query[key];
break;
}
}
}
return lang;
}
}
To provide initial options to your custom resolver use the @I18nResolverOptions()
decorator, also provide the resolver as followed:
I18nModule.forRoot({
fallbackLanguage: 'en',
parser: I18nJsonParser,
parserOptions: {
path: path.join(__dirname, '/i18n/'),
},
resolvers: [{ use: QueryResolver, options: ['lang', 'locale', 'l'] }],
});
I18nModule.forRootAsync({
useFactory: () => {
return {
fallbackLanguage: 'en',
parserOptions: {
path: path.join(__dirname, '/i18n'),
},
};
},
parser: I18nJsonParser,
resolvers: [{ use: QueryResolver, options: ['lang', 'locale', 'l'] }],
});
I18nLang
decorator and I18nService
@Controller()
export class SampleController {
constructor(private readonly i18n: I18nService) {}
@Get()
async sample(@I18nLang() lang: string) {
await this.i18n.translate('HELLO_MESSAGE', {
lang: lang,
args: { id: 1, username: 'Toon' },
});
await this.i18n.translate('SETUP.WELCOME', {
lang: 'en',
args: { id: 1, username: 'Toon' },
});
await this.i18n.translate('ARRAY.0', { lang: 'en' });
}
}
I18n
decorator@Controller()
export class SampleController {
@Get()
async sample(@I18n() i18n: I18nContext) {
await i18n.translate('HELLO_MESSAGE', {
args: { id: 1, username: 'Toon' },
});
await i18n.translate('SETUP.WELCOME', {
args: { id: 1, username: 'Toon' },
});
await i18n.translate('ARRAY.0');
}
}
No need to handle lang
manually.
I18nRequestScopeService
within a custom service using request scoped translation service@Injectable()
export class SampleService {
constructor(private readonly i18n: I18nRequestScopeService) {}
async doFancyStuff() {
await this.i18n.translate('HELLO_MESSAGE', {
args: { id: 1, username: 'Toon' },
});
await this.i18n.translate('SETUP.WELCOME', {
args: { id: 1, username: 'Toon' },
});
await this.i18n.translate('ARRAY.0');
}
}
To be used within other services like sending E-mails.
The advantage is that you don't have to worry about transporting lang
from the Request
to your service.
Use with caution! The I18nRequestScopeService
uses the REQUEST
scope and is no singleton.
This will be inherited to all consumers of I18nRequestScopeService
!
Read Nest Docs for more information.
Dont use I18nRequestScopeService
within controllers. The I18n
decorator is a much better solution.
To easily check if your i18n folder is correctly structured you can use the following command:
nest-i18n check <i18n-path>
example: nest-i18n check src/i18n
This is very useful inside a CI environment to prevent releases with missing translations.
from V8.0.0 on we changed the internal 18n-middleware
for an interceptor
this way we can provide the ExecutionContext
so that nestjs-i18n
works on diffrent protocols was well for example (grpc or websockets). This contains one breaking change. It only applies to your code if you've made a custom resolver
. To resolve this breaking change take look at this example. Instead of providing the req
in the resolve
method, change this to take the ExecutionContext
as argument.
from V6.0.0 on we implemented the I18nParser
, by using this we can easily support different formats other than JSON. To migrate to this change look at the Quick start above. There are some changes in the declaration of the I18nModule
. Note: the translate function returns a Promise. So you need to call it using await i18n.translate('HELLO');
from V4.0.0 on we changed the signature of the translate
method, the language is now optional, if no language is given it'll fallback to the fallbackLanguage
from V3.0.0 on we load translations based on their directory name instead of file name. Change your translations files to the structure above: info
FAQs
The i18n module for Nest.
The npm package nestjs-i18n receives a total of 94,030 weekly downloads. As such, nestjs-i18n popularity was classified as popular.
We found that nestjs-i18n demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.