React, Next, Redux/⚛ React.JS

react-intl 적용하여 번역을 지원하는 Internationalization 하기

DarrenKwonDev 2020. 6. 10. 22:25

https://www.npmjs.com/package/react-intl

 

react-intl

Internationalize React apps. This library provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

www.npmjs.com

https://formatjs.io/docs/react-intl

 

Overview | Format.JS

[![npm Version](https://img.shields.io/npm/v/react-intl.svg?style=flat-square)](https://www.npmjs.org/package/react-intl)

formatjs.io

 

이번 랜딩페이지에 다국어를 지원해야 하는 일이 생겼다. (영어만 있다고 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

https://gracefullight.dev/2018/01/15/react-intl%EB%A1%9C-%EB%B2%88%EC%97%AD-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-react-i18n/