목차
  1. 언어 선정
    1. Translator
    2. Text Direction
    3. Material & Cupertino
    4. Package
    5. 최종 선정 언어
  2. 번역 준비
    1. Flutter 번역 준비
    2. iOS 번역 준비
      1. String Catalog 방식
      2. Strings File 방식
    3. Store 번역 준비
  3. 번역 시 만나는 문제
    1. 스크린샷 캡처
    2. 텍스트 줄 바꿈
    3. 번역 예외
    4. 번역 검사
    5. 생산성 저하
  4. 마무리

Flutter 프로젝트에 72개 언어를 추가한 이야기를 공유합니다.
👉 English Version

✔︎ 1부. Flutter 다국어 지원 - 준비
2부. Flutter 다국어 지원 - 도구 만들기


필자는 Flutter를 이용하여 VoCat - 나만의 단어장 앱 서비스를 운영하고 있습니다. 한국어와 영어를 지원하고 있는데, 간혹 외국 유저들로부터 다른 언어를 지원해달라는 요청이 들어옵니다.

별이 5개 ☺️별이 5개 ☺️

때마침 사이드 프로젝트로 만든 오늘 하루를 그려줘 - AI 그림 일기 팀에서도 더 많은 언어를 지원하자는 이야기가 나와 다국어 지원을 시작하게 되었습니다.

이땐 3개월이나 걸릴 줄 몰랐지..🫠이땐 3개월이나 걸릴 줄 몰랐지..🫠

언어 선정

먼저 지원할 언어 목록을 선정해 봅시다.

Translator

번역기를 이용하면 적은 비용으로 많은 언어를 손쉽게 지원할 수 있으며, 이번 프로젝트에선 133개 언어를 지원하는 Google 번역 API를 사용하도록 하겠습니다.

Google 번역 DeepL Papago
지원 언어 133개 34개 16개

Text Direction

언어는 읽는 방향에 따라 LTRRTL로 나눌 수 있습니다.

  • LTR(Left To Right) : 한국어 같이 왼쪽에서 오른쪽으로 읽는 언어
  • RTL(Right To Left) : 아랍어 같이 오른쪽에서 왼쪽으로 읽는 언어

Flutter에선 Intl 패키지의 isRtlLanguage 메서드를 이용하면 특정 언어가 RTL인지 확인할 수 있으며, Google 번역에서 지원하는 언어 기준으로 RTL 언어는 10개(아랍어, 디베히어, 히브리어, 파슈토어, 페르시아어, 신디어, 우르두어, 위구르어, 이디시어, 소라니어) 입니다.

<b>RTL</b> 언어인 <b>Kurdish(Sonari)</b> 언어는 잘못된 값이 반환되는 것 같아 <a href=https://github.com/dart-lang/i18n/issues/827>제보</a>해 두었습니다.RTL 언어인 Kurdish(Sonari) 언어는 잘못된 값이 반환되는 것 같아 제보해 두었습니다.

RTL 언어를 지원하고 싶은 경우, 단순히 텍스트 방향만 바꾸면 되는 게 아니라 전반적인 UI 자체를 좌우 반전 시켜야 합니다. RTL 언어 LocaleMaterialApp에 전달하면 자동으로 좌우가 반전되지만, 아이콘의 방향을 수정하고 LTR 기준으로 설계된 제약 조건들을 모두 고치는 등 많은 노력이 필요합니다.

<b>LTR</b> & <b>RTL</b> 인터페이스 - <a href=https://m2.material.io/design/usability/bidirectionality.html#mirroring-layout>material.io</a>LTR & RTL 인터페이스 - material.io

Material & Cupertino

MaterialCupertino 위젯에 내장된 텍스트들은 Flutter Localizations 패키지를 통해 현지화를 지원할 수 있습니다.

Material3 DatePicker에 내장된 텍스트<br><a href=https://m2.material.io/design/usability/bidirectionality.html#mirroring-layout>material.io</a>Material3 DatePicker에 내장된 텍스트
material.io

Flutter Localizations은 Material 79개, Cupertino 78개 언어를 지원하며, 프로젝트에서 내장 테마 위젯을 사용하고 있다면 지원 언어 선정시 고려할 필요가 있습니다.

만약 패키지에서 지원하지 않는 언어를 추가하고 싶은 경우, MaterialLocalizationsCupertinoLocalizations 클래스를 상속받아 구현한 뒤 MaterialApplocalizationsDelegates 속성으로 전달하시면 됩니다.

Package

다국어 지원 패키지 또는 텍스트가 내장된 패키지를 사용하는 경우 다국어 지원시 검토가 필요합니다.

이번 프로젝트에선 날짜 및 복수형 등 현지화 작업을 도와주는 Intl 패키지를 사용하고 있습니다. Intl 패키지는 119개 언어를 지원하며, 미지원 언어를 추가하는 방법은 제공되지 않습니다.

최종 선정 언어

