글로벌을 무대로 서비스하는 웹 애플리케이션은 국제화(i18n)가 필수다. 이를 위하여 국제화 라이브러리를 사용하거나 직접 구현하기도 한다. 이 글은 국제화 지원을 위한 개발과 번역 과정에서 이른바 "복붙"이나 반복적인 수작업으로 인해 고통받는 모든 프런트엔드 개발자를 위하여 작성하였다. 이 자동화 가이드를 따른다면 여러분은 단 한 줄의 스크립트 실행만으로 고통에서 해방될 것이다.
gettext를 활용한 라이브러리나 i18next와 같은 라이브러리는 다음 예제와 같이 key
를 받고 번역된 문자열을 리턴하는 함수를 제공한다.
// 번역 데이터
const en_US = {
이름: 'name',
};
// gettext의 경우
const translation = gettext('이름');
// 'name'
// i18next의 경우
const translation2 = i18next.t('이름');
// 'name'
사용자가 설정한 언어에 따라서 리턴된 문자열은 달라질 수 있다.
필자가 개발에 참여하고 있는 Dooray!는 한글, 영어, 일본어, 중국어를 지원하는 글로벌 올인원 협업 도구다. 온라인에서 필요한 협업 기능을 대부분 지원하기 때문에 애플리케이션에 포함되어 있는 텍스트가 매우 방대하다. 번역에 사용하는 key
의 개수가 만 개에 육박한다. 번역할 문자열이 많아지면서 개발자, 기획자, 번역가 간에 협업 비용이 많아졌고, 오래 걸리는 번역 작업을 배포할 때마다 반복적으로 해야 하는 어려움을 겪었다.
개발을 계속하는 한 신규 프로젝트는 앞으로 계속 생길 것이다. 온라인으로 전 세계가 연결되어 있고 여러분의 눈부신 아이디어를 글로벌에 서비스하려면 국제화도 계속 구현해야 할 것이다. 그러므로 반복적이고 시간이 많이 드는 작업, 손으로 하다 보니 실수하기 쉬운 국제화 작업을 개선할 필요가 있다.
기존 문제점을 일일이 열거하기는 어려우므로 수작업이 필요한 과정만 살펴보자.
key
를 관리하는 자바스크립트 모듈에 key
를 직접 추가한다.key
를 엑셀에 직접 추가하고 번역가에게 파일을 메일로 전달하면서 번역이 필요한 부분을 알린다.이 과정에서 소스 코드에 추가된 key
를 스캔해 주는 도구를 사용하면 수작업을 많이 줄일 수 있다. 그러나 파일을 주고 받는 번거로움이 있고, 어떤 브랜치의 어떤 시점을 기준으로 작성된 번역 파일인지를 알기 어려웠다. 또한 번역된 파일이 저장소에 관리되므로 번역 파일의 Conflict를 해결해야 하는 것도 불편한 점 중에 하나였다.
구글 스프레드 시트를 사용하면 파일을 직접 주고 받지 않고 편리하게 협업할 수 있다. 스프레드 시트를 업데이트하고 다운로드하는 자동화 도구까지 적용하면 어느 브랜치에서 개발하던지 추가된 모든 key
는 스프레드 시트에 있으므로 누락되지 않고 번역을 요청할 수 있다. 파일을 주고 받는 것보다 버전관리를 따로 하지 않는 것이 장점이다. 번역을 위한 도구를 별도로 설치하지 않아도 브라우저만 있으면 되므로 기획자, 번역가도 사용하기 쉽다.
지금부터 구글 스프레드 시트를 사용한 자동화 도구를 추가하여 반복적이고 시간이 많이 드는 작업, 손으로 하다 보니 실수하기 쉬운 국제화 작업을 개선해보자.
국제화를 자동화하기 위해서 크게 두 가지 기능을 만들 것이다.
key
를 스캔하고 추가된 key
를 구글 스프레드 시트에 업로드한다.자동화를 위해 몇 가지 Node.js 패키지를 먼저 설치한다.
npm install i18next // 작성 당시 버전 19.6.3
npm install -D i18next-scanner // 작성 당시 버전 2.11.0
npm install -D google-spreadsheet // 작성 당시 버전 3.0.13
i18next는 자바스크립트 국제화 라이브러리다. 복수형(plural), 인터폴레이션(interpolation), 문맥(context) 기능이 있고, i18next.t('key')
함수를 사용해 번역된 결과를 표현한다.
const translation = i18next.t('이름');
// 'name'
i18next-scanner는 소스 코드에서 i18next.t()
, i18n.t()
와 같은 지정된 패턴의 함수를 스캔하여 key
를 추출하고 언어별 json파일을 생성한다.
{
"translation": {
"이름": "",
"키값...": ""
}
}
google-spreadsheet는 Google Sheets API (v4)를 Node.js에서 사용할 수 있도록 래핑한 라이브러리다. 스프레드 시트를 생성하거나 행을 읽고 추가하거나 셀을 읽고 조작할 수 있다.
const doc = new GoogleSpreadsheet('spreadSheetDocId');
...
const sheet = doc.sheetsById['sheetId'];
const rows = await sheet.getRows();
...
await doc.addRows(newRows);
전체 설정 파일은 다음 링크에서 확인할 수 있고, 핵심적인 설정들만 설명한다.
package.json에 스크립트 추가
{
"scan:i18n": "i18next-scanner --config i18next-scanner.config.js",
"upload:i18n": "npm run scan:i18n && node translate/upload.js",
"download:i18n": "node translate/download.js",
"serve": "npm run download:i18n && vue-cli-service serve"
}
scan:i18n
은 소스 코드에서 key
를 추출하여 key
, value
로 구성된 언어별 json파일을 만들어 낸다. upload:i18n
은 생성된 여러 개의 언어별 json파일을 하나의 테이블로 만들어 구글 스프레드 시트에 업로드하며, download:i18n
은 반대로 동작하여 번역된 값을 각 언어별 json파일에 반영한다. 로컬에서 개발할 때나 프러덕션 빌드하기 전에 수행하여 번역 파일이 빌드에 포함할 수 있도록 npm 스크립트에 추가한다.
이제부터 번역에 사용할 스프레드 시트 파일을 새로 생성해야 한다. 소스코드에서 사용할 스프레드 시트의 아이디는 스프레드 시트의 URL을 보면 알 수 있다. URL의 spreadSheetDocId
에 있는 문자열이다.
그리고 google-spreadsheet
를 사용하여 자바스크립트에서 스프레드 시트를 조작해야 하므로 인증을 획득해야 한다. 다음 문서를 참고하여 사용할 구글 계정의 인증을 설정한다.
서비스 계정으로 봇(bot) 사용자를 추가하고 다운로드 받은 json 인증서(creds
)를 다음과 같이 사용한다.
const { GoogleSpreadsheet } = require('google-spreadsheet');
const creds = require('./.credentials/your-app-some-unique-id.json');
const doc = new GoogleSpreadsheet('spreadSheetDocId');
await doc.useServiceAccountAuth(creds);
await doc.loadInfo();
json 인증서는 보안에 주의해야할 파일이므로 실제 서비스에서 사용되는 경우 공개 깃헙에 올려두지 않도록 주의하자. 파일로 관리하지 않으려면 client_email
과 private_key
를 다음과 같이 입력할 수 있도록 따로 관리할 수도 있다.
await doc.useServiceAccountAuth({
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY,
});
그런 다음 생성한 스프레드 시트를 브라우저에서 열고 문서 공유자에 서비스 계정을 추가하면, google-spreadsheet
로 스프레드 시트를 조작할 수 있다.
설정은 모두 마쳤다. 이제 npm run upload:i18n
을 실행하면 소스 코드의 모든 key
가 스프레드 시트에 다음과 같은 형식으로 업로드된다.
키 | 한글 | 영어 | 일본어 | 중국어 | 비고 |
---|---|---|---|---|---|
이름 | _N/A | name | _N/A | _N/A | 영어 단수 |
이름_0 | 이름 | _N/A | 名前 | 姓名 | 복수형 없는 언어 |
이름_plural | _N/A | names | _N/A | _N/A | 영어 복수 |
번역가는 이 표를 보고 비어있는 셀을 찾아 번역을 완료하면 된다.
(i18next에서 단수, 복수 번역을 지원하는데 언어마다 복수 표현이 다르므로 소스 코드에서는 같은 key
라도 언어별 json에 생성되는 키는 "key_0", "key_1", "key_plural"처럼 접미사(postfix)가 붙어 있다. 언어에 따라 key
가 다르게 생성될 수 있으므로 복수 표현이 필요 없는 언어에서 "_N/A"로 명시하여 번역할 필요가 없음을 나타내었다.)
npm run upload:i18n
스크립트 실행 후 번역 요청이게 끝이다. 빌드할 때마다 npm run download:i18n
이 실행되어 스프레드 시트로부터 최신 번역값이 빌드에 반영되므로 신경 쓸 부분은 없다. 로컬 서버 구동이나 프러덕션 빌드, 테스트, 스토리북 실행 등 필요할 때 npm 스크립트에 추가하면 된다.
{
"scripts": {
"build": "npm run download:i18n && webpack"
}
}
번역이 필요한 셀은 배경색을 지정한다. 그러면 번역가가 번역해야 하는 부분을 알기 쉽다. 구글 스프레드 시트에서 언어 컬럼(한글, 영어, 일본어, 중국어)을 선택하고, 메뉴 > 서식 > 조건부 서식에서 다음에 맞도록 조건부 서식 규칙을 추가한다.
처음에는 설정하느라 노력에 들겠지만 완료하고 나면 그 다음부터는 진짜 npm 스크립트 한줄만 실행하면 된다. 도움이 되길 바란다.
진짜 끝.