Flutter 내장 현지화 시스템은 Application Resource Bundle(ARB) 파일과 intl 패키지를 사용해 어떤 언어에서도 완전한 네이티브 경험을 제공합니다. 이 가이드는 프로젝트 설정, ARB 형식 세부사항부터 언어 전환, 복수형 처리, 그리고 l10n.dev를 이용한 번역 자동화까지 모두 다룹니다.
Flutter 현지화는 앱을 여러 언어와 지역에 맞게 조정하는 공식 방법입니다. 번역된 문자열을 저장하는 ARB 파일과 타입 안전 Dart 접근자를 생성하는 flutter_gen 코드 생성기를 사용합니다. 실행 시 Flutter는 기기 로케일에 맞는 ARB 파일을 선택하며, 프로그래밍 방식으로 로케일을 변경해도 재시작이 필요 없습니다.
Flutter 현지화 파이프라인은 pubspec.yaml과 l10n.yaml 두 개의 구성 파일로 구동됩니다. pubspec.yaml에서 코드 생성을 활성화하고 Flutter에게 ARB 디렉터리를 지정하세요.
flutter_localizations와 intl을 의존성에 추가하고 generate 플래그를 활성화해 Flutter가 ARB 파일에서 자동으로 Dart 현지화 클래스를 생성하도록 하세요.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
flutter:
generate: true프로젝트 루트에 l10n.yaml 파일을 두어 ARB 디렉터리, 템플릿 파일, 생성될 출력 파일 이름을 설정하세요.
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dartARB(Application Resource Bundle)는 Flutter 국제화를 위해 특별히 설계된 JSON 기반 형식입니다. 각 키는 번역 가능한 문자열에 매핑되며, 선택적 메타데이터 키(@로 시작)는 설명, 자리표시자 정의, 번역가를 위한 문맥을 포함합니다.
{
"@@locale": "en",
"@@last_modified": "2026-01-15T10:30:00Z",
"appTitle": "My App",
"@appTitle": {
"description": "The title of the application"
},
"welcome": "Welcome, {name}!",
"@welcome": {
"description": "Welcome message shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"unreadMessages": "{count, plural, =0{No unread messages} =1{1 unread message} other{{count} unread messages}}",
"@unreadMessages": {
"description": "Number of unread messages",
"placeholders": {
"count": {
"type": "int"
}
}
}
}번역 후 UI 문자열은 현지화되고 메타데이터 구조는 그대로 유지됩니다. l10n.dev가 @@locale과 @@last_modified를 자동으로 업데이트합니다.
{
"@@locale": "fr",
"@@last_modified": "2026-01-15T10:30:01Z",
"appTitle": "Mon Application",
"@appTitle": {
"description": "The title of the application"
},
"welcome": "Bienvenue, {name} !",
"@welcome": {
"description": "Welcome message shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"unreadMessages": "{count, plural, =0{Aucun message non lu} =1{1 message non lu} other{{count} messages non lus}}",
"@unreadMessages": {
"description": "Number of unread messages",
"placeholders": {
"count": {
"type": "int"
}
}
}
}ARB 파일은 기본 접두사(app_) 뒤에 로케일 코드와 .arb 확장자를 붙이는 단순한 명명 패턴을 따릅니다. Flutter와 l10n.dev는 언어와 지역 코드를 구분할 때 밑줄(_)을 사용합니다.
# Default pattern (recommended)
app_en.arb
app_fr.arb
app_en_US.arb # Locale with region (underscore format)
app_zh_CN.arb # Chinese Simplified
# Custom prefix patterns also supported
my_app_en.arb
my_app_fr.arb
# Note: ARB files use underscores, not hyphens
# ✓ app_en_US.arb
# ✗ app_en-US.arb모든 ARB 파일을 lib/ 내의 전용 l10n 폴더에 정리하세요. Flutter 코드 생성기는 l10n.yaml의 arb-dir 설정을 기반으로 자동으로 이들을 인식하고 바로 사용할 수 있는 Dart 클래스를 출력합니다.
lib/
├── l10n/
│ ├── app_en.arb ← Source (English)
│ ├── app_fr.arb ← French
│ ├── app_de.arb ← German
│ ├── app_ja.arb ← Japanese
│ ├── app_zh_CN.arb ← Chinese Simplified
│ └── app_es.arb ← Spanish
├── main.dart
└── ...
# Generated output (do not edit manually)
.dart_tool/
└── flutter_gen/
└── gen_l10n/
└── app_localizations.dart생성된 Dart 현지화 클래스를 MaterialApp에 연결하려면 localizationsDelegates와 supportedLocales를 제공하세요. 그러면 Flutter가 기기 로케일에 맞는 ARB 데이터를 올바르게 해결합니다.
필수 4개 위임자(생성된 AppLocalizations.delegate와 Flutter SDK 3개 위임자)를 전달하고, 앱이 지원하는 모든 로케일을 나열하세요. 위젯 트리 어디서든 AppLocalizations.of(context)!를 사용해 번역된 문자열에 접근할 수 있습니다.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
// Required delegates
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// Supported locales
supportedLocales: AppLocalizations.supportedLocales,
home: const HomePage(),
);
}
}
// Use in a widget
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.appTitle)),
body: Center(child: Text(l10n.welcome('Alice'))),
);
}
}Flutter는 MaterialApp의 locale 속성을 업데이트해 실행 중에 앱 로케일을 전환할 수 있게 합니다. 일반적인 패턴은 StatefulWidget이나 상태 관리 솔루션에서 Locale을 보유하고 변경 콜백을 노출하는 것입니다. 위젯 트리는 새 로케일로 자동 재빌드됩니다.
// Manage locale state at the app level
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Locale _locale = const Locale('en');
void _changeLocale(Locale locale) {
setState(() => _locale = locale);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: SettingsPage(onLocaleChange: _changeLocale),
);
}
}
// Language selector widget
class LanguageSelector extends StatelessWidget {
final void Function(Locale) onLocaleChange;
const LanguageSelector({super.key, required this.onLocaleChange});
@override
Widget build(BuildContext context) {
return DropdownButton<Locale>(
value: Localizations.localeOf(context),
items: AppLocalizations.supportedLocales
.map((locale) => DropdownMenuItem(
value: locale,
child: Text(locale.toLanguageTag()),
))
.toList(),
onChanged: (locale) {
if (locale != null) onLocaleChange(locale);
},
);
}
}Flutter는 ARB 파일 내 복수형 처리를 위해 ICU 메시지 구문을 사용합니다. intl 패키지는 모든 CLDR 복수형 범주를 처리하며, 대상 언어 규칙에 따라 올바른 형태를 자동 적용합니다.
{
"cartItems": "{count, plural, =0{Your cart is empty} =1{1 item in your cart} other{{count} items in your cart}}",
"@cartItems": {
"description": "Cart item count",
"placeholders": {
"count": { "type": "int" }
}
},
"daysLeft": "{days, plural, =1{1 day left} other{{days} days left}}",
"@daysLeft": {
"description": "Days remaining",
"placeholders": {
"days": { "type": "int" }
}
}
}l10n.dev는 Flutter ARB 워크플로우와 원활히 작동하도록 설계되었습니다. 원본 ARB 파일을 업로드하면 정확하고 올바른 구조의 번역을 받을 수 있습니다:
ai-l10n npm 패키지를 사용해 명령줄이나 CI/CD 파이프라인에서 원본 ARB 파일을 번역하세요. 한 번 설치 후 한 명령으로 여러 언어로 번역할 수 있습니다.
# Install the CLI
npm install ai-l10n
# Translate your source ARB to multiple languages
npx ai-l10n translate lib/l10n/app_en.arb \
--languages fr,de,ja,zh_CN,es,koai-l10n을 Flutter 빌드 프로세스에 직접 연결하여 앱이 컴파일되기 전에 항상 번역이 최신 상태로 유지되도록 할 수 있습니다. 두 가지 일반적인 방법으로 package.json 스크립트나 Makefile을 사용하는 방법이 있습니다.
translate 스크립트를 추가하고 npm의 prebuild 라이프사이클 훅을 사용하여 모든 빌드 전에 자동으로 실행되도록 하세요. npm run build:android(또는 build:ios / build:web)를 실행하면 ai-l10n이 먼저 번역을 수행한 다음 Flutter로 작업을 넘깁니다.
{
"scripts": {
"translate": "ai-l10n translate lib/l10n/app_en.arb --languages fr,de,ja,zh_CN,es,ko --update",
"prebuild": "npm run translate",
"build:android": "flutter build apk",
"build:ios": "flutter build ios",
"build:web": "flutter build web"
}
}팀에서 Make를 사용하는 경우, translate 타겟을 선언하고 각 빌드 타겟이 이에 의존하도록 설정하세요. make build-android(또는 build-ios / build-all)를 실행하면 flutter build를 호출하기 전에 모든 대상 언어가 번역됩니다.
LANGUAGES = fr,de,ja,zh_CN,es,ko
translate:
npx ai-l10n translate lib/l10n/app_en.arb --languages $(LANGUAGES) --update
build-android: translate
flutter build apk
build-ios: translate
flutter build ios
build-web: translate
flutter build web
build-all: translate
flutter build apk
flutter build ios
flutter build webCI/CD 통합, 점진적 업데이트, 여러 파일 일괄 번역, GitHub Actions 워크플로 예제는 현지화 자동화 가이드에서 확인하세요.
l10n.dev VS Code 확장은 Flutter ARB 번역을 편집기 내에서 바로 제공합니다. ARB 파일을 우클릭해 VS Code를 떠나지 않고 대상 언어로 번역하세요.
다음은 VS Code 내에서 Flutter ARB 파일을 우즈베크어로 번역하는 실시간 예제입니다:

전 세계 사용자에게 다가갈 준비가 되셨나요? l10n.dev 작업 공간에서 ARB 파일을 직접 번역하거나, npm CLI로 자동화하거나, VS Code 내에서 바로 번역할 수 있습니다:
l10n.dev를 사용해 Flutter 앱을 현지화해 주셔서 감사합니다! 🚀
이 가이드가 도움이 되셨다면, 글로벌 사용자에게 다가가려는 다른 Flutter 개발자와 공유해 주세요.
함께 Flutter 앱을 더 포용적이고 세계에 준비된 앱으로 만들어 갑시다.