Skip to content

Internationalized errors

Library exceptions use a stable error code plus an English fallback. Enabling i18n adds language resolution, catalogs, interpolation, and a global exception filter.

Configure catalogs

ts
import { SdCoreModule } from '@sdcorejs/nestjs';

SdCoreModule.forRoot({
  i18n: {
    supportedLanguages: ['en', 'vi'],
    fallbackLanguage: 'en',
    catalogs: {
      en: {
        'app.product.name.required': 'Product name is required',
        'app.product.name.min': 'Name must contain at least {minimum} characters',
      },
      vi: {
        'app.product.name.required': 'Tên sản phẩm là bắt buộc',
        'app.product.name.min': 'Tên phải có ít nhất {minimum} ký tự',
      },
    },
  },
});

Application catalogs merge over built-in English/Vietnamese core.* catalogs. Unknown codes fall back to the fallback-language catalog and then to the code itself. The default resolver parses Accept-Language quality weights and converts regional tags such as vi-VN to vi.

Error envelopes

ts
import { BadRequestException } from '@nestjs/common';
import { apiError } from '@sdcorejs/nestjs/core';

throw new BadRequestException(
  apiError('app.product.name.min', 'Product name is too short', { minimum: 3 }),
);

With the default global filter, the response body is:

json
{
  "error": {
    "code": "app.product.name.min",
    "message": "Name must contain at least 3 characters",
    "data": { "minimum": 3 }
  }
}

Non-apiError exceptions pass through unchanged. ApiResponse.noContent() returns { data: null }; it does not set HTTP status 204 by itself.

Validation issues

When data.issues contains Zod issue details, the filter translates each string issues[].message with its params. Put i18n codes in schema messages:

ts
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(3, 'app.product.name.min'),
});

Custom resolver

ts
import { Injectable } from '@nestjs/common';
import type { II18nResolver } from '@sdcorejs/nestjs/i18n';

@Injectable()
class AppI18nResolver implements II18nResolver {
  translate(
    code: string,
    lang: string | undefined,
    data?: Record<string, unknown>,
  ): string {
    const suffix = data ? ' ' + JSON.stringify(data) : '';
    return (lang ?? 'en') + ':' + code + suffix;
  }
}

Register with i18n: { resolver: AppI18nResolver }. Set useGlobalFilter: false only when the application installs SdI18nExceptionFilter itself or has an equivalent envelope-preserving filter.

Released under the MIT License.