Skip to main content

คู่มือโลคัลไลเซชัน Flutter อย่างครบถ้วน

ตั้งแต่ไฟล์ ARB จนถึงการรองรับ RTL ทำโลคัลไลเซชันแอป Flutter ด้วย easy_localization จัดการพหูพจน์สำหรับทุกภาษา แล้วทำให้การแปลเป็นอัตโนมัติด้วย AI

1

ติดตั้ง easy_localization

เพิ่มแพ็กเกจ easy_localization ใน pubspec.yaml ซึ่งเป็นแพ็กเกจ Flutter i18n ยอดนิยมที่สุดและรองรับไฟล์ ARB/JSON พหูพจน์ และส่วนขยาย context เพิ่ม flutter_localizations จาก SDK เพื่อจัดรูปแบบวันที่ ตัวเลข และทิศทางข้อความตามภาษาด้วย

easy_localization มีส่วนขยาย context อย่าง context.tr() และ 'key'.tr() เพื่อเข้าถึงคำแปลอย่างกระชับ จัดการการโหลดและบันทึกภาษา รวมถึงโหลดไฟล์แปลใหม่ทันทีระหว่างพัฒนา
pubspec.yaml
dependencies:
  easy_localization: ^3.0.7
  flutter_localizations:
    sdk: flutter
2

สร้างไฟล์แปล ARB

ไฟล์ Application Resource Bundle (ARB) เป็นรูปแบบโลคัลไลเซชันมาตรฐานของ Flutter แต่ละไฟล์มีคู่คีย์-ค่าพร้อมเมทาดาทาเสริมที่อธิบายตัวยึดตำแหน่ง กฎพหูพจน์ และบริบทสำหรับนักแปล สร้างหนึ่งไฟล์ต่อภาษาในไดเรกทอรี assets/translations

assets/translations/en.arb
{
  "@@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"
      }
    }
  }
}
คีย์ที่ขึ้นต้นด้วย @ เช่น @greeting เป็นเมทาดาทาที่อธิบายข้อความด้านบน ใส่ชนิดและตัวอย่างตัวยึดตำแหน่งเพื่อช่วยให้นักแปลแปลได้แม่นยำ คีย์เมทาดาทาเหล่านี้จะถูกลบขณะรันและไม่เพิ่มภาระ
3

กำหนดค่าแอป

ครอบแอปด้วยวิดเจ็ต EasyLocalization ซึ่งจัดการสถานะภาษา โหลดคำแปลจากไฟล์แอสเซ็ต และให้ delegate ภาษาที่ MaterialApp ต้องใช้ พร็อพเพอร์ตี delegate ที่ต้องมีสามรายการคือ localizationsDelegates, supportedLocales และ locale ซึ่งเข้าถึงได้ผ่านส่วนขยาย context

lib/main.dart
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(),
    );
  }
}
ข้อผิดพลาด “Easy Localization not initialized” หมายความว่าคุณลืมเรียก EasyLocalization.ensureInitialized() ก่อน runApp() ต้อง await หลัง WidgetsFlutterBinding.ensureInitialized() และก่อน runApp()
4

แปลวิดเจ็ต

ใช้เมธอดส่วนขยาย .tr() กับคีย์ข้อความเพื่อรับข้อความที่แปลแล้วในวิดเจ็ตใดก็ได้ สำหรับพหูพจน์ให้ใช้ .plural() พร้อมค่าจำนวน easy_localization มีทั้งไวยากรณ์ส่วนขยายข้อความ ('key'.tr()) และเมธอด context (context.tr('key'))

lib/widgets/home_page.dart
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)),
        ],
      ),
    );
  }
}
หากคำแปลคืนคีย์ดิบ ให้ตรวจ : 1) วิดเจ็ต EasyLocalization ครอบ MaterialApp ไม่ใช่กลับกัน 2) ประกาศไฟล์แปลใน pubspec.yaml ใต้ assets 3) พาธไฟล์ใน EasyLocalization ตรงกับโครงสร้างไดเรกทอรีจริง
5

จัดการพหูพจน์และตัวแปร

Flutter ใช้ ICU MessageFormat สำหรับพหูพจน์ ซึ่งเป็นมาตรฐานเดียวกับ iOS, Android และเว็บ กำหนดรูปพหูพจน์ด้วยไวยากรณ์ {count, plural, ...} ในไฟล์ ARB แต่ละภาษาต้องใช้ชุดรูปแบบตามกฎ CLDR ของตัวเอง ภาษาอาหรับมี 6 รูป รัสเซียมี 4 และญี่ปุ่นมี 1

Plural forms by language
// 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} товаров}}"
Variables and named arguments
// 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}));
อย่าฮาร์ดโค้ดตรรกะเอกพจน์/พหูพจน์ด้วย if (count == 1) ภาษาฝรั่งเศสมอง 0 เป็นเอกพจน์ ส่วนรัสเซีย โปแลนด์ และอาหรับมีรูปพหูพจน์ที่ภาษาอังกฤษไม่มี ให้ใช้ไวยากรณ์พหูพจน์ ICU และให้เฟรมเวิร์กเลือกรูปที่ถูกต้อง
6

รองรับภาษา RTL

Flutter สะท้อนเลย์เอาต์ทั้งหมดโดยอัตโนมัติเมื่อภาษาเป็นแบบขวาไปซ้าย เช่น อาหรับ ฮีบรู เปอร์เซีย หรืออูรดู แต่โค้ดต้องใช้วิดเจ็ตและพร็อพเพอร์ตีที่รับรู้ทิศทาง จึงจะสะท้อนได้ถูกต้อง ให้แทน left/right ที่ฮาร์ดโค้ดด้วย start/end

