Skip to content

i18n API

Import path: @sdcorejs/nestjs/i18n

The i18n package localizes library apiError envelopes. It ships English and Vietnamese catalogs, an Accept-Language resolver, a simple template resolver and an optional global exception filter.

Exports

ExportKindPurpose
MessageCatalog, Catalogstypescode -> template and language -> catalog maps
II18nResolverinterfacetranslate(code, rawLang, data?) contract
ILanguageResolverinterfaceresolve(raw): locale contract
I18N_RESOLVER, LANGUAGE_RESOLVERvaluesDI tokens
LanguageResolverOptionsinterfaceSupported languages and fallback
DefaultLanguageResolverclassWeighted Accept-Language parser
SimpleI18nOptionsinterfaceCatalogs, fallback and optional language resolver
SimpleI18nResolverclassCatalog lookup and {var} interpolation
CORE_CATALOG_EN, CORE_CATALOG_VI, CORE_CATALOGSvaluesBuilt-in library messages
SdI18nExceptionFilterclassLocalizes HttpException apiError bodies
I18nModule, I18nModuleOptionsclass/typeGlobal providers and optional APP_FILTER

Module configuration

ts
I18nModule.forRoot({
  supportedLanguages: ['en', 'vi'],
  fallbackLanguage: 'en',
  catalogs: {
    en: { 'catalog.product.not-found': 'Product {id} was not found' },
    vi: { 'catalog.product.not-found': 'Không tìm thấy sản phẩm {id}' },
  },
});
OptionDefaultBehavior
catalogs{}Shallow-merged per language over built-in core.* messages; consumer wins
supportedLanguages['en', 'vi']Base locale codes accepted by default resolver
fallbackLanguage'en'Fallback locale and catalog
resolverSimpleI18nResolverCustom DI class implementing II18nResolver
useGlobalFiltertrueRegisters SdI18nExceptionFilter as APP_FILTER

SdCoreModule wires i18n only when its i18n option is present.

Language resolution

ts
const resolver = new DefaultLanguageResolver({
  supported: ['en', 'vi'],
  fallback: 'en',
});

resolver.resolve('fr;q=0.4, vi-VN;q=0.9'); // 'vi'

The resolver splits comma-separated tags, reads q weights (default 1), sorts descending, lowercases and strips the region (vi-VN becomes vi). Unknown or invalid tags fall back.

Translation contract

ts
interface II18nResolver {
  translate(
    code: string,
    lang: string | undefined,
    data?: Record<string, unknown>,
  ): string;
}

SimpleI18nResolver looks up target language, then fallback language, then returns the stable code. It replaces {word} placeholders from data and leaves unmatched placeholders unchanged. For ICU, pluralization or remote catalogs, register a custom resolver class.

Exception filter behavior

ts
throw new NotFoundException(
  apiError('catalog.product.not-found', 'Product not found', { id }),
);

With the filter enabled, the HTTP response becomes:

json
{
  "error": {
    "code": "catalog.product.not-found",
    "message": "Không tìm thấy sản phẩm 123",
    "data": { "id": "123" }
  }
}

The raw ContextService.lang header is passed to the resolver. Validation issues are localized individually using their message as a code and params as interpolation data. Non-apiErrorHttpException responses pass through unchanged. With no resolver, code and default message pass through unchanged.

Set useGlobalFilter: false only when you register SdI18nExceptionFilter yourself or have an application filter that preserves the same error contract.

Security notes

  • Treat catalog text as presentation, not authorization logic. Clients should branch on stable code, never translated message.
  • Keep interpolation data bounded and non-sensitive; the filter retains it in the response.
  • ContextService.lang is untrusted input. Custom resolvers must normalize it before filesystem, network or dynamic-module lookup.

Released under the MIT License.