sonamu.config.ts ์ค์
sonamu.config.ts์์ i18n ์ต์
์ ํ์ฑํํฉ๋๋ค:
import { defineConfig } from "sonamu";
export default defineConfig({
// ... ๊ธฐํ ์ค์
i18n: {
defaultLocale: "ko", // ๊ธฐ๋ณธ ์ธ์ด
supportedLocales: ["ko", "en"], // ์ง์ ์ธ์ด ๋ชฉ๋ก
},
});
| ์ต์
| ํ์
| ์ค๋ช
|
|---|
defaultLocale | string | ๊ธฐ๋ณธ ์ธ์ด ์ฝ๋ (fallback) |
supportedLocales | string[] | ์ง์ํ๋ ๋ชจ๋ ์ธ์ด ์ฝ๋ |
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
i18n์ ํ์ฑํํ๋ฉด api/src/i18n/ ๋๋ ํ ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค:
๐api/
๐src/
๐i18n/
๐TSko.ts - ํ๊ตญ์ด ๋์
๋๋ฆฌ (defaultLocale)
๐TSen.ts - ์์ด ๋์
๋๋ฆฌ
๐TSsd.generated.ts - ์๋ ์์ฑ ํ์ผ
๋์
๋๋ฆฌ ํ์ผ ์์ฑ
defaultLocale ๋์
๋๋ฆฌ (ko.ts)
import { createFormat, josa } from "sonamu/dict";
const format = createFormat("ko");
export default {
// ๊ณตํต UI
"common.save": "์ ์ฅ",
"common.cancel": "์ทจ์",
"common.delete": "์ญ์ ",
"common.results": (count: number) => `${count}๊ฐ ๊ฒฐ๊ณผ`,
// ์๋ฌ ๋ฉ์์ง
"error.notFound": "์ฐพ์ ์ ์์ต๋๋ค",
"error.unauthorized": "์ธ์ฆ์ด ํ์ํฉ๋๋ค",
// ๊ฒ์ฆ ๋ฉ์์ง (ํจ์ํ)
"validation.required": (field: string) => `${josa(field, "์๋")} ํ์์
๋๋ค`,
"validation.email": "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค",
// ๋จ์ ํค๋ ์ฌ์ฉ ๊ฐ๋ฅ (๋จ, ๋ค์์คํ์ด์ค ํจํด ๊ถ์ฅ)
notFound: (name: string, id: number) => `์กด์ฌํ์ง ์๋ ${name} ID ${id}`,
test: (date: Date) => format.date(date),
} as const;
ํค ๋ค์ด๋ฐ ๊ถ์ฅ ํจํด: "๋๋ฉ์ธ.ํญ๋ชฉ" ํ์์ ๋ค์์คํ์ด์ค ํจํด์ ์ฌ์ฉํ๋ฉด ํค๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๊ณ IDE ์๋์์ฑ๋ ํธ๋ฆฌํฉ๋๋ค.
"common.*" - ๊ณตํต UI
"error.*" - ์๋ฌ ๋ฉ์์ง
"validation.*" - ๊ฒ์ฆ ๋ฉ์์ง
"entity.*" - Entity ๊ด๋ จ
๋ค๋ฅธ locale ๋์
๋๋ฆฌ (en.ts)
import { plural } from "sonamu/dict";
import { defineLocale } from "./sd.generated";
export default defineLocale({
// ๊ณตํต UI
"common.save": "Save",
"common.cancel": "Cancel",
"common.delete": "Delete",
"common.results": (count: number) =>
plural(count, { one: `${count} result`, other: `${count} results` }),
// ์๋ฌ ๋ฉ์์ง
"error.notFound": "Not found",
"error.unauthorized": "Authentication required",
// ๊ฒ์ฆ ๋ฉ์์ง
"validation.required": (field: string) => `${field} is required`,
"validation.email": "Invalid email format",
// ๋จ์ ํค (๋ค์์คํ์ด์ค ํจํด ๊ถ์ฅ)
notFound: (name: string, id: number) => `${name} ID ${id} not found`,
});
defineLocale์ sd.generated.ts์์ export๋๋ฉฐ, defaultLocale์ ํค๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์
์ฒดํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
Context์์ Locale ์ค์
์์ฒญ๋ณ๋ก locale์ ์ค์ ํ๋ ค๋ฉด ๋ฏธ๋ค์จ์ด์์ Context๋ฅผ ๊ตฌ์ฑํฉ๋๋ค:
// api/src/middlewares/locale.ts
import { Sonamu } from "sonamu";
export async function localeMiddleware(request: FastifyRequest) {
// Accept-Language ํค๋ ๋๋ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ์์ locale ์ถ์ถ
const locale = request.headers["accept-language"]?.split(",")[0]?.split("-")[0]
|| request.query.locale
|| "ko";
// Context์ locale ์ค์
const ctx = Sonamu.getContext();
ctx.locale = locale;
}
์ด๊ธฐํ ํ์ธ
์ค์ ์ด ์๋ฃ๋๋ฉด pnpm sync๋ฅผ ์คํํ์ฌ sd.generated.ts๊ฐ ์์ฑ๋๋์ง ํ์ธํฉ๋๋ค:
์์ฑ๋ sd.generated.ts์๋ ๋ค์์ด ํฌํจ๋ฉ๋๋ค:
- Entity์์ ์ถ์ถํ ๋ผ๋ฒจ (
entityLabels)
SD() ํจ์
localizedColumn() ํจ์
defineLocale() ํจ์