lib/widgets/adaptive_layout.dart
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,
          ),
        ],
      ),
    );
  }
}
RTL-aware widget patterns
// 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
ทดสอบ RTL ด้วยการตั้งภาษาแอปเป็นอาหรับชั่วคราว (Locale('ar')) Flutter จะสะท้อน UI ทั้งหมด ทั้งลิ้นชักการนำทางเปิดจากขวา ลูกศรย้อนกลับพลิก และข้อความชิดขวา ใช้ EdgeInsetsDirectional, AlignmentDirectional และ BorderRadiusDirectional เพื่อให้เลย์เอาต์กำหนดเองสะท้อนถูกต้อง
7

ลำดับการใช้ภาษาสำรองอัจฉริยะ

ตามค่าเริ่มต้น เมื่อไม่มีคำแปล pt-BR Flutter จะถอยไปใช้ภาษาอังกฤษทันทีและข้ามคำแปล pt-PT ที่ใช้ได้ดี แพ็กเกจ locale_chain แก้ปัญหานี้ด้วยลำดับภาษาสำรองที่กำหนดค่าได้ ตั้งค่าเพียงหนึ่งบรรทัดโดยไม่ต้องย้ายข้อมูล การเรียก .tr() เดิมจะทำงานต่อไป

lib/main.dart with LocaleChain
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.
Custom fallback configuration
// 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
);
locale_chain มีลำดับสำเร็จรูปสำหรับรูปแบบภาษาตามภูมิภาคของโปรตุเกส สเปน ฝรั่งเศส เยอรมัน อิตาลี ดัตช์ นอร์เวย์ และมลายู ผู้ใช้ pt-BR จะเห็นเนื้อหา pt-PT ก่อนอังกฤษ ส่วนผู้ใช้ es-MX จะเห็น es-419 แล้ว es ก่อนภาษาเริ่มต้น
8

ทำให้การแปลเป็นอัตโนมัติ

เมื่อตั้งค่าโลคัลไลเซชันเสร็จแล้ว ให้แปลไฟล์ ARB ด้วย AI โดยบอกผู้ช่วย AI ใน IDE ให้แปลไฟล์ ARB ต้นฉบับ หรือใช้ CLI ของ i18n Agent ในไปป์ไลน์ CI/CD เมทาดาทา ARB ทั้งตัวยึดตำแหน่งและคำอธิบายให้บริบทที่ช่วยเพิ่มคุณภาพคำแปล

Terminal
# 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 ต้นฉบับ ให้แปลเฉพาะส่วนต่างแทนการสร้างทุกไฟล์ใหม่ วิธีนี้ช่วยรักษาคำแปลที่มนุษย์ตรวจทานแล้วและคงเมทาดาทา ARB ไว้

ทำให้คุณภาพการแปลเป็นอัตโนมัติ

ใช้ i18n-validate จับคีย์ที่หายไปและตัวยึดตำแหน่งเสียหายก่อนส่งขึ้นใช้งาน แล้วทดสอบ UI ด้วยคำแปลจำลองผ่าน i18n-pseudo ก่อนคำแปลจริงจะมาถึง

ข้อผิดพลาดที่พบบ่อย

หาไฟล์แปลไม่พบขณะรัน

ต้องประกาศไฟล์ ARB/JSON ใน pubspec.yaml ใต้ส่วน assets หากข้ามขั้นตอนนี้ Flutter จะหาไฟล์ไม่พบขณะรันแม้มีอยู่ในดิสก์ ให้เพิ่ม : assets: - assets/translations/

ไวยากรณ์ไฟล์ ARB ไม่ถูกต้อง

ไฟล์ ARB เป็น JSON ที่เคร่งครัด ไม่รองรับจุลภาคท้าย เครื่องหมายอัญประกาศเดี่ยว หรือความคิดเห็น ข้อผิดพลาดไวยากรณ์เพียงจุดเดียวทำให้ทั้งไฟล์โหลดไม่ได้โดยไม่แจ้งเตือน ให้ตรวจไฟล์ ARB ด้วยลินเตอร์ JSON ก่อนแก้ปัญหาคำแปล

การเปลี่ยนแปลงคำแปลไม่แสดงเมื่อ Hot Reload

easy_localization แคชคำแปลในหน่วยความจำ การเพิ่มคีย์ใหม่หรือเปลี่ยนคำแปลเดิมอาจต้อง hot restart ทั้งหมด ไม่ใช่ hot reload จึงจะมีผล ระหว่างพัฒนาให้ใช้ hot restart (Shift+R) หลังแก้ไฟล์แปล

ฮาร์ดโค้ด Left/Right ทำให้ RTL เสียหาย

การใช้ EdgeInsets.only(left: 16) แทน EdgeInsetsDirectional.only(start: 16) ทำให้ Flutter สะท้อนเลย์เอาต์สำหรับภาษา RTL ไม่ได้ ค้นหา EdgeInsets, Alignment และ BorderRadius ที่ไม่มีส่วนต่อท้าย Directional ในฐานโค้ด

โครงสร้างไฟล์ที่แนะนำ

Project Structure
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 ก่อน

Terminal
flutter pub add locale_chain
Configuration
import '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 →

คำถามที่พบบ่อย