TOC
들어가며
저는 이제 이 extension 뺏으면 다국어 개발 못합니다. i18n-ally를 소개합니다.
배경 소개
저는 EXEM에서 공식 홈페이지 리뉴얼을 담당하고 있습니다. (리뉴얼을 주요 목표로 하는 브랜드팀의 유일한 개발자입니다.) 저희 EXEM은 해외 법인을 보유한 글로벌 기업이기 때문에, 현재 한글과 영어, 그리고 향후 일본어와 중국어를 공식 언어로 제공할 계획을 가지고 있습니다.
이런 상황에서, 저는 Next.js로 애플리케이션을 구성했고, next-intl을 사용하여 다국어 구조를 설계했습니다. (선택의 과정은 있었지만, 오늘은 그 이야기는 접고 넘어가겠습니다.)
중요한 것은 단순히 다국어 구조를 갖춘 것 뿐만 아니라, 더 좋은 구조와 DX를 위해 활용할 수 있는 툴을 조사하던 중에 이 VSCode extension을 발견했습니다. i18n-ally입니다. (참고로 i18n은 Internationalization의 축약어입니다. 국제화, 다국어를 의미합니다.)
그런데 i18n-ally, 왜 써야 할까요?
다국어 개발의 어려움
다국어 개발 경험은 해봤거나 해보지 않았거나, 둘 중 하나겠죠. 안 해보신 분들을 위해 간단히 다국어 개발의 어려움을 소개합니다.
문제 1. key로는 식별이 어려운 텍스트
다국어는 컴포넌트에 텍스트를 그대로 넣지 않습니다.
- 다국어 key를 정의합니다.
- 해당 key에 대응하는 텍스트를 언어별로 json 파일에 지정합니다.
- namespace, key를 통해 변수/함수로 컴포넌트에서 사용합니다.
그러다 보니, 그 수많은 key들이 컴포넌트에 박혀 들어가는데, 어떤 텍스트인지 도무지 알 수 없습니다.
문제 2. 언어별 비교가 어려운 구조
하나의 key에 대한 여러 언어의 매핑을 한눈에 비교하는 것이 쉽지 않습니다.
언어가 늘어날수록 어려움은 가파르게 증가합니다.
문제 3. 누락하기 쉬운 구조
컴포넌트에 넣은 텍스트가 바로 렌더링되는 게 아니라, key를 넣고 key에 1:N으로 매핑된 언어들이 보여지다 보니 이런 문제가 종종 발생합니다.
- key를 바꿨는데 언어 json은 변경이 안 된 경우
- 한글 json은 바꿨는데 영어 json은 놓친 경우
사용한 key가 언어 JSON 파일에 없으면 앱에서 오류가 발생합니다.
i18n-ally로 해결하기
i18n-ally는 이런 문제를 해결하기 위해 다양한 기능을 제공합니다. 가장 드라마틱한 효과를 맛보고 넘어가겠습니다.
Inline Annotation이라는 기능입니다. 쉽게 말해 미리보기인데요, 특정 key에 매핑된 대표 언어 텍스트를 미리 보여줍니다. 식별이 잘 되네요.
이 덕분에 한 가지 효과가 더 있습니다. 바로 누락된 key를 컴포넌트에서 바로 찾을 수 있게 됩니다.
제가 임의로 한글 json 파일의 key를 변경해버리면 이렇게 되죠.
하지만 대표로 설정한 언어들에 대해서만 유효한 효과입니다. 물론 key 누락을 잡는 더 좋은 방식은 있습니다. 이후에 소개하겠습니다.
i18n-ally 공식 소개
우선 i18n-ally에서 공식적으로 제공하는 설명과 예시들을 정리해보겠습니다.
Hover and Direct Actions
마우스를 올리면 여러 기능을 사용할 수 있습니다.
- 대표 언어 텍스트로 '실제 key'를 미리 보여줍니다.
- 해당 key에 매핑된 모든 언어 텍스트를 한눈에 볼 수 있습니다.
- JSON을 열지 않더라도 특정 언어 텍스트를 바로 수정할 수 있습니다.
- 대표 언어를 기준으로 자동으로 번역이 가능합니다.
- 특정 언어의 해당 키 텍스트 위치로 바로 이동할 수 있습니다.
- key를 번역하거나 제안 댓글을 다는 에디터를 열 수 있습니다.
Manage All Translations in One Place
모든 언어를 한 곳에서 관리할 수 있습니다. 다음과 같은 기능이 있습니다.
- 현재 파일에서 사용 중인 key들을 볼 수 있습니다.
- 현재 파일에서 사용 중인 key 중 언어 파일에 없는 key들을 찾을 수 있습니다.
- 전체 언어 파일을 비교하여 없는 key 또는 값이 비어 있는 key를 찾을 수 있습니다.
- 현재 언어 파일의 텍스트에 대한 제안 댓글을 확인할 수 있습니다.
정말 강력한 기능입니다. i18n-ally를 사용하는 주요 이유 중 하나입니다.
저희 서비스의 상황을 보면 이렇습니다.
Editor UI & Review System
특정 key에 대하여 바로 번역을 하고, 그에 대한 review를 달거나 확인할 수 있습니다.
팀원이나 오픈소스인 경우 유용하겠다만, 저는 private repo에서 혼자 작업하는 프로젝트라 사용 경험은 없습니다.
Report Missing Translations
누락된 번역 key를 터미널에서 확인할 수 있습니다.
Machine Translation
- Inline 레벨에서 자동으로 번역이 가능합니다.
- DeepL, Google 번역 등의 기계 번역을 사용합니다. (설정 방법)
- 번역 엔진별 설정을
settings.json에 추가해 주어야 하며, API key가 필요할 수 있습니다. (ex. DeepL)
Annotations for JSON and YAML
- Annotations for JSON and YAML
- JSON과 YAML에서 Inline 레벨로 특정 key에 매핑된 대표 언어 텍스트를 미리 볼 수 있습니다.
i18n-ally의 사용
Supported Frameworks
React뿐만 아니라 Vue, Angular, Flutter, Svelte 등 수많은 프레임워크를 지원합니다.
프로젝트 기본 설정
먼저, i18n-ally 설치부터 프로젝트 적용까지 핵심만 소개하고, 세부 configuration은 더 아래에서 이어서 소개하겠습니다.
Installation
VS Code Marketplace에서 설치하면 됩니다.
VS Code Configuration
본 설정은 Next.js, next-intl과 함께 언어별 단일 json 파일 구조를 사용하는 경우를 기준으로 합니다.
저는 모노레포를 사용하고 있고, next-intl에서 제안한 다국어 폴더 구조를 가집니다.
apps/
├── homepage/
│ ├── messages/
│ │ ├── en.json
│ │ ├── ko.json
│ │ └── ...
│ └── ...
이런 경우 이렇게 설정했습니다.
// .vscode/settings.json
{
"i18n-ally.localesPaths": ["apps/homepage/messages"], // 다국어 파일이 위치한 경로를 설정
"i18n-ally.keystyle": "nested", // 번역 키의 스타일을 설정 (nested: 중첩)
"i18n-ally.enabledParsers": ["json"],
"i18n-ally.enabledFrameworks": ["next-intl"],
"i18n-ally.sourceLanguage": "ko", // 기본 소스 언어를 설정
"i18n-ally.displayLanguage": "ko", // VSCode에서 표시할 언어를 설정
"i18n-ally.pathMatcher": "{locale}.json",
"i18n-ally.namespace": true, // https://github.com/lokalise/i18n-ally/wiki/Namespaces
"i18n-ally.keepFulfilled": true, // https://github.com/lokalise/i18n-ally/wiki/FAQ#-recommended-setup
// "i18n-ally.theme.annotation": "rgba(255, 255, 255, .6)",
// "i18n-ally.theme.annotationBorder": "rgba(255, 255, 255, 0)",
// "i18n-ally.theme.annotationMissing": "rgba(255, 0, 0, .8)",
// "i18n-ally.theme.annotationMissingBorder": "rgba(255, 0, 0, .8)"
}
그리고 팀원이 생긴다면 프로젝트에서 추천하고자 하는 extension들을 모아둘 목적으로 .vscode/extensions.json 파일을 만들어 extension들을 저장해두었습니다.
// .vscode/extensions.json
{
"recommendations": [..., "lokalise.i18n-ally"]
}
next-intl 사용시
저처럼 next-intl을 사용하시는 경우, next-intl - i18n-ally Integration을 참고하셔도 됩니다. 다만, 아래의 공식 configuration과 제 설정 코드를 첨부할 테니 이것만 보셔도 됩니다.
공식 Configurations
위에서 사용한 설정 옵션들을 간단히 소개합니다. 위의 링크와 아래 이미지를 참고해주세요.
이 base configuration에는 잘 드러나지 않는 추가 설정들을 이어서 소개합니다.
sortkey
"i18n-ally.sortKeys": true,
key를 같은 scope에 대하여 오름차순으로 정렬합니다. 기본적으로 정렬되어 있지 않고, 명시해주어야 합니다.
저는 오름차순 정렬이 아닌, UI에 더 맞는 자체 순서로 등록해 두었기 때문에 설정을 추가하지 않았습니다. 프로젝트 상황과 팀 컨벤션/개인 기호에 맞추시면 되겠죠.
theme
annotation theme도 설정할 수 있습니다.
{
"i18n-ally.theme.annotation": "rgba(255, 0, 0, .8)",
"i18n-ally.theme.annotationBorder": "rgba(0, 255, 0, .8)",
"i18n-ally.theme.annotationMissing": "#d37070",
"i18n-ally.theme.annotationMissingBorder": "#d37070"
}
설정은 rgba, hex 등 다양한 형식을 지원합니다. 위의 코드로 설정하면 이런 결과를 얻을 수 있습니다.
저는 annotationMissing 관련 설정은 잘 적용되지 않았습니다. 크게 중요하지는 않아서 넘어갔습니다.
settings.json 작성 요령
settings.json은 보수적으로 작성하는 게 좋습니다.
settings.json은 프로젝트를 다루는 모든 개발자들에게 공통된 설정을 적용하게 합니다. 그래서 테마 관련 설정은 settings.json에 작성하지 않는 편이 좋습니다. 프로젝트의 개발자들마다 에디터의 테마가 다를 수 있는데 공용 설정으로 테마를 적용한다면 누군가에게는 적합하지 않을 수 있기 때문입니다. 마찬가지로 취향을 탈 수 있는 설정은 공용 설정에서는 제외하고, localesPaths, pathMatcher 등 프로젝트에 따라 불변하는 설정만 공용 settings에 작성해 두면 좋겠죠.
혹은 configuration에 익숙하지 않은 동료를 위해서 주석 처리하고 가이드처럼 활용해도 좋겠습니다.
Troubleshooting
제가 경험했거나, 만나면 어려울 것 같은 부분들을 공식문서를 돌아보며 파악해봤습니다.
monorepo 사용시
Usually, i18n ally looks for your package.json file at the root of your project. If you don't have it at the root, you can always specify the framework(s) you are using by i18n-ally.enabledFrameworks config.
i18n-ally - FAQroot 디렉토리 기준으로 i18n-ally의 path를 설정하면 됩니다. 공식 문서에서는 고급 디렉토리 구조에 대해 이런 예시를 들고 있습니다.
패키지에 이런 다국어 폴더 구조가 있다고 가정해봅니다.
packages
├── pkgA
│ └── i18n
│ ├── en.messages.json
│ ├── zh-CN.messages.json
│ └── ...
├── pkgB
│ └── i18n
│ ├── en.messages.json
│ ├── zh-CN.messages.json
│ └── ...
└── ...
그렇다면 localesPaths와 pathMatcher를 이렇게 설정하면 됩니다.
{
"i18n-ally.localesPaths": [
"packages/**/**/i18n"
],
"i18n-ally.pathMatcher": "{locale}.messages.json",
}
참고로 위에 정리했듯이 저는 이렇게 했습니다.
{
"i18n-ally.localesPaths": ["apps/homepage/messages"],
"i18n-ally.pathMatcher": "{locale}.json",
...
}
- 그래도 잘 되지 않는다면
enabledParsers나enabledFrameworks등의 설정이 잘못된 문제일 수 있으니 확인이 필요합니다. - pathMatcher에 대한 고급 설정은 i18n-ally - Path Matcher를 참고해주세요.
namespace 설정
i18next, Laravel 등의 i18n 프레임워크는 namespace를 지원합니다. 기본적으로 locale 파일 이름을 i18n key의 루트로 매핑하는데, 이런 식입니다.
// en/common.json
{
"hello": "Hello",
"foo": {
"bar": "Foobar"
}
}
이런 JSON 언어 구조가 있다고 가정했을 때, 아래처럼 쓸 수 있는 겁니다.
i18n.t('common.hello') // Hello
i18n.t('common.foo.bar') // Foobar
하지만 i18n-ally는 기본적으로는 namespace를 지원하지 않습니다. 그래서 이런 설정을 추가로 해주어야 합니다.
{
"i18n-ally.namespace": true
}
마치며
사이드 프로젝트로의 기여
다국어 애플리케이션의 구조를 설계하면서, 당장의 개발, 미래의 유지보수, 확장이 용이한 구조를 어떻게 만들 수 있을지 고민도, 탐구도 많이 했습니다. 그리고 그 과정에서 이렇게 좋은 툴을 발견할 수 있어 보석을 발견한 듯한 기분이었습니다. 회사 개발에서 얻은 노하우를 사이드 프로젝트로 참여하던 GitAnimals에 소개하고, 적용해 볼 수도 있었죠.
추가로 next-intl의 404 관련 문제를 겪은 적이 있는데 해결을 못하고 넘어갔었습니다. 그런데 회사 개발을 하며 해결할 수 있는 정보를 얻게 되었습니다. 다국어 토픽으로 묶어 한 번에 반영해보았습니다.
아래 PR에서 내용을 확인하실 수 있습니다.
https://github.com/git-goods/git-animal-client/pull/345회사 개발과 개인 개발
저는 회사 개발과 개인 개발을 함께 하려 합니다. 각지에서 얻은 경험과 트러블슈팅, 지식들은 다시 서로에게 노하우가 됩니다. 블로그를 혼자서 구축하며 헤맸던 경험은 홈페이지 리뉴얼 개발을 혼자서 설계부터 개발까지 할 수 있게 만드는 거름이 되었고, 회사 개발을 더 잘하기 위해 R&D했던 지식으로 개인 개발을 더 탄탄하고 빠르게 할 수 있는 실력의 성장을 얻었습니다. 이 시너지를 회사를 다니시는 많은 개발자분들께서도 경험하시면 좋겠습니다.
다국어 개발
주제를 조금 벗어났네요. 오늘의 핵심은 다국어 개발 extension i18n-ally의 소개였습니다. 제 포스팅을 통해 모르셨던 분들은 도입하거나 잘 활용해 보시면 좋겠습니다. 혹시나 잘못된 정보나 추가할 정보가 있다면 언제든 공유해주세요. 감사합니다!
