플러터 다국어 지원 - Flutter Internalization
intl 패키지와 툴 써서 번역하기
- arb란?
- intl 이란?
- intl - message, format, number
- intl 툴로 arb 추출하기
- app에 다국어 적용하기
플러터 다국어 지원은 좀 까다롭습니다. Intl.message를 .arb 파일로 바꾼 뒤 읽어들여야 하죠.
.arb 파일이란?
arb(Application Resource Bundle)는 구글에서 만든, 파일 형식입니다.
언어 데이터를 저장했다가 번역하기 위해 쓰이죠. 단순한 형태의 json 파일이라고 보면 됩니다.
플러터에선 arb 말곤 다국어지원을 안해서, 어쨌든 arb 파일 다루는 법을 알아야합니다.
Google Translators Toolkit도 .arb 파일형식을 지원하고 있습니다.
intl 란?
intl은 다트에서 다국어 (internalization and localization)을 지원하는 패키지입니다. 언어 번역이나 단수/복수, 성별, 날짜/숫자를 그 지역이나 국가에 맞게 바꿀 때 씁니다.
1. Intl.message
일단 패키지들을 설치하고 설명을 따라해보세요.
1.1 pubspec.yaml 설정
intl 패캐지와 intl_translation 패키지를 추가해주세요.
xdependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.15.8
dev_dependencies:
flutter_test:
sdk: flutter
intl_translation: ^0.17.1
1.2 Intl.message() 사용해보기
Messages는 번역할 문자들을 말합니다. 이 문자들을 필요할 때, 한번에 추출해 번역합니다.
Intl.message()은 문자를 담은 함수로 볼 수 있습니다.
이 함수를 한번 사용해볼께요.
xxxxxxxxxx
import 'package:intl/intl.dart';
String helloWorld() => Intl.message("Hello World",
name: "helloWorld", // 함수랑 동일
args: [],
desc: "Hello world message");
String openButton() => Intl.message("open button",
name: 'openButton',
args: [],
desc: "This is an open button"
);
void main() {
print(helloWorld());
print(openButton());
}
일반 문자랑 큰 차이없이 쓰이죠? 다만 Intl.message의 name은 꼭 함수이름이랑 일치해야지 나중에 .arb 파일로 추출할 수 있습니다.
1.3 Flutter에서 Intl.message 사용해보기
위젯 코드는 보통때처럼 main.dart에 작성하고
Intl.message는 i18n/messages 클래스에 작성하겠습니다.
main.dart 파일
xxxxxxxxxx
import 'package:flutter/material.dart';
import 'i18n/messages.dart';
void main() => runApp(MyApp());
final msg = Messages();
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void initState() {
print(msg.widgetMessage);
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(msg.appName),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'${msg.widgetMessage}',
),
],
),
),
);
}
}
Message에다가 Intl.message를 모아놓습니다. 이래야 관리하기 편하더군요.
i18n/messages.dart 파일
xxxxxxxxxx
import 'package:intl/intl.dart';
class Messages {
String get widgetMessage => Intl.message("widget message",
name: "widgetMessage"
);
String get appName => Intl.message("widget message",
name: "appName"
);
}
2. 다국어 적용
이제 문자열 번역을 해보겠습니다.
문자열 번역은 크게 4단계에요.
-1. intl message를 .arb로 추출하기 -2. arb로 파일들 번역하기 -3. arb를 message_xx.dart로 바꿔주기 -4. app에 적용하기
2.1 intl의 messages를 추출해 arb 파일 만들기
intl_translation 패키지는 arb를 만드는 명령어를 지원합니다.
flutter pub run intl_translation:extract_to_arb --output-dir=[저장될 경로] [arb를 추출할 dart 파일]
같은 형식인데요. 경로와 파일을 정해줘야합니다.
아래 명령을 실행해볼께요.
파일에 있는 Intl.messages()만 골라서 arb로 바꿔주는 명령이에요.
xxxxxxxxxx
flutter pub run intl_translation:extract_to_arb --output-dir=lib/i18n lib/i18n/messages.dart
실행하면 intl_messages.arb 이 생깁니다.
intl_messages.arb를 열어볼께요.
xxxxxxxxxx
{
"@@last_modified": "2019-07-02T08:43:15.261695",
"widgetMessage": "widget message",
"@widgetMessage": {
"type": "text",
"placeholders": {}
},
"appName": "widget message",
"@appName": {
"type": "text",
"placeholders": {}
}
}
messages.dart 에서 정의했던 Intl.message()들이 json 형태로 바뀌어 있습니다.
2.2 arb로 파일들 번역하기
이제 intl_messages.arb 을 복사해 언어별 파일을 만듭니다.
- intl_en.arb
- intl_ko.arb
만든 arb을 언어에 맞게 수정해주면 되겠죠.
arb를 수정하는 건 직접 해도 되고, Google Translate Toolkit을 써도 됩니다.
intl_ko.arb
xxxxxxxxxx
{
"@@last_modified": "2019-07-02T08:23:30.820815",
"widgetMessage": "위젯이에요",
"@widgetMessage": {
"description": "Explains that we will not proceed further until the user presses a key",
"type": "text",
"placeholders": {}
},
"appName": "앱 이름입니다.",
"@appName": {
"type": "text",
"placeholders": {}
}
}
intl_en.arb
xxxxxxxxxx
{
"@@last_modified": "2019-07-02T08:23:30.820815",
"widgetMessage": "This is a new widget",
"@widgetMessage": {
"description": "Explains that we will not proceed further until the user presses a key",
"type": "text",
"placeholders": {}
},
"appName": "My flutter app",
"@appName": {
"type": "text",
"placeholders": {}
}
}
arb 파일을 직접 사용하진 못하고, 플러터에서 쓸 수 있게 dart 파일로 바꿔줘야해요.
arb로부터 변환된 dart 파일은 message_en.dart, message_ko.dart 형식이 됩니다.
2.3 arb 파일을 dart로 바꾸기
arb파일을 변환하려면 역시 intl_translation에서 지원하는 명령어를 써야합니다.
flutter pub run intl_translation:generate_from_arb --output-dir=[저장될 경로] --no-use-deferred-loading [문자열 있어 있는 다트파일] [번역된 arb 파일들]
실행해볼께요.
(맥,리눅스의 경우)
xxxxxxxxxx
flutter pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/messages.dart lib/i18n/intl_*.arb
(윈도우의 경우)
xxxxxxxxxx
flutter pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/messages.dart lib /i18n/intl_ko.arb lib/i18n/intl_en.arb
위 명령을 실행하면 messages_all.dart, messages_ko.dart, message_en.dart 가 만들어집니다.
2.4 app에 적용하기
다국어 적용은 LocationDelegate 클래스를 써서 합니다.
번역된 message들을 읽어들이고, 어떤 언어를 지원하는지 알아내서 적용하죠.
i18n/app_localizations.dart
xxxxxxxxxx
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart';
class AppLocalizations {
static Future<AppLocalizations> load(Locale locale) {
final String name = locale.countryCode == null ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((bool _ ) {
Intl.defaultLocale = localeName;
return new AppLocalizations();
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
bool isSupported(Locale locale) {
return ['en', 'ko'].contains(locale.languageCode);
}
Future<AppLocalizations> load(Locale locale) {
return AppLocalizations.load(locale);
}
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) {
return false;
}
}
AppLocalizationDelegate()를 위젯에 적용해보도록 할께요.
MaterialApp()의 localizationsDelegates 속성과, supportedLocales 속성을 바꿔줘야합니다.
main.dart
xxxxxxxxxx
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_internalization/i18n/app_localizations.dart';
import 'i18n/messages.dart';
void main() => runApp(MyApp());
final msg = Messages();
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: [
AppLocalizationDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [Locale("en"), Locale("ko")],
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
전체 프로젝트 구조입니다.
3. iOS의 경우
ios의 경우 Info.plist 도 업데이트 해줘야합니다.
번역 하려는 locale을 추가해야합니다.
Info.plist
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ko</string>
</array>
이번 글에서는 플러터 다국어 지원을 알아보았는데요.
xml 간단히 수정하던 안드로이드에 비하면 번거롭긴 합니다.
해외 출시를 계획중이라면 미리미리 다국어 지원을 해놓기를 권합니다.
한꺼번에 하려면 무척 할 일이 많더군요. (제가 그랬습니다.)
이번 글에 쓰인 코드는 깃허브 에서 확인할 수 있습니다~!
그럼 다음 플러터 글에서 뵐께요 :)
'플러터(Flutter)' 카테고리의 다른 글
Flutter - 5분안에 AndroidX 마이그레이션 하기 (0) | 2019.08.26 |
---|---|
Flutter - Provider 패턴에 대해서 알아보자 (11) | 2019.08.06 |
Flutter - CustomPainter로 차트(그래프) 그려보기 (7) | 2019.05.17 |
Flutter - 유닛 테스트 해보기 (1) | 2019.04.22 |
Flutter - Zone이란? 프로그램 종료되지 않게 예외처리 하기. (1) | 2019.04.15 |