본문 바로가기
프로그래밍 개발/IT 서비스 개발 운영

IT 서비스 개발 운영 - 글로벌 서비스 다국어 지원 기능 구현하기 (feat. i18next)

by Jinseok Kim 2023. 8. 9.
반응형

글로벌 서비스 다국어 지원

 

 

1.  서비스 다국어 지원 도입 배경

 

MyVenus - Aplikasi Perawatan Kecantikan Terlengkap & Terpecaya #1 di Indonesia

Bergabunglah dengan komunitas kami untuk menemukan solusi dari berbagai masalah seputar dunia kecantikan, melihat real review hingga mencari tahu segala hal yang berhubungan dengan treatment kecantikan maupun operasi plastik!

myvenus.io

  • 현재 제가 서비스 운영 중인 뷰티 플랫폼 MyVenus는 인도네시아 중심으로 주변 동남아 국가까지 사업 확장을 고려하고 있어 처음 서비스 런칭 때부터 다국어 지원을 하게 되었습니다.
  • 보통 국내의 앱 서비스들은 한국어 하나로 서비스를 운영하는 것이 일반적이지만 MyVenus는 다국어 지원이 필수적으로 적용되는 것이 특징이었습니다. 현재는 인도네시아어, 영어, 한국어로 운영 중이며 베트남어도 추가 예정입니다.
  • App, CMS Web에도 모두 적용중이며 글로벌한 서비스 운영에 필수적인 지원 기능이 되었습니다.

 

 

 

2.  서비스 다국어 지원을 위한 라이브러리 ' i18 next'

 

Introduction - i18next documentation

The framework was built with scalability in mind. For smaller projects, having a single file with all the translation might work, but for larger projects this approach quickly breaks down. i18next gives you the option to separate translations into multiple

www.i18next.com

다국어 지원 기능을 위하여 저희 서비스는 i18next 라이브러리를 사용합니다. 선정 이유는 단순합니다. 글로벌 서비스 다국어 기능에 i18next 기능이 가장 적합하였고 많은 서비스들에서 많이 쓰기 때문입니다.



프론트 기준 ReactJs, NextJs, React Native 등등 프레임워크에 관계없이 웬만한 프로젝트에는 npm을 사용하면 쉽게 인스톨 및 호환 적용할 수 있습니다.

 

1.  라이브러리를 인스톨합니다.

npm install react-i18next i18next --save

 

2. 디렉터리 파일 추가

저희 프로젝트는 이렇게 json 언어파일 따로 index 파일 따로 구별해 두었습니다.

 

서버 DB에서 언어 데이터를 가져올 수도 있지만 서버에서 가져오는 시간 텀이 존재하여 프론트단의 사용성이 떨어질 수도 있다는 판단을 하게 되어 로컬에서 직접 하드코딩하여 프로젝트에 빌드하기로 하였습니다.

 

 

id.json

{
  "_comment": "src/constants/i18n/indo/common.json",
  "app_name": "My Venus",
  "switchLanguage": "Indonesia",
  "tab.homeTabText": "Home",
  "tab.wishTabText": "Suka",
  "tab.mySurgeryTabText": "Reservasi Saya",
  "tab.myPageTabText": "Saya",
  "tutorial.LoginBtn": "Login/Daftar",
  "tutorial.SeeBtn": "Lihat Lebih",
  "tutorial.lngSettingTitle": "Pengaturan Bahasa",
  "tutorial.lngSettingConfirmBtn": "Selesai",
  "tutorial.indo": "Bahasa Indonesia",
  "tutorial.en": "Bahasa Inggris",
  "tutorial.vietnam": "Bahasa Vietnam",
  "tutorial.kr": "Bahasa Korea",
  
  .....
  
  }

 

index.ts

import i18n from 'i18next';
export {i18n};

import ChainedBackend from 'i18next-chained-backend'; // i18next-chained-backend should be placed here
import LocalStorageBackend from 'i18next-localstorage-backend'; // primary use cache
import translationEN from '../../i18n/en.json';
import translationKR from '../../i18n/kr.json';
import translationINDO from '../../i18n/id.json';
import translationVIETNAM from '../../i18n/vn.json';

export const resources = {
  en: {
    translation: translationEN,
  },
  kr: {
    translation: translationKR,
  },
  in: {
    translation: translationINDO,
  },
  vn: {
    translation: translationVIETNAM,
  },
};

i18n.use(ChainedBackend).init({
  lng: 'in',
  fallbackLng: 'in',
  compatibilityJSON: 'v3',
  interpolation: {
    escapeValue: false,
  },
  resources,
  backend: {
    backends: [
      LocalStorageBackend, // primary
    ],
    backendOptions: [
      {
        expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days
      },
    ],
  },
});

 

위와 같이 프로젝트에 세팅이 완료되면 바로 기능을 사용할 수 있습니다.

 

 

 

 

저는 유틸 함수로 따로 선언 및 명시하여 프로젝트 내 다국어를 지원해야 하는 모든 텍스트를 바인딩합니다.

import {t} from 'i18next';

