플루터에 i18n을 적용하려고 합니다.
지원하는 패키지는 intl, flutter_i18n, easy_localization 등이 있는데, publisher가 dart.dev인 intl 패키지를 사용하겠습니다.
intl은 dart에서 internalization and localization를 지원하는 패키지입니다. 다국어 처리나 성별, 날짜, 숫자 등을 그 지역이나 국가에 맞게 바꿀 때 사용합니다.
간단하게 설명하자면 dart code를 arb로 generate 하고 변환한 arb 파일을 다시 dart로 변환하여 다국어 적용을 합니다.
arb란 Application Resource Bundle로 구글에서 만든 파일 형식입니다.
(key:value 형식을 원하시면 easy_localization 패키지를 확인)
우선 pubspec.yaml에 dependency를 추가합니다.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.16.1
dev_dependencies:
flutter_test:
sdk: flutter
intl_translation: ^0.17.10+1
key로 사용할 변수들을 담은 messages.dart를 작성하겠습니다. name과 변수명은 같아야합니다.
import 'package:intl/intl.dart';
class Messages {
String get appName => Intl.message(
"Flutter APP",
name: "appName"
);
String get helloWorld => Intl.message(
"Hello, World!",
name: "helloWorld"
);
hello(str) => Intl.message(
"Hello, $text!",
name: "hello",
args: [str]
);
}
그다음은 intl message를 .arb로 추출해야 하는데요. intl_translation 패키지는 arb를 만드는 명령어를 지원합니다.
flutter pub run intl_translation:extract_to_arb --output-dir=[추출한 arb 파일이 저장될 경로] [arb를 추출할 dart 파일]
flutter pub run intl_translation:extract_to_arb --output-dir=assets/i18n lib/i18n/messages.dart
위 명령어를 실행하면 intl_messages.arb 파일이 assets/i18n 폴더 하위에 생성됩니다.
{
"@@last_modified": "2021-03-04T20:04:17.732096",
"appName": "Flutter APP",
"@appName": {
"type": "text",
"placeholders": {}
},
"helloWorld": "Hello, World!",
"@helloWorld": {
"type": "text",
"placeholders": {}
},
"hello": "Hello, {str}!",
"@hello": {
"type": "text",
"placeholders": {
"str": {}
}
}
}
messages.dart에서 정의했던 Intl.messages()들이 json형태로 바뀌었습니다.
이제 intl_messages.arb을 복사하여 언어별로 파일을 생성합니다.
- intl_en.arb
- intl_ko.arb
{
"@@last_modified": "2021-03-04T20:04:17.732096",
"appName": "플루터 APP",
"@appName": {
"type": "text",
"placeholders": {}
},
"helloWorld": "안녕, 세상!",
"@helloWorld": {
"type": "text",
"placeholders": {}
},
"hello": "안녕, {str}!",
"@hello": {
"type": "text",
"placeholders": {
"str": {}
}
}
}
arb 파일이 생성되었으면 끝인가? 아닙니다. arb 파일을 직접 사용하진 못하고 플루터에서 사용할 수 있게 dart파일로 변경해줘야 합니다.
arb파일을 변환하려면 역시 intl_translation에서 지원하는 명령어를 사용해야 합니다.
flutter pub run intl_translation:generate_from_arb --output-dir=[저장될 경로] --no-use-deferred-loading [문자열이 있는 dart파일] [다국어 arb 파일들]
# windows
flutter pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/messages.dart assets/i18n/intl_ko.arb assets/i18n/intl_en.arb
# mac, linux
flutter pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/messages.dart assets/i18n/intl_*.arb
위 명령어를 실행하면 lib/i18n 하위에 messages_all.dart, messages_ko.dart, messages_en.dart가 생성됩니다.
다국어 적용은 LocationDelegate 클래스를 사용해야 합니다. 번역된 message들을 읽어 들이고, 어떤 언어를 지원하는지 알아내서 적용합니다.
lib/i18n/localizations.dart
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> {
@override
bool isSupported(Locale locale) {
return ['en', 'ko'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) {
return AppLocalizations.load(locale);
}
@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) {
return false;
}
}
AppLocalizationDelegate()를 위젯에 적용해보도록 하겠습니다.
MaterialApp()의 localizationsDelegates 속성과 supportedLocales 속성을 바꿔줘야 합니다.
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'i18n/localizations.dart';
import 'i18n/messages.dart';
void main() => {
runApp(MyApp());
}
final msg = Messages();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: [
AppLocalizationDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, // https://moonsiri.tistory.com/119
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [Locale("en"), Locale("ko")],
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: msg.hello('Flutter')),
);
}
}
완성된 파일 구조는 다음과 같습니다.
project
│
└─ assets
└─ i18n
└─ intl_en.arb
└─ intl_ko.arb
└─ intl_messages.arb
│
└─ lib
└─ i18n
└─ localization.dart
└─ messages.dart
└─ messages_all.dart
└─ messages_en.dart
└─ messages_ko.dart
└─ main.dart
iOS의 경우는 Info.plist에 번역하려는 locale을 추가해야 합니다.
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ko</string>
</array>
이렇게 기기의 설정된 언어로 다국어 처리하는 방법을 알아보았습니다.
[Reference]
software-creator.tistory.com/24
'mobile > flutter' 카테고리의 다른 글
[flutter] Copy to clipboard on tap. Use ScaffoldMessenger.showSnackBar. (0) | 2021.08.17 |
---|---|
[Dart] Null safety (0) | 2021.07.14 |
[flutter] Scaffold.of() called with a context that does not contain a Scaffold. 해결방법과 Copy to clipboard on tap (0) | 2021.02.06 |
[flutter] draw horizontal, vertical line(divider) (0) | 2020.12.01 |
[flutter] Unexpected key assets ((String)) under font. (0) | 2020.12.01 |
댓글