لماذا يحتاج تطبيق Rust الخاص بك إلى التدويل (وكيف لا تفسد الأمور)
\n\nلقد كتبت محللًا مثاليًا بلغة Rust. إنه يعمل أسرع من البرق ويستهلك ذاكرة أقل من عصفور متواضع. لكن هناك مشكلة واحدة: نصك يظهر باللغة الإنجليزية فقط. والمستخدمون من طوكيو وبرلين وسانت بطرسبرغ يريدون رؤية الواجهة بلغتهم الأم.
\n\nالتدويل (i18n) ليس مجرد "ترجمة الأزرار". إنه قرار معماري يمكن أن يحول كودك إلى منشئ أنيق أو إلى مكب نفايات جحيمي من if-else. في هذه المقالة، سنشرح كيفية تنفيذ التدويل في Rust بشكل صحيح: بدون ألم، مع أمثلة، ومع احترام لعقول المطور المستقبلي (عقلك).
\n\nسنتحدث عن نهجين قويين: مكتبة fluent (توصية من Mozilla) و rust-i18n الكلاسيكي القائم على gettext. في نهاية المقال، ستتمكن من إضافة دعم اللغة الروسية والإنجليزية وأي لغة أخرى إلى أداتك في 15 دقيقة.
خطة المعركة: ما هو i18n و l10n في عالم Rust
\n\nقبل أن ننغمس في الكود، دعونا نتفق على المصطلحات:
\n- \n
- التدويل (i18n) — إعداد الكود للترجمة. تقوم بإخراج جميع السلاسل النصية إلى ملفات منفصلة وتعليم البرنامج كيفية تحميل اللغة المناسبة. \n
- التوطين (l10n) — الترجمة نفسها. إنشاء ملفات لكل لغة. \n
- اللغة المحلية — مزيج من اللغة والمنطقة (مثل
ru-RU،en-US،de-DE). \n
في Rust، هناك عدة طرق. سننظر في اثنتين من الأكثر شيوعًا:
\n- \n
- Fluent — نظام حديث من Mozilla. قوي، مرن، مع دعم الجمع والسياق. \n
- rust-i18n — وحدات ماكرو مستوحاة من Ruby on Rails. أبسط، لكنها أقل مرونة. \n
الاختيار يعتمد على المهمة. بالنسبة لأدوات CLI، فإن rust-i18n كافٍ. بالنسبة لتطبيقات الويب أو واجهات المستخدم الرسومية المعقدة (مثل Tauri)، من الأفضل استخدام Fluent.
الطريقة رقم 1: تدويل Rust باستخدام Fluent (النهج الاحترافي)
\n\nتثبيت وإعداد Fluent
\n\nلنقم بإنشاء مشروع جديد وإضافة التبعيات:
\n\ncargo new i18n_demo\ncd i18n_demo\ncargo add fluent fluent-bundle fluent-langtag unic-langid\n\n\nسيكون هيكل الملفات كالتالي:
\n\nمشروع/\n├── Cargo.toml\n├── locales/\n│ ├── ru/\n│ │ └── main.ftl\n│ └── en/\n│ └── main.ftl\n└── src/\n └── main.rs\n\n\nإنشاء ملفات التوطين (FTL)
\n\nيستخدم Fluent تنسيق FTL (قائمة ترجمة Fluent). إنه النقاء والقوة:
\n\nlocales/en/main.ftl:
\ngreeting = Hello, { $name }!\ngoodbye = Goodbye\napples = { $count ->\n [one] { $count } apple\n [few] { $count } apples\n *[other] { $count } apples\n}\n\n\nlocales/ru/main.ftl:
\ngreeting = Привет, { $name }!\ngoodbye = До свидания\napples = { $count ->\n [one] { $count } яблоко\n [few] { $count } яблока\n [many] { $count } яблок\n *[other] { $count } яблок\n}\n\n\nلاحظ الجمع. في اللغة الروسية، هناك أربع صيغ (واحد، اثنان-أربعة، خمسة-عشرون، والباقي)، بينما في الإنجليزية هناك اثنتان (واحد، والباقي). يتعامل Fluent مع هذا تلقائيًا.
\n\nكتابة كود Rust لتحميل اللغات المحلية
\n\nالآن الجزء الممتع — الكود الذي سيختار اللغة المناسبة بناءً على نظام المستخدم:
\n\nuse fluent_bundle::{FluentBundle, FluentResource};\nuse unic_langid::LanguageIdentifier;\nuse std::fs;\nuse std::path::Path;\n\nfn load_locale(locale: &str) -> FluentBundle<FluentResource> {\n let langid: LanguageIdentifier = locale.parse().expect("لغة محلية غير صالحة");\n let mut bundle = FluentBundle::new(vec![langid]);\n\n let file_path = format!("locales/{}/main.ftl", locale);\n let source = fs::read_to_string(&file_path)\n .expect(&fo