DEV Community

fadow
fadow

Posted on

How to Set Up i18n in NestJS — with Vietnamese Translations

Most i18n tutorials stop at English and Spanish. If you're building for Vietnamese users, you need proper tone marks, natural phrasing, and a setup that doesn't fight you.

Here's how I built a NestJS i18n system that auto-detects Accept-Language and returns error messages in Vietnamese or English.

1. Install nestjs-i18n

npm install nestjs-i18n
Enter fullscreen mode Exit fullscreen mode

2. Create translation files

src/i18n/
├── vi/
│   ├── validation.json
│   └── response.json
└── en/
    ├── validation.json
    └── response.json
Enter fullscreen mode Exit fullscreen mode

vi/validation.json:

{
  "EMAIL_INVALID": "Email không hợp lệ",
  "PASSWORD_MIN_LENGTH": "Mật khẩu phải có ít nhất 6 ký tự",
  "USER_NOT_FOUND": "Không tìm thấy người dùng",
  "EMAIL_EXISTS": "Email đã tồn tại",
  "FORBIDDEN": "Bạn không có quyền truy cập"
}
Enter fullscreen mode Exit fullscreen mode

vi/response.json:

{
  "LOGIN_SUCCESS": "Đăng nhập thành công",
  "REGISTER_SUCCESS": "Đăng ký thành công",
  "LOGOUT_SUCCESS": "Đăng xuất thành công"
}
Enter fullscreen mode Exit fullscreen mode

3. Register I18nModule

// app.module.ts
import { I18nModule, HeaderResolver } from 'nestjs-i18n';
import * as path from 'path';

@Module({
  imports: [
    I18nModule.forRoot({
      fallbackLanguage: 'vi',
      loaderOptions: {
        path: path.join(__dirname, '/i18n/'),
        watch: true,
      },
      resolvers: [new HeaderResolver(['accept-language'])],
    }),
    // ... other imports
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

4. Use in exception filters

// http-exception.filter.ts
import { I18nContext } from 'nestjs-i18n';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const i18n = I18nContext.current();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    const message = exception.message;

    // Translate if the message is a dot-notation key like "validation.EMAIL_INVALID"
    const translated = i18n?.translate(message) ?? message;

    response.status(status).json({
      statusCode: status,
      message: translated,
      timestamp: new Date().toISOString(),
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Auto-detect language from headers

The Accept-Language header resolver does this automatically. The frontend sends Accept-Language: vi or en — NestJS picks it up. Zero extra code.

Vietnamese-specific gotchas

  • Tone marks: nest-i18n stores JSON as UTF-8. Vietnamese characters (ắ, ễ, ở) work natively. No special handling needed.
  • Polite phrasing: Vietnamese error messages should sound respectful. "Vui lòng nhập email" (Please enter email) not "Nhập email" (Enter email).
  • Placeholders: Use {min} and {max} — they work with nestjs-i18n's interpolation:
  "NAME_LENGTH": "Tên phải có từ {min} đến {max} ký tự"
Enter fullscreen mode Exit fullscreen mode

That's it. Your NestJS API now speaks Vietnamese natively.


If you don't want to set this up from scratch, I built a NestJS Vietnam Starter Kit with i18n, JWT auth, RBAC, Docker, and 36 tests — ready to go.

Top comments (0)