react-intl 적용하여 번역을 지원하는 Internationalization 하기
https://www.npmjs.com/package/react-intl
https://formatjs.io/docs/react-intl
이번 랜딩페이지에 다국어를 지원해야 하는 일이 생겼다. (영어만 있다고 VC에게 털렸다)
수작업으로 번역을 해서 각 언어마다 다른 컴포넌트를 렌더링하게 만들어야 하는 방법을 먼저 생각해봤는데 아무리 봐도 어리석은 짓 같다.
아니면 컴포넌트에 props를 줘서 특정 props이 en/ko/ja 등등 일 때 다른 값을 렌더링하게 만드는 방법도 있긴 하다. Redux로 상태 관리를 해서 한꺼번에 변화시키는 방법도 있겠다.
그러나, 기왕 패키지를 찾은 김에 한 번 사용해보고 싶어서 패키지를 사용해보려고 한다.
npm 다운로드 수를 보아하니 react-intl이 가장 수가 높으므로 해당 패키지를 이용하기로 했다.
react-intl은 번역 뿐만 아니라 지역대 별로 시간이나 화폐 포맷 등도 제어하여 Internationalization에 적합한 패키지라고 한다. 여기서는 간단히 번역 기능만 알아보겠다.
npm i react-intl
locale 폴더 하에 지원하고 싶은 언어 별로 js을 만듭시다.
// en.js
export default {
"At popkorn, we make language fun and addictive.":
"At popkorn, we make language fun and addictive."
}
// ko.js
export default {
"At popkorn, we make language fun and addictive.":
"팝콘에서는 언어를 쉽고 중독적인 방법으로 배울 수 있습니다.",
};
다음과 같이 App을 Wrap 해줍시다. Redux에서 씌웠던 hoc 같은 개념입니다.
* addLocaleData는 사용하면 오류가 납니다. 업데이트 하면서 사라졌습니다. (v3 이후 부터 발생한다고 합니다)
그런데도 많은 튜토리얼에서는 아직도 addLocaleData를 사용하고 있습니다...
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import App from "./App";
import { IntlProvider } from "react-intl";
# 언어 관련 설정을 localStorage에 저장하기로 했습니다.
# 이후 특정 버튼을 누르면 localStroage를 바꾸면 되겠죠?
const defaultLang = localStorage.getItem("lang") || "en";
// 번역된 결과물들
import en from "./locale/en";
import ko from "./locale/ko";
let message;
if (defaultLang === "ko") {
message = ko
} else {
message = en
}
// IntlProvider의 locale에는 번역하고자 하는 언어, message에는 번역된 결과물을 삽입합니다.
ReactDOM.render(
<IntlProvider locale={defaultLang} messages={message}>
<App />
</IntlProvider>,
document.getElementById("root")
);
이제 값에 따라 번역하고 싶은 부분에 <Formatted*> 컴포넌트를 적용하면 됩니다. react-intl에서 사용되는 컴포넌트는 대개 Formatted로 시작합니다.(물론 injectIntl과 같은 경우도 있습니다.)
컴포넌트의 경우 위의 공식 문서를 살펴보는 것이 빠를 듯합니다.
가장 자주 사용하는 예를 하나 들어보자면 FormattedMessage는 id에 준 값에 해당하는 값으로 번역합니다.
// <IntlProvider locale={"ko"} messages={ko}>로 변경
<FormattedMessage id="At popkorn, we make language fun and addictive." />
전자에서 후자로 바뀌었습니다. 부족한 번역 실력.. ㅋㅋ
이제 버튼을 클릭하거나, 다른 url로 접근하는 등 다양한 방법으로 locale과 messages에 변화를 주면 됩니다. 끝~!
저 같은 경우에는 버튼을 클릭할 대마다 localStorage를 바꿔주고 reload를 강제했습니다.
<Button
type="link"
style={langButtonStyle}
onClick={() => {
localStorage.setItem("lang", "en");
window.location.reload();
}}
>
injectIntl
간혹 IntlProvider를 ancestor로 하지 않았다고 (했음에도 불구하고) 오류가 뜨는 경우가 있습니다. antd와 같은 CSS 디자인 프레임워크나 그 밖에 다른 것에 붙여 쓸 때 일어나는 일입니다.
혹 순수한 HTML을 사용하더라도 그 내부에 placeholder와 같은 속성에는 곧바로 <Formatted*>를 사용할 수 없습니다.
이 문제는 injectIntl를 통해서 쉽게 해결할 수 있습니다.
withRouter 처럼 컴포넌트를 감싸서 활용해야 합니다.
다음과 같이 사용할 수 있습니다!
import { injectIntl } from 'react-intl';
// 'intl' will be available among the component's props, after the injection
function HelloWorld({ intl, ...props }) {
return <>
<Input
type="email"
name="email"
placeholder={intl.formatMessage({ id: "Get our NewsLetter!" })}
value={Email}
onChange={handleChange}
></Input>
</>
return <textarea placeholder={placeholder}></textarea>;
}
export default injectIntl(HelloWorld);
props를 찍어보면 intl은 다음과 같습니다. 적절하게 원하는 내용을 찾아 활용하시면 됩니다.
참고
https://code-masterjung.tistory.com/58
https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-react-app-with-react-intl