Flutter에서 다국어 앱(i18n)을 어떻게 구현하나요?
Flutter에서 다국어 앱(국제화, i18n)을 구현하는 방법에는 여러 가지가 있지만, 가장 권장되는 방법은 공식 flutter_localizations
패키지와 intl
패키지를 함께 사용하는 것입니다. 아래에서 단계별로 Flutter 앱에서 다국어 지원을 구현하는 방법을 설명하겠습니다.
1. 필요한 패키지 추가
먼저 pubspec.yaml
파일에 필요한 패키지를 추가합니다:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.18.0
flutter:
generate: true # 자동 생성된 localizations를 사용하기 위해 필요
2. l10n.yaml 설정 파일 생성
프로젝트 루트 디렉토리에 l10n.yaml
파일을 생성하고 다음 내용을 추가합니다:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
nullable-getter: false
3. ARB 파일 생성
lib/l10n
디렉토리를 생성하고, 기본 언어(영어)에 대한 app_en.arb
파일을 만듭니다:
{
"helloWorld": "Hello World",
"@helloWorld": {
"description": "The conventional greeting"
},
"welcome": "Welcome {name}",
"@welcome": {
"description": "Welcome message",
"placeholders": {
"name": {
"type": "String",
"example": "John"
}
}
},
"items": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@items": {
"description": "Number of items",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
}
그 후 다른 언어 파일도 생성합니다. 예를 들어, 한국어를 위한 app_ko.arb
:
{
"helloWorld": "안녕 세상",
"welcome": "{name}님 환영합니다",
"items": "{count, plural, =0{항목 없음} =1{1개 항목} other{{count}개 항목}}"
}
4. Localizations 설정
main.dart
파일에서 앱의 지원 언어를 설정합니다:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter i18n Demo',
// 지원할 언어 목록
supportedLocales: AppLocalizations.supportedLocales,
// 로컬라이제이션 대리자
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// 기본 언어 설정 (장치 언어를 따름)
localeResolutionCallback: (locale, supportedLocales) {
// 장치 언어가 지원되지 않는 경우 영어를 기본으로 사용
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return supportedLocales.first; // 기본값은 영어
},
home: MyHomePage(),
);
}
}
5. 생성된 Localizations 사용
위젯에서 생성된 AppLocalizations
클래스를 사용하여 현재 언어에 맞는 문자열을 표시합니다:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 현재 로케일에 대한 AppLocalizations 인스턴스 가져오기
final localizations = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(localizations.helloWorld),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(localizations.welcome('John')),
Text(localizations.items(5)),
],
),
),
);
}
}
6. 언어 전환 기능 구현
사용자가 앱 내에서 언어를 변경할 수 있도록 하려면 Provider
패키지나 다른 상태 관리 솔루션을 사용하여 현재 locale을 관리할 수 있습니다:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class LocaleProvider extends ChangeNotifier {
Locale _locale = Locale('en');
Locale get locale => _locale;
void setLocale(Locale locale) {
_locale = locale;
notifyListeners();
}
}
// main.dart
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => LocaleProvider(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<LocaleProvider>(
builder: (context, localeProvider, child) {
return MaterialApp(
title: 'Flutter i18n Demo',
locale: localeProvider.locale, // 현재 선택된 로케일 사용
supportedLocales: AppLocalizations.supportedLocales,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MyHomePage(),
);
},
);
}
}
// 언어 선택 화면
class LanguageSelectionPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.languageSelection),
),
body: ListView(
children: [
ListTile(
title: Text('English'),
onTap: () {
localeProvider.setLocale(Locale('en'));
},
trailing: localeProvider.locale.languageCode == 'en'
? Icon(Icons.check)
: null,
),
ListTile(
title: Text('한국어'),
onTap: () {
localeProvider.setLocale(Locale('ko'));
},
trailing: localeProvider.locale.languageCode == 'ko'
? Icon(Icons.check)
: null,
),
// 다른 언어 추가...
],
),
);
}
}
7. 날짜, 숫자 등의 형식화
intl
패키지를 사용하여 날짜, 숫자 등을 현재 로케일에 맞게 형식화할 수 있습니다:
import 'package:intl/intl.dart';
// 현재 로케일에 맞는 날짜 형식
String formatDate(BuildContext context, DateTime date) {
final locale = Localizations.localeOf(context).toString();
return DateFormat.yMMMd(locale).format(date);
}
// 현재 로케일에 맞는 숫자 형식
String formatNumber(BuildContext context, num number) {
final locale = Localizations.localeOf(context).toString();
return NumberFormat.decimalPattern(locale).format(number);
}
// 현재 로케일에 맞는 통화 형식
String formatCurrency(BuildContext context, num amount) {
final locale = Localizations.localeOf(context).toString();
return NumberFormat.currency(
locale: locale,
symbol: locale == 'ko' ? '₩' : '\$',
).format(amount);
}
8. 언어 설정 저장
사용자가 선택한 언어 설정을 shared_preferences
와 같은 로컬 저장소에 저장하여 앱 재시작 시에도 유지할 수 있습니다:
import 'package:shared_preferences/shared_preferences.dart';
class LocaleProvider extends ChangeNotifier {
Locale _locale = Locale('en');
Locale get locale => _locale;
// 저장된 언어 설정 로드
Future<void> loadSavedLocale() async {
final prefs = await SharedPreferences.getInstance();
final languageCode = prefs.getString('languageCode');
if (languageCode != null) {
_locale = Locale(languageCode);
notifyListeners();
}
}
// 선택한 언어 설정 저장
Future<void> setLocale(Locale locale) async {
if (_locale == locale) return;
_locale = locale;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setString('languageCode', locale.languageCode);
}
}
// main.dart에서 앱 시작 시 저장된 설정 로드
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final localeProvider = LocaleProvider();
await localeProvider.loadSavedLocale();
runApp(
ChangeNotifierProvider.value(
value: localeProvider,
child: MyApp(),
),
);
}
9. 특정 언어에 대한 리소스 관리
이미지, 아이콘 등 텍스트가 아닌 리소스도 언어에 따라 달라질 수 있습니다. 이런 리소스도 로케일에 따라 관리할 수 있습니다:
class LocalizedAssets {
static String getImage(BuildContext context, String name) {
final locale = Localizations.localeOf(context).languageCode;
return 'assets/images/${locale}/$name';
}
}
// 사용 예
Image.asset(LocalizedAssets.getImage(context, 'welcome_banner.png'))
10. 효율적인 번역 관리
번역 파일이 많아지면 관리가 복잡해질 수 있습니다. 스프레드시트나 번역 관리 도구를 사용하여 번역을 관리하고 ARB 파일로 내보내는 워크플로우를 구축하면 효율적입니다.
요약
Flutter에서 다국어 앱을 구현하는 주요 단계는 다음과 같습니다:
flutter_localizations
및intl
패키지 추가- ARB 파일에 번역 정의
- 앱 설정에
localizationsDelegates
와supportedLocales
설정 - 생성된
AppLocalizations
클래스를 통해 번역된 문자열 사용 - 언어 전환 기능 및 사용자 설정 저장 구현
- 날짜, 숫자 등의 형식화 처리
이 방법을 통해 Flutter 앱에서 효과적으로 다국어를 지원하고, 사용자에게 지역화된 경험을 제공할 수 있습니다.