다국어 라이브러리 비교 | react-i18next, react-intl, Lingui, next-translate

    반응형

    새롭게 리뉴얼 하려는 프로젝트가 다국어로 되어있는 서비스이다. 사실 다국어래봤자 이젠 한국어와 영어만 남아있는데... 아무튼 글로벌 서비스다 이 말이다! (?)

    기존 서비스는 .NET으로 되어있기에 React 기반으로 옮기면서 SSR을 할지, 다국어 라이브러리는 어떤걸 쓸지 고민도 되고 다국어 서비스는 처음이라 그 내용들을 기록 해보려고 한다

     

    서비스 스펙 (?)


    • 영어, 한국어 지원
    • 실시간성 필요 없음
    • 동적 콘텐츠 비율이 높으나 계정 자체에 언어가 기본 설정 되어있기에 동적 콘텐츠가 곧 정적인 콘텐츠라고 볼 수 있음
    • 여러 해외 사람들이 와서 보는 B2C 형태인 서비스가 아닌 B2B이기에 본인이 쓴 글을 본인이 보는거고 관리자같은 경우에는 상대방에 맞춰 영어 또는 한국어로 작성하기에 굳이 상대방에게 서로 작성한 내용에 대해 번역을 보여줄 이유가 없다

     

    다국어 처리 방식 비교 (서비스 비교)


    처음에는 실시간성이 필요할까? 싶어서 알아본 플랫폼 서비스랑 처리 방식 모두 비교해보았다

    비교 실시간 번역 API CMS 기반 다국어 관리 DB 기반 다국어 처리 서버 기반 템플릿 렌딩
    특징 1. 미리 JSON에 번역한 외 내용을 번역하기 좋음
    2. 실시간 사용자 콘텐츠도 번역 가능
    1. 번역 데이터를 CMS를 통해서 관리
    2. 운영자가 직접 번역 콘텐츠 수정 가능
    1. DB 테이블로 관리하는 방식
    2. 유저별로 커스텀하게 다국어 저장 및 관리 가능
    3. 동적인 데이터에 적합
    언어별로 HTML 템블릿을 따로 두고 렌더링하는 형태
    단점 유료임 번역 기능 하나를 쓰기위해 CMS를 쓰는게 오버스펙임
    우린 이미 백엔드가 구축되어있고, 관리자 페이지가 있는데 이 부분만 별도로 또 프레임워크를 쓴다? 잘 모르겠음
    DB도 수정하고 프론트도 수정해야하는 이중 번거로움이 생김 템플릿을 2번 제작해야하는 번거로움
    종류 google cloud translate strapi    

    하지만 실시간성도 필요 없고, DB 기반으로 가는건 진짜... 이 인력에선 오바싸바(?)가 있고, 구우우우우욷이 템플릿으로 갈아끼우는 방식 옛날에 많이 쓰던 방식 같고, 생산성이 좋아보인다고 느껴지진 않았다

     

    굳이 라이브러리가 필요한가?


    다국어 처리 라이브러리를 보면 결국 개발자가 하나하나 수동으로 작업해줘야한다. 그럼 이게 변수 처리하는거랑 뭐가 다르지? 똑같은 수동아닌가? 굳이 라이브러리를 안쓰고 en, ko로 분류해서 어떤 변수 보여줄지 작업하면 되는건데?

    상황 1. 복수형 처리

    • 직접 변수 처리 방식
    const messages = {
    	en: {
    		users: (count) => count === 1 ? `${count} user` : `${count} users`.
    	},
    	ko: {
    		users: (count) => `${count}명의 사용자`,
    	}
    }
    
    const locale = "en";
    const count = 1;
    
    return <p>{messages[locale].users(count)}</p>;
    • Lingui or react-lnti 사용시
    <FormattedMessage
    	id="users"
    	defaultMessage="{count, plural, one {# user} other {# users}}"
    	values={{ count }}
    />

    상황 2. 누군가가 문구 수정을 요청할 경우

    • 수동 방식
      • 개발자가 직접 수정해야함
      • git commit → PR → 리뷰 → 배포
    • 라이브러리 사용시
      • 요청자에게 JSON 파일만 전달
      • 번역만 바꾸고 다시 컴파일 → 배포 자동화
    MVP 수준에서는 라이브러리와 큰 차이를 못 느낄 수는 있겠으나
    규모가 확잘 될 수록 유지보수성이 어려워지거나 번거로운 상황들을 맞이할 확률이 높아짐

     

    CSR vs SSR


    다국어 서비스에서 CSR과 SSR의 차이는 어떨까?

    항목 CSR SSR
    초기 언어 감지 브라우저에서 JS가 실행된 후 언어 감지됨 서버에서 쿠키, 헤더 등을 이용해 언어 감지
    초기 로딩 속도 느림 빠름
    다국어 처리 클라이언트에서 쉽게 처리 가능 서버측에서 가능
    언어 처리 프로세스 1. 사용자가 최초 접속
    2. 초기 HTML에는 빈 내용
    3. JS가 실행되고 다국어 처리 라이브러리 초기화
    4. 브라우저 언어 감지 또는 로컬 스토리지 확인 후 언어 설정
    5. 클라이언트에서 번역된 UI 표시
    1. 사용자 언어를 감지해서 특정 라우터로 접속
    2. 서버에서 다국어 처리된 HTML 생성
    3. 완성된 HTML 반환
    4. JS 상호 작용 가능
    단점 UI 랜더링 과정에서 화면 깜빡임 발생  
    • 초기 로딩시 SSR로 하고, 이후 동적 로딩은 CSR로 처리하자!
    • Next.js는 하이브리드 프레임워크이기 때문에 CSR, SSR 둘 다 처리가 가능하다

     

    다국어 처리 라이브러리


     

    종류 react-i18next react-intl Lingui next-translate
    특징 1. react, next,js에 최적화된 라이브러리
    2. SSR + CSR 완벽 지원
    3. 변수 바인딩, 플루럴, 날짜 포맷 등 JS와 결합한 형태로 동적 사용 가능
    4. next.js 공식 문서에도 언급된 라이브러리 (next-i18next 라이브러리 별도로 있음)
    1. ICU 매시지 포맷 지원 (문장의 구조, 성별, 숫자 등 유연하게 처리)
    2. 컴포넌트 기반 메시지 처리
    3. 다양한 날짜, 숫자, 통화 등 포맷팅 지원
    4. 형식이 복잡한 언어 번역 처리

    1. 미니멀한 라이브러리
    2. 빌드 최적화 강점
    3. 코드 기반 메시지 추출 자동화 지원
    4. JSX에서 직관적인 방식으로 메시지 작성 가능
    5. 개발자 중심의 번역 관리
    1. 파일 기반 라우팅에 맞춘 다국어 처리
    2. JSON이 아닌 JS 파일 기반 번역 사용
    3. Next.js에서 빠르게 적용 가능
    3. Next.js의 동적 라우팅 구조를 적극 활용할때 좋음 (템플릿 기반)
    단점 러닝커브가 상대적으로 있는 편 1. Next.js 연동은 별도의 설정 필요
    2. SSR 지원도 별도의 번거로운 설정이 필요함
    Next.js 통합은 별도의 수동 설정이 필요 1. react-i18next 라이브러리와 유사하면서도 기능은 제한적
    2. 복잡한 복수형 처리는 불편

    Hello World를 출력한다고 했을때

    react-i18next

    // /public/locales/en/common.json
    {
        "hello_world": "Hello, world!"
    }
    
    // /public/locales/ko/common.json
    {
        "hello_world": "안녕하세요!"	
    }
    import { useTranslation } from "react-i18next";
    
    export default function Main() {
    	const { t } = useTranslation('common');
    	return <h1>{t("hello_world)}</h1>;
    }
    • react-i18next는 public/locales 폴더 안에 있는 언어별 JSON 파일들을 자동으로 인식함 (config 파일 설정 필요)
    • 현재 언어에 따라 자동으로 해당 언어 번역 파일을 불러와 렌더링해줌 (config 파일 설정 필요)
    • URL 또는 브라우저 정보로 설정 내용과 매칭하는 형태
      • 언어가 추가될때마다 설정 파일도 같이 업데이트 필요
      • 언어가 추가될때마다 json 파일도 같이 추가 필요

    react-intl

    // en.ts
    export default {
    	hello_world: "Hello, world!"
    }
    
    // ko.ts
    export default {
    	hello_world: "안녕하세요!"
    }
    import { FormattedMeassage } form "react-intl";
    import { useIntl } from "react-intl";
    
    function Home() {
    	return (
        	<h1><FormattedMessage id="hello_world" /></h1>
        );
    }
    
    function Sub() {
    	const intl = useIntl();
    	const greeting = intl.formatMessage({id: "hello_world"});
    	
    	return <h1>{greeting}</h1>;
    }
    • 상위 컴포넌트에서 언어 설정 필요

    Lingui

    // 방법 1
    import { t } from "@lingui/macro";
    
    function Home() {
    	return <h1>{t`Hello, world!`}</h1>
    }
    
    // 방법 2
    import { Trans } from "@lingui/macro";
    
    function Home() {
    	return <h1><Trans>Hello, world!</Trans></h1>
    }
    • 작성된 내용으로 추출 과정 진행
    # en/messages.po
    msgid "Hello, world!"
    msgstr ""
    
    # ko/messages.po
    msgid "Hello, world!"
    msgstr ""
    • 바벨 매크로로 추출된 내용을 기반으로 생성된 po 파일에서 번역 내용 수동 채워넣기 작업 진행
    • 컴파일 진행

    next-translate

    // locales/en/common.json
    {
    	"hello_world": "Hello, world!"
    }
    
    // locales/ko/common.json
    {
    	"hello_world": "안녕하세요!"	
    }
    import useTranslation from 'next-translate/useTranslation';
    
    export default function Home() {
      const { t } = useTranslation('common');
      return <h1>{t('hello_world')}</h1>;
    }

     

    npm 다운로드 수


     

     

    결론


    Next.js 결정

    • Next.js를 이용해서 라우팅도 유연하게 동적 처리
    • 초기 랜더링 SSR 처리해서 빠르게 응답

    Lingui 결정

    • 텍스트를 입력하고 추출을 하면 알아서 po 파일을 생성해줘서 간편
    • 텍스트를 기준으로 알아서 key를 생성해주고 관리해줘서 간편
      • 만약 기존에 있던 key가 사라지면 po 파일에서는 주석을 달아주고 ⇒ 삭제는 아님
      • 컴파일 결과물에는 포함되지 않음
    • 스타수, 다운로드 수는 낮지만 꾸준히 릴리즈 업데이트를 해주고 있음
    반응형

    댓글