번역 문구 파일이 점점 늘어나면서 "이 문구를 어디에 넣어야 하지" 하고 헤매는 순간이 자주 왔어요. img-compressor 저장소messages/ko.json, messages/en.json이 이 문제를 어떻게 나눠서 풀었는지 볼게요. 두 파일은 최상위 키 구조는 같고 값만 언어별로 달라요.

최상위 키를 기능 단위로 나눴어요

{
  "common": { "windowTitle": "...", "menu": {...}, "status": {...}, "startMenu": {...} },
  "compressor": { /* 압축 도구 화면 전용 문구 */ },
  "language": { /* 언어 전환 UI 문구 */ },
  "seo": { /* 메타 설명, OG 문구 */ }
}

common은 메뉴, 상태 표시, 시작 메뉴처럼 여러 화면에서 재사용되는 문구를 모아둔 네임스페이스고, compressor는 압축 기능 화면에서만 쓰는 문구, languageLanguageSwitcher 전용, seo는 메타 태그용 문구예요. 화면 단위가 아니라 "여러 곳에서 재사용되나, 한 곳에서만 쓰이나"를 기준으로 나눴어요.

컴포넌트는 필요한 네임스페이스만 가져다 써요

const t = useTranslations("language");
// ...
<button aria-label={t("switchLanguage")}>
  {locale === "ko" ? t("korean") : t("english")}
</button>

LanguageSwitcheruseTranslations("language")language 네임스페이스만 가져와요. 전체 메시지 객체를 다 넘기는 대신 필요한 것만 지정하니까, 이 컴포넌트가 뭘 참조하는지 코드만 보고 바로 알 수 있고, common이나 compressor 쪽 문구가 바뀌어도 이 컴포넌트는 영향 안 받는다는 게 확실해지더라고요.

이 구조에서 자주 놓치는 부분

네임스페이스가 늘수록 어떤 문구를 어디에 넣을지 판단이 애매해지는 경우가 생기더라고요. 예를 들어 압축 화면에도 나오고 시작 메뉴에도 나오는 문구는, common이랑 compressor 중 어디에 둘지 팀 규칙이 없으면 같은 뜻 문구가 두 곳에 중복 선언될 수 있어요. 지금은 언어가 두 개뿐이라 이 문제가 크게 안 드러났는데, 언어나 네임스페이스가 늘어나면 따로 중복 검사가 필요해질 것 같아요.