i18next-parser ์‚ฌ์šฉํ•ด์„œ ๋ฒˆ์—ญ ํ”„๋กœ์„ธ์Šค ๊ฐœ์„ ํ•˜๊ธฐ

๋„์ž… ๋ฐฐ๊ฒฝ

ํšŒ์‚ฌ์—์„œ react-i18next๋กœ ๋‹ค๊ตญ์–ด๋ฅผ ์ ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ๋ฌธ์ œ๋Š” ๋ชจ๋“  ๋ฌธ๊ตฌ๊ฐ€ ํ‚ค ๊ธฐ๋ฐ˜์œผ๋กœ๋งŒ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์—ˆ๋‹ค.
์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” t("SOME_KEY") ํ˜•ํƒœ๋กœ๋งŒ ์“ฐ์ด๋‹ค ๋ณด๋‹ˆ, ์‹ค์ œ ํ…์ŠคํŠธ๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ํ‚ค๋ฅผ ๊ฒ€์ƒ‰ํ•ด JSON ํŒŒ์ผ์—์„œ ๊ฐ’์„ ์ฐพ์•„์•ผ ํ–ˆ๋‹ค. ์ฝ”๋“œ๋งŒ ๋ณด๊ณ ๋Š” ์–ด๋–ค ๋ฌธ๊ตฌ์ธ์ง€ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์—†์—ˆ๊ณ , ๋งฅ๋ฝ์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ๋ถˆํ•„์š”ํ•œ ๋น„์šฉ์ด ๊ณ„์† ๋ฐœ์ƒํ–ˆ๋‹ค. ๋˜, ์ƒˆ๋กœ์šด ๋ฌธ๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ๊ฐ ์–ธ์–ด๋ณ„ JSON์— ํ‚ค๋ฅผ ๋งŒ๋“ค๊ณ  ์ง์ ‘ ๊ฐ’์„ ์ž…๋ ฅํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€๋„ ์žˆ์—ˆ๋‹ค. ์˜จ๋ณด๋”ฉ ์ดํ›„ ๋งก์€ ๋ฆฌ์•กํŠธ ๋„ค์ดํ‹ฐ๋ธŒ ๊ด€๋ จ ์—ํ”ฝ์—์„œ๋„ ๊ฐ™์€ ๋ถˆํŽธ์ด ์žˆ์—ˆ๊ณ , ์•ž์œผ๋กœ ๋ฒˆ์—ญ ๋ฌธ๊ตฌ๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด ํŒ€ ์ „์ฒด๊ฐ€ ๊ฒช์„ ๋ฌธ์ œ๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋น ๋ฅด๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์•ˆ์„ ์ฐพ์•„ ํŒ€์— ์ œ์•ˆํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

i18next-parser ๋„์ž…

๋ชจ๋…ธ๋ ˆํฌ ํ™˜๊ฒฝ์—์„œ์˜ ์‚ฌ์šฉ

ํ˜„์žฌ ์—ฌ๋Ÿฌ ์•ฑ์ด ์žˆ๋Š” ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์กฐ๋ฅผ ์“ฐ๊ณ  ์žˆ๋Š”๋ฐ, i18next-parser๋Š” CLI๋กœ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•ฑ๋ณ„๋กœ ๋…๋ฆฝ ์„ค์ •์„ ๋‘๊ณ , ํ•„์š”ํ•œ ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. (ํŠน๋ณ„ํžˆ ๋ณต์žกํ•œ ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ • ์—†์ด๋„ ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Œ!)

๊ธฐ์กด ๋กœ์ผ€์ผ ๊ตฌ์กฐ ์œ ์ง€

๊ธฐ์กด์— ์“ฐ๋˜ ๋กœ์ผ€์ผ ๊ตฌ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์ž๋™ํ™”๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

src/utils/i18n/locales/
โ”œโ”€โ”€ ko-KR/ko-KR.json
โ””โ”€โ”€ ja-JP/ja-JP.json

์„ค์ •์—์„œ ์ถœ๋ ฅ ๊ฒฝ๋กœ๋งŒ ๋งž์ถฐ์ฃผ๋ฉด ๋œ๋‹ค.