위 고려 사항들을 정리하면 다음과 같습니다.

Google 번역 LTR & RTL Material & Cupertino Package
133 LTR(123)
RTL(10)
Material(79)
Cupertino(78)
Intl(119)

최소한의 노력으로 최대한 많은 언어를 지원하기 위해, RTL 언어를 배제하고 각 지원 언어의 교집합을 구하여 다음 72개 언어를 선정하였습니다.

갈리시아어 구자라트어 그리스어 네덜란드어
네팔어 노르웨이어 덴마크어 독일어
라오어 라트비아어 러시아어 루마니아어
리투아니아어 마라티어 마케도니아어 말라얄람어
말레이어 몽골어 미얀마어 바스크어
베트남어 벨라루스어 벵골어 보스니아어
불가리아어 세르비아어 스리랑카어 스와힐리어
스웨덴어 스페인어 슬로바키아어 슬로베니아어
아르메니아어 아삼어 아이슬란드어 아제르바이잔어
아프리칸스어 알바니아어 암하라어 에스토니아어
영어 오리야어 우즈베크 우크라이나어
웨일즈어 이탈리아어 인도네시아어 일본어
조지아어 줄루어 중국어(간체) 중국어(번체)
체코어 카자흐어 카탈루냐어 칸나다어
크로아티아어 크메르어 키르기스어 타갈로그어
타밀어 태국어 텔루구어 튀르키예어
펀자브어 포르투갈어 폴란드어 프랑스어
핀란드어 한국어 헝가리어 힌디어

72개 언어를 선정하였으니 본격적으로 다국어를 지원해 봅시다.


번역 준비

Flutter 앱 서비스 기준으로 번역이 필요한 범위는 다음과 같습니다.

  1. 앱 내 번역
    • Flutter 텍스트
    • iOS 권한 텍스트
  2. Play Store & App Store 번역
    • Metadata
    • Changelog
    • Screenshot

번역 작업을 진행하기에 앞서 다국어 개발 환경부터 설정해 봅시다.

Flutter 번역 준비

Flutter 텍스트Flutter 공식 문서를 참고하거나 Flutter Intl 확장 프로그램(VSCode / Android Studio)을 사용하여 다국어 개발 환경을 구축할 수 있습니다.

Flutter 다국어 개발 환경을 구축하면 완료하면 각 언어별 텍스트를 ARB(Application Resource Bundle) 파일 형태 관리하게 됩니다.

보다 상세한 다국어 개발 환경 구축 방법은 <a href=https://inf.run/HFSvE>Flutter 앱 개발 실전 강의</a>를 참고해 주세요.보다 상세한 다국어 개발 환경 구축 방법은 Flutter 앱 개발 실전 강의를 참고해 주세요.

iOS 번역 준비

String Catalog 방식

공식 문서에 따르면 XCode 15부터 String Catalog 파일로 InfoPlist.strings 파일의 권한 메세지를 번역할 수 있습니다.

Xcode를 실행하고 RunnerInfoLocalizations에서 언어를 추가한 뒤, Runner 폴더 밑에 String CatalogInfoPlist.xcstrings라는 이름으로 추가해 주세요.

InfoPlist의 key를 입력하고 각 언어로 번역하면 됩니다.InfoPlist의 key를 입력하고 각 언어로 번역하면 됩니다.

Strings File 방식

XCode 15 이전 버전에선 다음 방법으로 권한 메세지를 번역할 수 있었습니다.

Xcode를 실행하고 RunnerInfoLocalizations에서 언어를 추가한 뒤, Runner 폴더 밑에 Strings FileInfoPlist.strings라는 이름으로 추가해 주세요.

+ 버튼을 누르고 선택하시면 됩니다.+ 버튼을 누르고 선택하시면 됩니다.

InfoPlist.strings 파일을 열고 우측 Localization 섹션에서 번역을 지원할 언어를 선택해 주세요. 그러면 ios/Runner/언어.lproj 폴더 밑에 InfoPlist.strings 파일이 생성되며 해당 파일에 각 언어별 iOS 권한 요청 문구를 작성하시면 됩니다.

Store 번역 준비

Play StoreApp Store에서 번역이 필요한 항목은 다음과 같습니다. (Tablet과 iPad 스크린샷은 미지원시 생략할 수 있습니다.)

Play Store App Store
지원 언어 87개 39개
Metadata title
short_description
full_description
name
subtitle
keywords
description
promotional_text
Changelogs build_version.txt release_notes.txt
Screenshots Grapic (1024x500)
Mobile (1242x2208)
Tablet 7-inch (2048x2732)
Tablet 10-inch (2048x2732)
iPhone 5.5 (1242x2208)
iPhone 6.5 (1284x2778)
iPad 12.9 2Gen (2048x2732)
iPad 12.9 3Gen (2048x2732)