export const Lang = t;

 

 

 

이렇게  id.json의 객체 데이터에서 각각 원하는 Key값으로 바인딩해 주면 다국어 지원 기능 끝입니다.

<span>(Lang('home.brand.advNotiBtnText')}</span>

객체 형식이므로 더 좋은 모듈화, 더 좋은 가독성을 위하여 많은 방법으로 적용할 수 있습니다.

 

 

 

이제 사용자가 언어 번경을 할 수 있는 기능을 제공하려면 아래와 같이 changeLanguage 메서드의 인자로 index.ts에서 선언한 언어별 resources key값을 넣어 호출하면 프로젝트 전체적으로 i18next로 바인딩한 언어들이 변경되는 다국어 지원 기능을 제공하게 됩니다.

 

import {changeLanguage} from 'i18next';

const onChangeIdnosiaLanguage = () => {
changeLanguage('id');
}

<button onClick={onChangeIdnosiaLanguage}>Indonesia</button>

 

 

 

 

 

 

3. 더 효율적인 다국어 데이터 배포/업데이트를 위한 자동화

  • 서비스 런칭 초반에는 추가 신규 개발로 인하여 언어 텍스트가 추가되면 일일이 프로젝트 로컬에서 데이터를 입력해 주었는데 점점 서비스의 덩치가 커지고 일이 많아져서 자동화가 필요한 시점이었습니다.
  • 그리고 인도네시아, 영어 번역을 일일이 적어서 요청하는 것도 막노동 수준으로 힘든 반복 작업이었습니다.

 

 

다국어 언어 및 번역 추가 자동화 프로세스는 App, CMS 와의 조합으로 운영팀(번역팀)과 개발팀 서로 편한 협업을 도출해 내도록 기획하고 구축하였습니다.

 

 

번역 및 프로젝트 다국어 텍스트 데이터 업데이트 프로세스

  1. 개발팀 -> CMS 페이지 -> [번역] -> [번역 요청]
  2. Slack 번역 요청 메시지 발송 -> 운영팀 -> 번역하러 가기 버튼 제공 (CMS 번역탭 링크)
  3. 운영팀 -> CMS -> [번역]  -> 번역본 작성 후 저장 버튼 -> Airtable DB 테이블에 저장
  4. Slack 번역 완료 메세지 발송
  5. 개발팀 -> i18n 업데이트 스크립트 명령어 실행
  6. Airtable DB에서 id.json ,en.json 파일 업데이트

 

 

APP, CMS에 들어가는 번역시트 DB는 Airtable을 사용하였습니다.

 

The platform to build next‒gen apps | Airtable

Airtable is a low‒code platform to build next‒gen apps. Move beyond rigid tools, operationalize your critical data, and reimagine workflows with AI.

www.airtable.com

CMS에서 번역을 요청하면 이렇게 DB 테이블에 저장됩니다. 웹에서 확인 가능하고 엑셀처럼 바로바로 수정 가능하니 유용합니다.

 

 

 

운영팀에서 번역이 완료되었다는 소식을 알게 되면 바로 로컬 개발 프로젝트 내의 스크립트에 지정해 둔 명령어를 실행하여 Airtable에서 최신 DB 데이터를 가져온 데이터를 가공 및 파싱 하여  i18next의 언어 텍스트 파일(id.json... 등등)의 객체 데이터에 업데이트되어 최신 적용하도록 합니다.

 

 

package.json

  "scripts": {
  ...
    "i18next": "node ./myvenus-i18next.js",
  },

 

 

myvenus-i18n.js

const fs = require('fs');
const Airtable = require('airtable');
const PATH = './src/i18n/';

const base = new Airtable({apiKey:{key}}).base(
  {ApiKey},
);

const language = [];
const data = {};

const saveToJson = payload => {
  language.map(lang => {
    fs.writeFile(
      `${PATH}${lang}.json`,
      JSON.stringify(payload[lang], null, 2),
      'utf-8',
      () => {
        console.log('finished writing');
      },
    );
  });
};

const getData = async () => {
  try {
    const response = await base('language')
      .select({
        maxRecords: 1200,
        view: 'all_list',
      })
      .all();

    if (response.length > 0) {
      response.map(({fields}) => {
        if (fields) {
          const {code, complete, ...rest} = fields;
          const lang = Object.keys(rest);
          if (language.length < lang.length) {
            language.push(...lang);
          }

          if (complete) {
            language.map(c => {
              if (data[c]) {
                data[c][code] = fields[c]?.trim();
              } else {
                data[c] = {
                  [code]: fields[c]?.trim(),
                };
              }
            });
          }
        }
      });

      saveToJson(data);
    }
  } catch (e) {
    console.error(e);
  }
};

getData();

 

 

 

이렇게 자동화 시스템까지 구축하고 나서 다국어 언어 추가/번역의 반복 작업이 매우 간단한 작업이 되었습니다.

 

글로벌 서비스를 운영 중인 개발자분들에게 도움이 많이 되었으면 좋겠습니다.

 

감사합니다.

 

 

 

 

 

반응형

댓글