이번에 회사 사이트에서 다국어를 지원하려고 계획중이라, react 다국어 지원 라이브러리를 검색해봤어요.
SSR(server-side-rendering)을 사용하지 않는다면 기능이 더 많은 react-intl을 쓰는 게 좋다고 해서,
저는 react-intl을 사용하기로 했어요.
공식 document가 그렇게 좋은 편은 아니라서, 다른 분들이 올린 블로그 글도 몇 개 봤는데, 다들 아주 심플한 테스트 코드만 작성하셨더라고요. 저는 nested object를 사용해야 해서 조금 더 찾아보면서 개발했어요. 저같은 분들을 위해 과정을 공유합니다.
설치
먼저 라이브러리를 설치합니다.
$ yarn add react-intl
설정
다국어를 지원할 Text들을 넣을 폴더와, index 파일을 만듭니다.
저는 src/locales에 생성했어요.
폴더명은 BCP47 기준으로 생성했고, 변수명은 ISO 693-1 기준으로 생성했습니다.
(명명규칙을 저랑 똑같이 하실 필요는 없어요. 저는 편의상 이렇게 지정했습니다.)
영어, 스페인어, 일본어, 한국어, 중국어(간체)를 지원할 예정이라, 아래와 같이 index 파일을 설정했어요.
// src/locales/index.js
import localeEnUs from 'src/locales/enus';
import localeEsEs from 'src/locales/eses';
import localeJpJp from 'src/locales/jpjp';
import localeKoKr from 'src/locales/kokr';
import localeZnCn from 'src/locales/zncn';
export const en = { ...localeEnUs };
export const es = { ...localeEsEs };
export const ja = { ...localeJpJp };
export const ko = { ...localeKoKr };
export const zh = { ...localeZnCn };
export default { en, es, ja, ko, zh };
locales 폴더의 구성은 이렇게 되어있어요.
각 언어별로 common.json 파일과 index.js 파일이 있답니다.
json파일은 원하는 대로 많이 추가해도 좋아요.
index는 아래와 같이 아주 심플한 import와 export만 있어요.
import common from 'src/locales/kokr/common.json';
const index = {
common
};
export default index;
common.json의 내용은 다음과 같습니다.
다른 폴더의 내용물도 언어만 다르고 내용은 모두 같습니다.
에러 페이지를 렌더링하는 부분을 모아봤어요.
// src/locales/kokr/common.json
{
"tomain": "메인화면 바로가기",
"404": {
"description": [
"요청하신 페이지를 찾을 수 없습니다.",
"방문하시려는 페이지의 주소가 잘못 입력되었거나,",
"페이지의 주소가 변경 혹은 삭제되어 요청하신 페이지를 찾을 수 없습니다.",
"입력한 주소가 정확한지 다시 한번 확인해 주시기 바랍니다."
]
},
"503": {
"description": [
"서비스를 이용할 수 없습니다.",
"서버에 문제가 발생했거나, 서버 작업 중입니다.",
"서버 작업은 대체로 1-2시간 내에 완료됩니다.",
"잠시 후 다시 시도해 주세요."
]
}
}
// src/locales/enus/common.json
{
"tomain": "Go to main",
"404": {
"description": [
"The page you requested could not be found.",
"The page you requested could not be found because the address of the page you are trying to visit was incorrectly entered,",
"or the page's address was changed or deleted.",
"Please check if the address you entered is correct."
]
},
"503": {
"description": [
"Service is not available.",
"There's a problem with the server, or we're working on the server.",
"Server operations are typically completed in one or two hours.",
"Please try again in a moment."
]
}
}
적용
이제 전체 App을 <IntlProvider>로 감싸줘야 합니다.
보통은 index 파일에서 App component를 감싸서 사용하는데,
저는 recoil이라는 상태관리 라이브러리를 사용중이어서, App 파일에서 전체 component를 감싸서 사용했어요.
현재 react-intl은 nested object에 대한 접근을 지원하지 않아서, document에 제시된 function을 사용해서 object를 펼쳤어요. 그러면 일반 object처럼 사용할 수 있어요. (function은 object를 펼친 다음 모든 상위 key들을 온점으로 구분된 하나의 key로 만들어줘요, 예를 들어 {obj: {nestedObj: 1}} -> {obj.nestedObj: 1})
주의할 점은, IntlProvider의 parameter인 locale은 꼭 ISO 693-1 기준으로 작성해야 합니다. 임의의 변수를 넣으면 react-intl이 인식하지 못해요. 코드는 https://iso639-3.sil.org/code_tables/639/data를 참고하세요.
(대표 코드 [영어: en, 스페인어: es, 일본어: ja, 한국어: ko, 중국어(간체): zh])
// src/App.tsx
import React from 'react';
import { BrowserRouter, Switch, Route, } from 'react-router-dom';
import { useRecoilState, } from 'recoil';
import atoms from 'src/recoil_related/atoms';
import { IntlProvider } from 'react-intl';
import locales from 'src/locales';
import Error404Screen from 'src/container/common/Error404Screen';
import Error503Screen from 'src/container/common/Error503Screen';
function App() {
const [language,] = useRecoilState(atoms.language); // 전역 language 상태관리 변수
function flattenMessages(nestedMessages: Object, prefix = '') {
return Object.keys(nestedMessages).reduce((messages, key) => {
const msg = messages;
const value = nestedMessages[key];
const prefixedKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'string') {
msg[prefixedKey] = value;
} else {
Object.assign(msg, flattenMessages(value, prefixedKey));
}
return msg;
}, {});
}
return (
<IntlProvider locale={language} messages={flattenMessages(locales[language])}>
<BrowserRouter>
<Switch>
<Route path='/503' component={Error503Screen} />
<Route component={Error404Screen} />
</Switch>
</BrowserRouter>
</IntlProvider>
);
}
export default App;
404 에러 페이지에서 이렇게 적용했어요.
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import Layout from 'src/container/layout/Layout';
const Error404Screen = () => (
<Layout>
<div>
<div>404</div>
<div><FormattedMessage id='common.404.description.0' /></div>
<div><FormattedMessage id='common.404.description.1' /></div>
<div><FormattedMessage id='common.404.description.2' /></div>
<div><FormattedMessage id='common.404.description.3' /></div>
<Link to='/' target='_self'>
<button type='button'>
<FormattedMessage id='common.tomain' />
</button>
</Link>
</div>
</Layout>
);
export default Error404Screen;
그러면 language 변수에 따라 다른 나라의 언어가 출력됩니다!
저는 다국어처리를 이번에 처음 해봤는데, 생각보다 간편하더라고요.
저는 1개국어(한국어) 사용자라 번역을 어떻게 잘 하느냐가 관건일 것 같지만...
아무쪼록 이 글을 보신 분들에게 도움이 되었으면 좋겠습니다.
읽어주셔서 감사합니다!
'👩💻 Dev > 💫 React' 카테고리의 다른 글
React-draggable official demo convert using hooks (0) | 2021.05.04 |
---|
댓글