@iamalond/nestjs-i18next
v1.3.2
Published
π i18next module for NestJS
Maintainers
Readme
About the Project
nestjs-i18next is an internationalization module for NestJS applications.
The project was inspired by the popular nestjs-i18n module, but with a major enhancement: full type-safety for translation arguments. No more any in your translations β your keys and variables are strictly typed based on your JSON files.
Key Features:
- π‘οΈ Strict Typing: Automatic type generation for paths and arguments.
- π§© ICU Format: Support for variables and complex pluralization (similar to the
i18next-icuplugin). - π Live Reload: Watch for translation file changes without restarting the server.
- π Recursive Namespaces: Support for nested subfolders to organize your translations cleanly.
Installation
Node.js 18.0.0 or newer is required.
$ npm install @iamalond/nestjs-i18next
$ yarn add @iamalond/nestjs-i18next
$ pnpm add @iamalond/nestjs-i18nextUsage
After installation, we can import the I18nextModule into the root AppModule:
import { I18nextModule } from '@iamalond/nestjs-i18next';
@Module({
imports: [
I18nextModule.forRoot({
fallbackLanguage: 'en',
throwOnMissingKey: true,
logging: true,
generatedTypesPath: path.join(process.cwd(), 'src/i18n/index.d.ts'),
loadingOptions: {
path: path.join(process.cwd(), 'src/i18n'),
subfolders: true, // recursive loading
watch: true
}
})
]
})
export class AppModule {}Then use I18nextService in your service's constructor:
import { Injectable } from '@nestjs/common';
import { I18nextService } from '@iamalond/nestjs-i18next';
/**
* Import the types from the path you specified in 'generatedTypesPath'
* (e.g., src/i18n/index.d.ts)
*/
import { I18nTranslations } from './i18n';
@Injectable()
export class AppService {
// Pass I18nTranslations to the service for strict typing
constructor(private readonly i18n: I18nextService<I18nTranslations>) {}
getHello(): string {
// 1. Simple translation (autocomplete for keys, when start typing!)
const simple = this.i18n.t('common.hello');
// 2. Translation with arguments (Typescript will REQUIRE 'args' if variables exist)
const withArgs = this.i18n.t('common.welcome', {
args: { name: 'Matvey iamAlond' },
lang: 'en'
});
const apples = this.i18n.t('common.apples', {
args: { count: 5 }
});
// 3. You can also use the 'translate' alias
return this.i18n.translate('core.errors.notFound', {
defaultValue: 'Not Found',
debug: true
});
}
}Dynamic Translation Keys
If you need to use a dynamic key (e.g., based on an HTTP status code), use the I18nPath type. This type is also automatically generated in your generatedTypesPath and contains all valid translation paths.
import { I18nPath, I18nTranslations } from './i18n';
@Injectable()
export class ErrorsService {
constructor(private readonly i18n: I18nextService<I18nTranslations>) {}
handleError(statusCode: number) {
/**
* Use 'as I18nPath' to tell TypeScript that this string
* is a valid translation key.
*/
return this.i18n.t(`errors.status.${statusCode}` as I18nPath, {
defaultValue: 'An unexpected error occurred'
});
}
}File Structure
By default, the module uses the locale/ns.json structure. With subfolders: true, you can use recursive nesting. Keys will be resolved as namespace.subfolder.key. You can also combine both of these approaches.
src/i18n/
βββ en/
β βββ common.json
β βββ core/
β βββ errors.json <-- key will be "core.errors.invalid_password"
βββ ru/
βββ common.json
βββ ...Examples
To support the examples above, your translation directory should look like this:
src/i18n/en/common.json
{
"hello": "Hello!",
"welcome": "Welcome, {{name}}!",
"apples": "{count, plural, =0 {No apples} one {# apple} =3 {three apples} other {# apples}}"
}src/i18n/en/core/errors.json
{
"invalid_password": "Invalid password",
"notFound": "Resource not found",
"status": {
"400": "Bad request",
"401": "Unauthorized",
"403": "Forbidden",
"404": "Not found",
"500": "Internal server error"
}
}Key Resolution Rules
- Simple file: common.json -> key starts with common (e.g., common.hello).
- Subfolders: core/errors.json -> key starts with core.errors (e.g., core.errors.status.404).
- Arguments: Any string containing {{var}} or ICU structures like {count, plural, ...} will automatically require args in your TypeScript code.
