webpack 기반 H5 게임에 i18next 적용하기
글로벌 출시하는 H5 게임에 i18next로 다국어를 붙였다. 번들러는 webpack이다.
| 패키지 | 용도 |
|---|---|
i18next |
번역 함수 t와 언어 관리 |
i18next-xhr-backend |
서버에 둔 언어별 JSON을 XHR로 받아 온다 |
i18next-browser-languagedetector |
브라우저에서 사용할 언어를 감지한다 |
i18next-korean-postposition-processor |
한국어 조사를 앞 글자에 맞춰 고른다 |
ya-i18next-webpack-plugin |
webpack 설정에 플러그인으로 넣어 번역 파일의 번들링 옵션을 지정한다 |
webpack은 프로젝트의 모든 파일을 모듈로 다루므로, 다국어 관련 파일도 webpack 설정 안에서 모듈로 다뤄야 했다.
언어 감지 순서
섹션 제목: “언어 감지 순서”i18next-browser-languagedetector가 지원하는 감지 소스는 querystring, hash, cookie, localStorage, sessionStorage, navigator, htmlTag, path, subdomain이다. order 배열에 넣은 순서대로 확인하고, 직접 만든 감지기는 addDetector로 등록한 뒤 그 이름을 order에 넣어야 실행된다(README).
게임에서는 쿼리스트링, localStorage, navigator 순서만 썼다. 나머지 소스는 필요가 없었다.
import i18next from 'i18next';import LanguageDetector from 'i18next-browser-languagedetector';
i18next.use(LanguageDetector).init({ detection: { order: ['querystring', 'localStorage', 'navigator'], lookupQuerystring: 'lng', // ?lng=ko 로 언어 지정 lookupLocalStorage: 'i18nextLng', caches: ['localStorage'], // 감지한 언어를 저장할 곳 excludeCacheFor: ['cimode'], // 이 언어는 저장하지 않는다 },});lookupQuerystring, lookupLocalStorage, caches, excludeCacheFor의 위 값은 라이브러리 기본값과 같다(src/index.js의 getDefaults). 기본 order는 ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag']이므로 쿠키·sessionStorage·<html lang>을 보지 않으려면 order를 직접 지정한다.
적용 시점과 언어 변경
섹션 제목: “적용 시점과 언어 변경”게임이 실행되기 전에 언어 설정이 끝나 있어야 한다. 그래서 entry 진입 직후 i18next 설정 모듈을 import하고, 번역 함수를 window 객체에 바인딩했다.
게임 설정에서 언어를 바꾸면 바로 반영되도록 언어 변경을 이벤트로 연결했다. i18next는 changeLanguage(lng)가 호출되면 languageChanged 이벤트를 낸다(API).
i18next.on('languageChanged', (lng) => { // 화면의 텍스트를 다시 그린다});
i18next.changeLanguage('en');한국어 조사 후처리기
섹션 제목: “한국어 조사 후처리기”한국어는 명사 뒤에 붙는 조사가 앞 글자의 받침 유무에 따라 달라진다(사과를, 귤을). 번역 문자열에 변수를 끼워 넣으면 어느 조사를 쓸지 미리 정할 수 없어서 후처리기로 고른다.
i18next의 후처리기는 type: 'postProcessor', name, process(value, key, options, translator)를 가진 객체이고, t가 값을 돌려주기 전에 문자열을 바꾼다(Creating own Plugins). init의 postProcess 옵션에 이름을 넣으면 기본으로 적용된다(Configuration Options).
i18next-korean-postposition-processor는 이름이 korean-postposition이고, 번역 문자열에서 조사를 겹대괄호로 감싸 표시한다. 슬래시 쌍([을/를])이 아니라 한쪽 형태 하나만 적는다(README).
import KoreanPostpositionProcessor from 'i18next-korean-postposition-processor';
i18next.use(KoreanPostpositionProcessor).init({ postProcess: ['korean-postposition'], resources: { ko: { translation: { got: '{{item}}[[를]] 얻었다.' } }, },});
i18next.t('got', { lng: 'ko', item: '사과' }); // 사과를 얻었다.i18next.t('got', { lng: 'ko', item: '귤' }); // 귤을 얻었다.지원하는 조사 쌍은 을/를, 이/가, 은/는, 으로/로, 과/와, 이랑/랑이다. 한글뿐 아니라 소수점 없는 아라비아 숫자도 읽는 소리 기준으로 처리한다.
받침 판정은 완성형 한글 음절(U+AC00~U+D7A3)의 코드 배치를 이용한다. 음절 코드는 0xAC00 + (초성 × 21 + 중성) × 28 + 종성이므로 (code - 0xAC00) % 28이 0이면 받침이 없다. 패키지 1.0.0의 한글 판정 코드도 같은 식이다.
function hasFinalConsonant(word) { const code = word.charCodeAt(word.length - 1); if (code < 0xac00 || code > 0xd7a3) return undefined; // 완성형 한글이 아니면 판단하지 않는다 return (code - 0xac00) % 28 !== 0;}으로/로는 받침이 ㄹ일 때도 로를 쓰는데(길로), 위 식은 받침 유무만 본다. 패키지 1.0.0도 ㄹ 받침을 따로 처리하지 않으므로 ㄹ로 끝나는 단어 뒤에 [[로]]를 쓰면 으로가 나온다.
cimode로 키 확인하기
섹션 제목: “cimode로 키 확인하기”lng를 'cimode'로 두면 t가 번역 대신 키를 그대로 돌려준다. appendNamespaceToCIMode: true면 키 앞에 namespace도 붙는다(Configuration Options). 화면에 어떤 키가 쓰였는지 바로 볼 수 있어 번역 누락이나 잘못 연결된 키를 찾을 때 쓴다.
i18next.changeLanguage('cimode');i18next.t('got'); // 'got'언어 감지기의 excludeCacheFor 기본값이 ['cimode']라서, 디버깅하려고 ?lng=cimode로 열어도 localStorage에 저장되지 않는다. 다음 접속은 원래 언어로 돌아간다.