🌐 언어: English · Tiếng Việt · 中文 · 한국어
앱은 ARB (Application Resource Bundle) 형식을 번역에 사용합니다. 파일은 apps/mobile/lib/l10n/에 위치합니다.
- 영어 (
app_en.arb) — canonical - 베트남어 (
app_vi.arb) — 지원 - 중국어 (
app_zh.arb) — 확장 가능 - 한국어 (
app_ko.arb) — 확장 가능
예시: app_en.arb
{
"@@locale": "en",
"helloTitle": "Hello",
"@helloTitle": {
"description": "Title shown on hello screen"
},
"greeting": "Welcome, {name}!",
"@greeting": {
"description": "Personalized greeting",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
}
}-
영어 ARB에 추가 (
app_en.arb):{ "myNewString": "This is my new string", "@myNewString": { "description": "Describes when/where used" } } -
즉시 다른 모든 로캘 파일에 추가 (
app_vi.arb,app_zh.arb,app_ko.arb):- 키 구조 복사
- 값 번역
- 설명 번역
-
재생성 Dart 코드:
cd apps/mobile fvm flutter gen-l10n이것은
lib/gen_l10n/app_localizations.dart+ 로캘별 파일을 생성합니다. -
코드에서 사용:
final localizations = AppLocalizations.of(context)!; Text(localizations.myNewString);
영어에는 문자열을 추가했지만 베트남어에는 추가하지 않은 경우:
- 빌드는 여전히 성공 (Flutter는 완성도 강제 안 함)
- 베트남어 사용자는 영어 문자열 표시 (폴백)
- 이상적이지 않음 — 일관되지 않은 경험
방지하려면: 코드 리뷰가 누락된 번역 발견. CI는 선택사항으로 강제 가능.
복수 형식에는 intl 메시지 구문 사용:
app_en.arb:
{
"itemCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}",
"@itemCount": {
"description": "Number of items in a list",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
}
}app_ko.arb:
{
"itemCount": "{count, plural, =0{항목 없음} one{1개 항목} other{{count}개 항목}}",
"@itemCount": {
"description": "목록의 항목 수",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
}
}사용:
final localizations = AppLocalizations.of(context)!;
Text(localizations.itemCount(items.length));- 친근함: "Oops!" 대신 "Error occurred"
- 간결함: 불필요한 단어 피하기
- 능동형: "Save your changes" not "Changes should be saved"
- 소문자 (고유명사 제외): "Enter your email" not "Enter Your Email"
- 액션 중심 버튼: "Delete Account" not "Confirm Deletion"
예시:
✓ "Save your progress"
✗ "The progress has been saved"
✓ "Oops, something went wrong"
✗ "An error has been encountered"
✓ "Sign in"
✗ "Please sign in"
- 모국어 선호 — 자연 베트남어 단어가 있을 때 영어 loanword 피하기
- 정중하지만 직접적: "Vui lòng" (please) 드물게 사용
- 소문자: "nhập email của bạn" not "Nhập Email Của Bạn"
- 영어식 표현 피하기: "đăng nhập" not "login", "hình ảnh" not "image" (실제로 모국어), "email" OK (좋은 대체 없음)
예시:
✓ "Lưu tiến trình của bạn" (Save your progress)
✗ "Tiến trình đã được save" (mixing Vietnamese + English)
✓ "Oops, có điều gì đó không ổn" (Oops, something isn't right)
✗ "Một lỗi đã xảy ra" (An error has occurred — formal/stiff)
✓ "Đăng nhập" (Sign in)
✗ "Vui lòng đăng nhập vào hệ thống" (Please sign in to the system — overly formal)
흔한 베트남어 번역:
| 영어 | 베트남어 | 참고 |
|---|---|---|
| Error | Lỗi | |
| Success | Thành công | |
| Loading | Đang tải | |
| Save | Lưu | |
| Delete | Xóa | |
| Cancel | Hủy | |
| Confirm | Xác nhận | |
| Close | Đóng | |
| Next | Tiếp theo | |
| Back | Quay lại |
- 직접적, 기술 톤 — 화려한 언어 피하기
- 간결한 문자 수 — 중국어는 이미 간결; 장황하지 않기
- 정중한 레지스터: "请" (please) 수용 가능
- 전통 용어: "用户" (user), "设置" (settings), "界面" (interface)
예시:
✓ "保存您的进度" (Save your progress)
✗ "您的进度已被保存" (Your progress has been saved — passive)
✓ "哎呀,出错了" (Oops, error)
✗ "系统遇到了一个错误" (The system encountered an error — formal)
✓ "登入" (Sign in)
✗ "请登入系统" (Please sign in to the system)
- 정중한 정식 레지스터 (~습니다) 표준
- 모국어 선호 영어 loanword보다, 하지만 기술 용어는 OK
- 주어+목적어+동사 (SOV) 어순 — 자연스럽게 따르기
- 입자 사용: 을/를 (목적어), 이/가 (주어)
예시:
✓ "진행 상황을 저장하세요" (Save your progress)
✗ "진행 상황이 저장되었습니다" (Passive/stiff)
✓ "어라, 뭔가 잘못되었어요" (Oops, something went wrong)
✗ "시스템에서 오류가 발생했습니다" (System encountered error — formal)
✓ "로그인" (Sign in — tech term, acceptable)
✗ "시스템에 로그인하십시오" (Sign in to the system — overly formal)
예시: 포르투갈어 (pt) 추가
-
app_pt.arb 생성
apps/mobile/lib/l10n/에:{ "@@locale": "pt", "helloTitle": "Olá", "greeting": "Bem-vindo, {name}!", ... }(app_en.arb에서 모든 키 복사, 값 번역)
-
pubspec.yaml 업데이트 (선택사항, IDE 지원):
flutter: generate: true supported-locales: - en - vi - zh - pt
-
재생성:
fvm flutter gen-l10n
-
app.dart에서 활성화:
MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, // Auto-updated ... )
-
커밋 both
app_pt.arb및 생성된app_localizations_pt.dart.
ARB:
{
"appTitle": "Mobile Boilerplate"
}사용:
Text(AppLocalizations.of(context)!.appTitle)ARB:
{
"welcome": "Welcome, {name}!",
"@welcome": {
"placeholders": {
"name": { "type": "String" }
}
}
}사용:
Text(AppLocalizations.of(context)!.welcome('Alice'))ARB:
{
"itemCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}",
"@itemCount": {
"placeholders": {
"count": { "type": "int" }
}
}
}사용:
Text(AppLocalizations.of(context)!.itemCount(5)) // "5 items"ARB:
{
"userRole": "{gender, select, male{He is} female{She is} other{They are}} an admin",
"@userRole": {
"placeholders": {
"gender": {
"type": "String",
"example": "male"
}
}
}
}사용:
Text(AppLocalizations.of(context)!.userRole('female')) // "She is an admin"- 기술적 키 유지:
submitButtonLabelnotbtn1 - 관련 문자열 그룹화: 모든 사용자 관련 메시지는 접두사 공유 (선택사항이지만 도움)
- 모든 로캘 테스트: 기기 언어 전환 및 텍스트 올바르게 표시 확인
- 컨텍스트 특화 문자열 피하기: "Click here" 번역 불가; "Submit" 대신 사용
- Sentence case 사용: "Save your work" not "Save Your Work"
- 코드에 하드코딩된 문자열 없음: 항상
AppLocalizations.of(context)!.key사용 - 모든 키에 설명 추가: 번역자가 컨텍스트 이해 도움
- 모든 로캘 함께 업데이트: 영어 추가 후 다른 언어 추가 안 함
| 실수 | 영향 | 수정 |
|---|---|---|
| 새 키 번역 잊음 | 사용자는 영어 (폴백) 표시 | 모든 ARB 파일에 번역 추가 |
| 코드에 하드코딩된 문자열 | 번역 불가 | AppLocalizations.of(context)!.key 사용 |
| 키 변경했지만 코드 미업데이트 | 빌드 오류 | 기존 키 검색/치환 → 새 |
| 중첩 플레이스홀더 | 구문 오류 | 평탄한 구조 사용, 필요시 코드에서 연결 |
| 로캘별 다른 매개변수 수 | 런타임 크래시 | 모든 로캘에서 매개변수 수 일치 |
최종 업데이트: 2026년 4월