Skip to main content

sonamu.config.ts Configuration

Enable the i18n option in sonamu.config.ts:
import { defineConfig } from "sonamu";

export default defineConfig({
  // ... other settings

  i18n: {
    defaultLocale: "ko", // Default language
    supportedLocales: ["ko", "en"], // List of supported languages
  },
});
OptionTypeDescription
defaultLocalestringDefault language code (fallback)
supportedLocalesstring[]All supported language codes

Directory Structure

When i18n is enabled, the api/src/i18n/ directory is required:

Creating Dictionary Files

defaultLocale Dictionary (ko.ts)

import { createFormat, josa } from "sonamu/dict";

const format = createFormat("ko");

export default {
  // Common UI
  "common.save": "μ €μž₯",
  "common.cancel": "μ·¨μ†Œ",
  "common.delete": "μ‚­μ œ",
  "common.results": (count: number) => `${count}개 결과`,

  // Error messages
  "error.notFound": "찾을 수 μ—†μŠ΅λ‹ˆλ‹€",
  "error.unauthorized": "인증이 ν•„μš”ν•©λ‹ˆλ‹€",

  // Validation messages (function type)
  "validation.required": (field: string) => `${josa(field, "μ€λŠ”")} ν•„μˆ˜μž…λ‹ˆλ‹€`,
  "validation.email": "μ˜¬λ°”λ₯Έ 이메일 ν˜•μ‹μ΄ μ•„λ‹™λ‹ˆλ‹€",

  // Simple keys are also allowed (but namespace pattern is recommended)
  notFound: (name: string, id: number) => `μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ${name} ID ${id}`,
  test: (date: Date) => format.date(date),
} as const;
Recommended key naming pattern: Using namespace patterns like "domain.item" helps organize keys systematically and makes IDE autocomplete more convenient. - "common.*" - Common UI - "error.*" - Error messages - "validation.*" - Validation messages - "entity.*" - Entity related

Other Locale Dictionary (en.ts)

import { plural } from "sonamu/dict";
import { defineLocale } from "./sd.generated";

export default defineLocale({
  // Common UI
  "common.save": "Save",
  "common.cancel": "Cancel",
  "common.delete": "Delete",
  "common.results": (count: number) =>
    plural(count, { one: `${count} result`, other: `${count} results` }),

  // Error messages
  "error.notFound": "Not found",
  "error.unauthorized": "Authentication required",

  // Validation messages
  "validation.required": (field: string) => `${field} is required`,
  "validation.email": "Invalid email format",

  // Simple keys (namespace pattern recommended)
  notFound: (name: string, id: number) => `${name} ID ${id} not found`,
});
defineLocale is exported from sd.generated.ts and provides type checking based on the keys from defaultLocale.

Locale Configuration

Frontend (Client)

sd.generated.ts includes setLocale and getCurrentLocale functions for client-side locale management:
import { setLocale, getCurrentLocale, SUPPORTED_LOCALES } from "@/i18n/sd.generated";

// Change locale
setLocale("en");

// Check current locale
const current = getCurrentLocale(); // "en"
The axios interceptor in sonamu.shared.ts automatically adds the Accept-Language header to all API requests:
// sonamu.shared.ts (auto-generated β€” no manual setup needed)
axios.interceptors.request.use((config) => {
  config.headers["Accept-Language"] = getCurrentLocale();
  return config;
});
After calling setLocale("en"), all subsequent API requests will include the Accept-Language: en header.

Backend (Server)

To set the locale per request, configure the Context in middleware:
// api/src/middlewares/locale.ts
import { Sonamu } from "sonamu";

export async function localeMiddleware(request: FastifyRequest) {
  // Extract locale from Accept-Language header or query parameter
  const locale =
    request.headers["accept-language"]?.split(",")[0]?.split("-")[0] ||
    request.query.locale ||
    "ko";

  // Set locale in Context
  const ctx = Sonamu.getContext();
  ctx.locale = locale;
}
When the client calls setLocale(), the axios interceptor automatically sends the Accept-Language header, and the server’s locale middleware reads it so that SD() calls return translations in the correct language. Client-server locale synchronization works without any additional configuration.

Verifying Initialization

After completing the setup, run pnpm sync to verify that sd.generated.ts is generated:
cd api
pnpm sync
The generated sd.generated.ts includes:
  • Labels extracted from Entity (entityLabels)
  • SD() function
  • localizedColumn() function
  • defineLocale() function

Using the SD Function

Writing and using dictionaries

Entity Labels

Auto-extracted labels