플러터는 다국어 지원을 위한 국제화(i18n) 처리를 간단하게 구현할 수 있습니다. 플러터에서는 intl
패키지를 사용하여 어플리케이션의 문자열을 다국어로 변환할 수 있습니다.
1. intl
패키지 추가하기
먼저, pubspec.yaml
파일에 intl
패키지를 추가해야 합니다. 아래의 코드를 dependencies
섹션에 추가해 주세요.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.17.0
그리고 터미널에서 flutter pub get
명령어를 실행하여 패키지를 가져옵니다.
2. arb
파일 생성하기
국제화를 위해 문자열을 저장할 arb
파일을 생성해야 합니다. arb
파일은 JSON 포맷으로 이루어져 있으며, 다국어 문자열을 저장합니다. lib
디렉토리에 l10n
디렉토리를 생성하고, 그 안에 app_localizations.arb
파일을 생성해 주세요.
app_localizations.arb
파일 내부에는 다음과 같은 형식으로 문자열과 해당 언어의 번역을 작성합니다. 예를 들면:
{
"hello": "Hello",
"@hello": {
"description": "The word 'hello'",
"type": "text",
"placeholders": {}
},
"welcome": "Welcome {name}",
"@welcome": {
"description": "A welcome message with a name parameter",
"type": "text",
"placeholders": {
"name": {}
}
}
}
위의 예제에서는 hello
과 welcome
이라는 두 개의 문자열을 다국어로 처리하고 있습니다. welcome
문자열은 이름을 포함하기 때문에 placeholders
를 사용하였습니다.
3. arb
파일을 dart
파일로 변환하기
이제 생성한 arb
파일을 dart
파일로 변환해야 합니다. 터미널에서 다음 명령어를 실행하여 변환을 수행합니다.
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/localizations.dart
변환 후, lib
디렉토리 내에 l10n
디렉토리에 app_localizations.dart
파일이 생성됩니다.
4. dart
파일에서 다국어 처리하기
이제 변환한 app_localizations.dart
파일에서 다국어 처리를 할 수 있습니다. lib
폴더에 localizations.dart
파일을 생성하고 다음과 같이 작성해주세요.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/message_lookup_by_library.dart' as messages;
import 'package:intl/message_lookup_by_library.dart' show MessageLookupByLibrary;
import 'l10n/app_localizations.dart';
typedef Future<dynamic> LibraryLoader();
Map<String, LibraryLoader> _deferredLibraries = {
'ko': () => Future.value(null)
};
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['ko'].contains(locale.languageCode);
@override
Future<AppLocalizations> load(Locale locale) {
final String lang = locale.languageCode.toLowerCase();
final String country = locale.countryCode.toLowerCase();
Intl.defaultLocale = "$lang_$country";
return initializeMessages(lang).then((_) {
return Future<AppLocalizations>.value(lookupMessages(lang));
});
}
@override
bool shouldReload(AppLocalizationsDelegate old) => false;
}
LocaleListResolutionCallback localeListResolution(
{required List<String> supportedLanguages}) {
return (List<Locale>? preferredLocales, List<Locale>? supportedLocales) {
Locale? preferredLocale;
for (final Locale locale in preferredLocales ?? <Locale>[]) {
if (locale.languageCode == "ko") {
preferredLocale = locale;
break;
}
}
if (preferredLocale != null) {
return preferredLocale;
}
for (final Locale locale in preferredLocales ?? <Locale>[]) {
if (locale.languageCode == "en") {
preferredLocale = locale;
break;
}
}
if (preferredLocale != null) {
return preferredLocale;
}
// Fallback to the first supported locale
return supportedLocales!.first;
};
}
typedef LocaleResolutionCallback = Locale Function(
Locale? locale, Iterable<Locale> supportedLocales);
Map<String, LocaleResolutionCallback> _localeResolutionCallbacks =
<String, LocaleResolutionCallback>{
'': localeListResolution(supportedLanguages: ['ko', 'en']),
};
@Deprecated('Use `ExampleLocalizations` from the generated file `l10n/app_localizations.dart`. ')
typedef ExampleLocalizations = AppLocalizations;
class AppLocalizations {
AppLocalizations(Locale locale)
: localeName = Intl.canonicalizedLocale(locale.toString());
final String localeName;
static const LocalizationsDelegate<AppLocalizations> delegate =
AppLocalizationsDelegate();
static Future<AppLocalizations> load(Locale locale) {
return initializeMessages(locale.toString()).then((_) {
return AppLocalizations(locale);
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
static Future<AppLocalizations> init(Locale locale) async {
final String localeName = Intl.canonicalizedLocale(locale.toString());
await initializeMessages(locale.toString());
Intl.defaultLocale = localeName;
return AppLocalizations(locale);
}
String get hello {
return Intl.message(
'Hello',
name: 'hello',
desc: 'The word \'hello\'',
locale: localeName,
);
}
String welcome(String name) {
return Intl.message(
'Welcome $name',
name: 'welcome',
desc: 'A welcome message with a name parameter',
locale: localeName,
args: [name],
);
}
}
위의 코드는 lib/localizations.dart
에 해당하는 부분입니다. 이 코드는 intl
패키지를 이용하여 다국어를 처리하는 코드입니다.
5. 언어 변경 기능 추가하기
이제 언어 변경 기능을 추가해야 합니다. 예를 들어 앱의 설정 화면에서 사용자가 원하는 언어를 선택할 수 있도록 구현할 수 있습니다.
먼저 main.dart
파일을 다음과 같이 수정해 주세요.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_i18n/l10n/app_localizations.dart';
import 'package:flutter_i18n/l10n/localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Internationalization Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('ko', 'KR'),
const Locale('en', 'US'),
],
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(appLocalizations.hello),
),
body: Center(
child: Text(
appLocalizations.welcome("John"),
style: TextStyle(fontSize: 20),
),
),
);
}
}
위의 예제에서는 언어 변경을 위해 GlobalMaterialLocalizations.delegate
와 GlobalWidgetsLocalizations.delegate
를 localizationsDelegates
에 추가하였습니다. 사용자가 원하는 언어로 앱을 실행하면 자동으로 해당 언어로 표시됩니다.
6. 번역 추가하기
새로운 언어로 번역을 추가하려면, app_localizations.arb
파일 내에 언어 코드에 해당하는 부분을 추가해야 합니다. 예를 들어 한국어로 번역을 추가하려면 다음과 같이 작성해 주세요.
{
"@@locale": "ko",
"hello": "안녕하세요",
"welcome": "{name}님 환영합니다"
}
이제 앱을 실행하여 언어 변경 기능이 원활하게 동작하는지 확인해보세요. 사용자의 언어 설정에 따라 앱의 문자열이 자동으로 번역되는 것을 확인할 수 있습니다.