이번 프로젝트에선 Play Store는 Google 번역에서 지원하지 않는 로만시어(Romansh)와 RTL 언어 8개를 제외한 79개 언어를, App Store는 RTL 언어 2개를 제외한 37개 언어를 지원하였습니다.

또한 스토어 등록 정보를 손쉽게 업로드할 수 있도록 Fastlaneupload_to_play_storeupload_to_app_store 명령어를 이용한 배포 자동화 환경을 구축하였습니다.


번역 시 만나는 문제

번역 작업을 진행하다 보면 많은 문제들을 만나게 됩니다.

스크린샷 캡처

VoCat 서비스는 기기당 8장의 이미지를 사용중이며 테블릿을 지원하기 때문에 총 3,080장(Play Store 1,896 + App Store 1,184)의 이미지가 필요합니다.

fastlane에는 자동으로 시뮬레이터에서 스크린샷을 촬영하는 기능과 캡처한 스크린샷을 디바이스 프레임에 넣고 텍스트를 넣는 frameit이라는 기능이 있습니다.

fastlane auto screenshot - <a href=https://docs.fastlane.tools/getting-started/ios/screenshots/#capture-screenshots-automatically>fastlane</a>fastlane auto screenshot - fastlane
.
fastlane device frame - <a href=https://docs.fastlane.tools/getting-started/ios/screenshots/#capture-screenshots-automatically>fastlane</a>fastlane device frame - fastlane

하지만 위 방법은 이미지 1장을 촬영하는데 3초 걸린다고 가정했을 때 스크린샷 촬영에만 2시간 30분이 소요되며, 앱에 존재하지 않는 화면은 생성할 수 없는 문제가 있습니다.

텍스트 줄 바꿈

기본적인 텍스트는 한 줄이 가득 차면 줄 바꿈하는 방식으로 이루어 집니다.

기본 텍스트 줄 바꿈 - <a href=https://clagnut.com/blog/2424>clagnut.com</a>기본 텍스트 줄 바꿈 - clagnut.com

반면 균형 잡힌 텍스트 줄 바꿈은 보다 안정적이고 가독성도 높아지는 효과가 있기 때문에 의도적으로 특정 단어 뒤에 줄 바꿈 기호를 넣거나 특정 라인수를 유지하도록 디자인하는 경우가 있습니다.

균형 잡힌 텍스트 줄 바꿈 - <a href=https://clagnut.com/blog/2424>clagnut.com</a>균형 잡힌 텍스트 줄 바꿈 - clagnut.com

문제는 번역시 텍스트의 길이가 달라지므로, 줄 바꿈 기호(\n)의 위치가 올바르지 않게 됩니다.

번역 예외

ARB 파일은 중괄호를 이용하여 {이름} 형태로 매개변수를 전달받아 보여주는 기능이 있습니다. 중괄호 안쪽 매개변수의 이름은 72개 언어에서 모두 동일해야 정상적으로 작동하는데, 번역시 매개변수 이름까지 번역되는 문제가 있습니다.

1
2
"corpusAdded": "{word} 단어 추가 완료!",
"vocatFileImport": "VOCAT 파일 불러오기",

매개변수 이름 외에도 서비스 명칭이나 이메일 주소번역 예외 처리가 필요한 항목들이 있습니다.

번역 검사

번역 품질 외에도 검사가 필요한 항목들이 있습니다.

  • 매개변수 명칭 : ARB 파일에서 매개변수 명칭이 동일한지 여부
  • 매개변수 개수 : ARB 파일에서 매개변수 개수가 동일한지 여부
  • 번역 예외 : 번역 예외가 올바르게 되었는지 여부
  • 괄호 개수 : 괄호 개수가 동일한지 여부
  • Metadata 길이 : 스토어 메타데이터 최대 글자 수
  • Changelogs 길이 : 버전 별 변경사항 최대 글자 수

매개변수 명칭 또는 개수가 동일하지 않은 경우 컴파일시 문제가 발생하고, Metadata나 Changlog에서 최대 글자 수를 넘긴 경우 스토어 제출이 불가능하므로 반드시 검사가 필요합니다.

생산성 저하

VoCat 앱 내 1,200개의 문구가 있는데 72개 언어를 지원하면 86,400 문장이 됩니다. 이 많은 문장을 직접 검사하려면 너무 많은 시간이 필요하며 또한 문구 추가, 수정 및 삭제 작업은 항상 72번 반복 진행해야 하므로 생산성이 급격히 하락하게 됩니다.

문제가 왜 이리 많냐고요? 저도 알고 싶지 않았어요..🫠문제가 왜 이리 많냐고요? 저도 알고 싶지 않았어요..🫠

마무리

Flutter 다국어 지원에 필요한 언어를 선정하고, 기본적인 개발 준비를 완료하였습니다. 다음 2부에선 번역시 만나는 문제들을 해결하기 위해 제작한 도구들을 소개하겠습니다 :)

✔︎ 1부. Flutter 다국어 지원 - 준비
2부. Flutter 다국어 지원 - 도구 만들기