Skip to main content

الدليل الشامل لتوطين Flutter

من ملفات ARB إلى دعم RTL: وطّن تطبيق Flutter باستخدام easy_localization، وتعامل مع صيغ الجمع لكل لغة، وأتمت الترجمات بالذكاء الاصطناعي.

1

تثبيت easy_localization

أضِف حزمة easy_localization إلى pubspec.yaml. هذه هي الحزمة الأكثر شيوعاً لتوطين Flutter، مع دعم ملفات ARB/JSON وصيغ الجمع وامتدادات السياق. وأضِف أيضاً flutter_localizations من SDK لتنسيق التواريخ والأرقام واتجاه النص اعتماداً على locale.

توفر easy_localization امتدادات سياق مثل context.tr() و 'key'.tr() للوصول المختصر إلى الترجمات. كما تتولى تحميل locale وحفظه وإعادة تحميل ملفات الترجمة تلقائياً أثناء التطوير (hot reload).
pubspec.yaml
dependencies:
  easy_localization: ^3.0.7
  flutter_localizations:
    sdk: flutter
2

إنشاء ملفات ترجمة ARB

تُعد ملفات ARB (Application Resource Bundle) صيغة التوطين القياسية في Flutter. يحتوي كل ملف أزواج مفتاح/قيمة مع بيانات وصفية اختيارية تصف العناصر النائبة وقواعد الجمع وسياق المترجم. أنشئ ملفاً واحداً لكل locale ضمن المجلد 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

تهيئة التطبيق

غلّف تطبيقك بالـ widget EasyLocalization. يدير حالة locale، ويحمّل الترجمات من ملفات الأصول لديك، ويوفر delegates المحلية التي يحتاجها MaterialApp. خصائص delegate الثلاث المطلوبة هي: localizationsDelegates وsupportedLocales وlocale؛ وجميعها متاحة عبر امتدادات السياق.

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(). يجب انتظارها بعد WidgetsFlutterBinding.ensureInitialized() وقبل runApp().
4

ترجمة Widgets

استخدم طريقة الامتداد .tr() على مفاتيح السلاسل للحصول على النص المترجم في أي Widget. ولصيغ الجمع، استخدم .plural() مع قيمة العدد. توفر easy_localization كلاً من صيغة امتداد السلسلة ('key'.tr()) وصيغة طريقة السياق (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) أن widget ‏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 التخطيط بالكامل تلقائياً عندما تكون locale لغة تُكتب من اليمين إلى اليسار (العربية، العبرية، الفارسية، الأردية). لكن يجب أن يستخدم كودك widgets وخصائص مدركة للاتجاه كي يعمل الانعكاس بشكل صحيح. استبدل 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 التطبيق مؤقتاً إلى العربية (Locale('ar')). سيعكس Flutter واجهة المستخدم بالكامل؛ ستفتح أدراج التنقل من اليمين، وستنقلب أسهم الرجوع، وسيتم محاذاة النص إلى اليمين. استخدم EdgeInsetsDirectional وAlignmentDirectional وBorderRadiusDirectional لضمان انعكاس تخطيطاتك المخصصة بشكل صحيح.
7

سلاسل تراجع locale الذكية

افتراضياً، عندما تكون ترجمة 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 قبل locale الافتراضي.
8

أتمتة الترجمات

بعد اكتمال إعداد التوطين، ترجم ملفات ARB باستخدام الذكاء الاصطناعي. في IDE، اطلب من مساعدك بالذكاء الاصطناعي ترجمة ملف ARB المصدر، أو استخدم i18n Agent CLI ضمن مسار 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. اختبر واجهة المستخدم بترجمات وهمية باستخدام 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());

اطّلع على دليل Locale Fallback لدينا للحصول على القائمة الكاملة للأُطر المدعومة و75 سلسلة مدمجة. Learn more →

الأسئلة الشائعة