์ ์ง„์  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ

keepRemoved: true ๋กœ ์„ค์ •ํ•ด ์ฃผ๋ฉด ๊ธฐ์กด ํ‚ค๋Š” ์œ ์ง€๋˜๊ณ , ์ƒˆ ๋ฌธ์ž์—ด์€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ ์ง„์ ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

// ๊ธฐ์กด
t("BUTTON_SUBMIT")

// ์ƒˆ๋กœ์šด
t("์ œ์ถœํ•˜๊ธฐ")

++ ์œ ์‚ฌ ๋„๊ตฌ์ธ i18next-scanner๋Š” Gulp ๋“ฑ ํŒŒ์ดํ”„๋ผ์ธ์— ์–น๊ธฐ ์ข‹๊ณ , i18next-parser๋Š” CLI๋กœ ๋‹จ์ˆœํ•˜๊ฒŒ ์นดํƒˆ๋กœ๊ทธ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์ข‹๋‹ค๊ณ  ํ•œ๋‹ค. ์šฐ๋ฆฌ ํ™˜๊ฒฝ์—์„œ๋Š” ๋นŒ๋“œ ์ฒด์ธ ๊ฒฐํ•ฉ์ด ํ•„์š” ์—†์–ด์„œ parser๊ฐ€ ๋” ์ ํ•ฉํ•ด ๋ณด์˜€๋‹ค.

์„ค์ •

// i18next-parser.config.cjs
module.exports = {
  input: ['src/**/*.{ts,tsx,js,jsx}', '!src/utils/i18n/locales/**'],
  output: // ํŒŒ์ผ ๊ฒฝ๋กœ,
  locales: ['ko-KR', 'ja-JP'],
  defaultNamespace: 'translation',
  keySeparator: false,
  namespaceSeparator: false,
  defaultValue: (locale, _ns, key) =>
    locale === 'ko-KR' ? key : `[๋ฒˆ์—ญํ•„์š”] ${key}`,
  keepRemoved: true, 
  sort: false,   
  ...
};

์ž๋™ํ™” ์‹œ ํ•œ๊ตญ์–ด๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” [๋ฒˆ์—ญํ•„์š”] prefix๋ฅผ ๋ถ™์—ฌ, ๋ฒˆ์—ญ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์„ ํ•œ๋ˆˆ์— ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค. ์ด์ œ๋Š” ํ•œ๊ตญ์–ด ์›๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์ฝ”๋“œ์— ์ž‘์„ฑํ•˜๊ณ  yarn i18n:extract๋งŒ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™ ๋ฐ˜์˜๋œ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ์‹

t('๋กœ๊ทธ์ธ')
t('์ €์žฅ {{value}}ํšŒ', { value })
t('์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค')
<Trans i18nKey="greeting">
  ์•ˆ๋…•ํ•˜์„ธ์š”, <strong>{{name}}</strong>๋‹˜!
</Trans>

ํŒŒ์„œ๊ฐ€ ๋ฌธ์ž์—ด์„ ์ถ”์ถœํ•ด ko-KR/translation.json, ja-JP/translation.json์— ๋ฐ˜์˜ํ•˜๊ฒŒ ๋˜๊ณ , ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€๋œ๋‹ค.

{
  "๋กœ๊ทธ์ธ": "[๋ฒˆ์—ญํ•„์š”] ๋กœ๊ทธ์ธ",
  "์ €์žฅ {{value}}ํšŒ": "[๋ฒˆ์—ญํ•„์š”] ์ €์žฅ {{value}}ํšŒ",
  "์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค": "[๋ฒˆ์—ญํ•„์š”] ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
}

++ ๊ฐœ์„  ๋ฐฉํ–ฅ์„ ์ •๋ฆฌํ•ด์„œ ํŒ€์›๋ถ„๋“ค๊ป˜ ๊ณต์œ ๋“œ๋ ธ๊ณ , ๋‹คํ–‰ํžˆ ํ˜„์žฌ์˜ ๋ถˆํŽธํ•จ์— ๋‹ค๋“ค ๊ณต๊ฐํ•ด ์ฃผ์…”์„œ ๋„์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค!