
Flutter 현지화 완벽 가이드
ARB 파일부터 RTL 지원까지 easy_localization으로 Flutter 앱을 현지화하고, 모든 언어의 복수형을 처리하고, AI로 번역을 자동화하세요.
easy_localization 설치
pubspec.yaml에 easy_localization 패키지를 추가하세요. ARB/JSON 파일, 복수형, context 확장을 지원하는 가장 인기 있는 Flutter i18n 패키지예요. 날짜, 숫자, 텍스트 방향의 로케일 인식 서식을 위해 SDK의 flutter_localizations도 추가하세요.
dependencies:
easy_localization: ^3.0.7
flutter_localizations:
sdk: flutterARB 번역 파일 생성
Application Resource Bundle(ARB) 파일은 Flutter의 표준 현지화 형식이에요. 각 파일에는 키-값 쌍과 플레이스홀더, 복수형 규칙, 번역가용 문맥을 설명하는 선택적 메타데이터가 있어요. assets/translations 디렉터리에 로케일마다 파일을 하나씩 만드세요.
{
"@@locale": "en",
"appTitle": "My App",
"@appTitle": {
"description": "The title of the application"
},
"greeting": "Hello, {name}!",
"@greeting": {
"description": "Greeting with user name",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"description": "Number of items in the cart",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
}앱 구성
EasyLocalization 위젯으로 앱을 감싸세요. 로케일 상태를 관리하고 애셋 파일에서 번역을 로드하며 MaterialApp에 필요한 로케일 대리자를 제공해요. 필수 대리자 속성 세 가지는 localizationsDelegates, supportedLocales, locale이며 모두 context 확장으로 사용할 수 있어요.
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
EasyLocalization(
supportedLocales: [
Locale('en'),
Locale('ar'),
Locale('ja'),
Locale('de'),
],
path: 'assets/translations', // Path to your ARB/JSON files
fallbackLocale: Locale('en'),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
home: HomePage(),
);
}
}위젯 번역
문자열 키에 .tr() 확장 메서드를 사용해 모든 위젯에서 번역된 텍스트를 가져오세요. 복수형에는 개수 값과 함께 .plural()을 사용하세요. easy_localization은 문자열 확장 구문('key'.tr())과 context 메서드 구문(context.tr('key'))을 모두 제공해요.
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('appTitle'.tr()), // Simple key
),
body: Column(
children: [
// String with variable interpolation
Text('greeting'.tr(args: ['Alice'])),
// Named arguments
Text('greeting'.tr(namedArgs: {'name': 'Alice'})),
// Plural form
Text('itemCount'.plural(3)),
],
),
);
}
}복수형 및 변수 처리
Flutter는 iOS, Android, 웹과 같은 표준인 ICU MessageFormat을 복수형에 사용해요. ARB 파일에 {count, plural, ...} 구문으로 복수형을 정의하세요. 각 언어에는 CLDR 복수형 규칙에 따른 자체 형식이 필요해요. 아랍어에는 6가지, 러시아어에는 4가지, 일본어에는 1가지가 있어요.
// English: 3 useful forms (=0, =1, other)
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}"
// Arabic: 6 forms (=0, =1, =2, few, many, other)
"itemCount": "{count, plural, =0{لا عناصر} =1{عنصر واحد} =2{عنصران} few{{count} عناصر} many{{count} عنصرًا} other{{count} عنصر}}"
// Japanese: 1 form (other)
"itemCount": "{count, plural, other{{count}個のアイテム}}"
// German: 2 forms (=1, other)
"itemCount": "{count, plural, =1{1 Artikel} other{{count} Artikel}}"
// Russian: 3 forms (one, few, many)
"itemCount": "{count, plural, =0{Нет товаров} one{{count} товар} few{{count} товара} many{{count} товаров} other{{count} товаров}}"// Simple variable
"welcome": "Welcome, {name}!"
// Multiple variables
"orderStatus": "Order #{orderId} — {status}"
// Variable in plural context
"unreadMessages": "{count, plural, =0{No unread messages} =1{1 unread message from {sender}} other{{count} unread messages}}"
// In widgets:
Text('welcome'.tr(namedArgs: {'name': userName}));
Text('orderStatus'.tr(namedArgs: {'orderId': '1234', 'status': 'Shipped'}));
Text('unreadMessages'.plural(count, namedArgs: {'sender': senderName}));RTL 언어 지원
Flutter는 로케일이 아랍어, 히브리어, 페르시아어, 우르두어 같은 오른쪽에서 왼쪽으로 쓰는 언어이면 전체 레이아웃을 자동으로 반전해요. 올바르게 반전하려면 코드에서 방향을 인식하는 위젯과 속성을 사용해야 해요. 하드코딩한 left/right를 start/end로 바꾸세요.
import 'package:flutter/material.dart';
class AdaptiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isRtl = Directionality.of(context) == TextDirection.rtl;
return Scaffold(
body: Row(
children: [
// Use start/end instead of left/right
Expanded(
child: Padding(
padding: EdgeInsetsDirectional.only(
start: 16.0, // Leading edge (left in LTR, right in RTL)
end: 8.0, // Trailing edge
),
child: Text('content'.tr()),
),
),
// Flip icons for RTL
Icon(
isRtl ? Icons.arrow_back : Icons.arrow_forward,
),
],
),
);
}
}// Use Directional widgets for RTL-aware layouts
EdgeInsetsDirectional.only(start: 16, end: 8) // Not EdgeInsets.only(left: 16, right: 8)
AlignmentDirectional.centerStart // Not Alignment.centerLeft
BorderRadiusDirectional.only(topStart: Radius.circular(8))
// Automatically mirrors when locale changes to Arabic, Hebrew, etc.
// No conditional logic needed — Flutter handles text direction from locale스마트 로케일 폴백 체인
기본적으로 pt-BR 번역이 없으면 Flutter가 쓸 수 있는 pt-PT 번역을 건너뛰고 영어로 바로 폴백해요. locale_chain 패키지는 구성 가능한 폴백 체인으로 이 문제를 해결해요. 한 줄만 설정하면 되고 마이그레이션도 필요 없으며 기존 .tr() 호출이 그대로 작동해요.
import 'package:locale_chain/locale_chain.dart';
import 'package:locale_chain/easy_localization.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
LocaleChain.configure(); // Enable smart fallback chains
runApp(
EasyLocalization(
supportedLocales: [
Locale('en'),
Locale('pt', 'BR'),
Locale('pt', 'PT'),
Locale('es'),
Locale('es', 'MX'),
],
path: 'assets/translations',
assetLoader: LocaleChainAssetLoader( // Swap in the chain loader
baseLoader: RootBundleAssetLoader(),
),
fallbackLocale: Locale('en'),
child: MyApp(),
),
);
}
// Result: pt-BR user sees pt-PT translations when pt-BR keys are missing,
// instead of falling back directly to English.// Override specific chains while keeping built-in defaults
LocaleChain.configure(
fallbacks: {
'pt-BR': ['pt-PT', 'pt'], // pt-BR → pt-PT → pt → en
'es-MX': ['es-419', 'es'], // es-MX → es-419 → es → en
'fr-CA': ['fr'], // fr-CA → fr → en
},
);
// Or replace all defaults with your own chains
LocaleChain.configure(
fallbacks: {
'zh-Hant-HK': ['zh-Hant-TW', 'zh-Hans'],
'pt-BR': ['pt-PT', 'pt'],
},
mergeDefaults: false, // Only your chains, no built-in defaults
);번역 자동화
현지화 설정을 마쳤다면 AI로 ARB 파일을 번역하세요. IDE에서 AI 어시스턴트에게 원문 ARB 파일 번역을 요청하거나 CI/CD 파이프라인에서 i18n Agent CLI를 사용하세요. ARB 메타데이터(플레이스홀더, 설명)는 번역 품질을 높이는 문맥을 제공해요.
# In your IDE, ask your AI assistant:
> Translate assets/translations/en.arb to Arabic, Japanese, and German
✓ ar.arb created (1.4s)
✓ ja.arb created (1.2s)
✓ de.arb created (1.1s)
# Or use the CLI in CI/CD:
npx i18n-agent translate assets/translations/en.arb --lang ar,ja,de번역 품질 자동화
흔한 실수
런타임에 번역 파일을 찾을 수 없음
잘못된 ARB 파일 구문
핫 리로드 후 번역 변경 사항 미표시
하드코딩된 Left/Right로 인한 RTL 오류
권장 파일 구조
my_flutter_app/
├── assets/
│ └── translations/
│ ├── en.arb # Source language (English)
│ ├── ar.arb # Arabic (with 6 plural forms)
│ ├── de.arb # German
│ ├── ja.arb # Japanese
│ ├── pt-BR.arb # Brazilian Portuguese
│ └── pt-PT.arb # European Portuguese
├── lib/
│ ├── main.dart # App entry with EasyLocalization
│ ├── app.dart # MaterialApp with locale delegates
│ └── widgets/
│ └── language_switcher.dart
├── pubspec.yaml # Dependencies
└── analysis_options.yaml지금 i18n Agent 사용해 보기
번역 파일을 여기에 드롭
JSON, YAML, PO, XML, CSV, Markdown, Properties
또는 클릭하여 파일 선택
대상 언어
locale_chain을 활용한 로케일 폴백
pt-BR 같은 지역 로케일에 번역 키가 없으면 Flutter는 상위 로케일 pt를 먼저 확인하지 않고 템플릿 언어로 바로 이동해요.
flutter pub add locale_chainimport 'package:locale_chain/locale_chain.dart';
LocaleChain.configure(fallbacks: {
'pt-BR': ['pt', 'en'],
'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
});
LocaleChainAssetLoader(baseLoader: RootBundleAssetLoader());지원 프레임워크와 내장 체인 75개의 전체 목록은 로케일 폴백 가이드에서 확인하세요. Learn more →