الفرق بين المراجعتين لصفحة: «Rails/i18n»
جميل-بيلوني (نقاش | مساهمات) لا ملخص تعديل |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(3 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة) | |||
سطر 11: | سطر 11: | ||
* إخبار ريلز بكيفيّة إعداد اللغة المحليّة والمحافظة عليها وتبديلها. | * إخبار ريلز بكيفيّة إعداد اللغة المحليّة والمحافظة عليها وتبديلها. | ||
في عملية توطين تطبيقك، سترغب غالبًا بالقيام بالأشياء الثلاثة التالية: | في عملية توطين تطبيقك، سترغب غالبًا بالقيام بالأشياء الثلاثة التالية: | ||
* استبدال أو استكمال الإعدادات الافتراضية لريلز؛ على سبيل المثال، تنسيقات التاريخ والوقت، وأسماء الشهور، وأسماء نماذج | * استبدال أو استكمال الإعدادات الافتراضية لريلز؛ على سبيل المثال، تنسيقات التاريخ والوقت، وأسماء الشهور، وأسماء نماذج [[Rails/active record|Active Record]]، ...إلخ. | ||
* تجريد السلاسل النصيّة في تطبيقك إلى قواميس مفتاحي؛ مثلًا الرسائل الوامضة (flash messages)، والنص الثابت في عروضك، ...إلخ. | * تجريد السلاسل النصيّة في تطبيقك إلى قواميس مفتاحي؛ مثلًا الرسائل الوامضة (flash messages)، والنص الثابت في عروضك، ...إلخ. | ||
* تخزين القواميس الناتجة في مكان ما. | * تخزين القواميس الناتجة في مكان ما. | ||
سطر 19: | سطر 19: | ||
* كيف تعمل الواجهة البرمجية I18n في ريلز. | * كيف تعمل الواجهة البرمجية I18n في ريلز. | ||
* كيفيّة استخدام I18n بشكل صحيح في تطبيق RESTful بطرق مختلفة. | * كيفيّة استخدام I18n بشكل صحيح في تطبيق RESTful بطرق مختلفة. | ||
* كيفيّة استخدام I18n لترجمة أخطاء [[Rails/active record | * كيفيّة استخدام I18n لترجمة أخطاء [[Rails/active record|Active Record]] أو مواضيع البريد الإلكتروني [[Rails/action mailer|Action Mailer]]. | ||
* بعض الأدوات الأخرى لمواصلة ترجمة تطبيقك. | * بعض الأدوات الأخرى لمواصلة ترجمة تطبيقك. | ||
'''ملاحظة''': يوفّر لك إطار روبي I18n جميع الوسائل اللازمة لتدويل / توطين تطبيقك ريلز. يمكنك أيضًا استخدام مختلف الجواهر المتاحة لإضافة وظائف أو ميزات إضافية. انظر [https://github.com/svenfuchs/rails-i18n جوهرة ريلز i18n] لمزيد من المعلومات. | '''ملاحظة''': يوفّر لك إطار روبي I18n جميع الوسائل اللازمة لتدويل / توطين تطبيقك ريلز. يمكنك أيضًا استخدام مختلف الجواهر المتاحة لإضافة وظائف أو ميزات إضافية. انظر [https://github.com/svenfuchs/rails-i18n جوهرة ريلز i18n] لمزيد من المعلومات. | ||
== | == كيفية عمل الواجهة البرمجية I18n في ريلز == | ||
التدويل مشكلة معقدة. تختلف اللغات الطبيعية بطرق كثيرة (مثلًا في قواعد الجمع) بحيث يصعب توفير أدوات لحل جميع المشاكل في وقت واحد. لهذا السبب، تركّز الواجهة I18n البرمجية في ريلز على: | التدويل مشكلة معقدة. تختلف اللغات الطبيعية بطرق كثيرة (مثلًا في قواعد الجمع) بحيث يصعب توفير أدوات لحل جميع المشاكل في وقت واحد. لهذا السبب، تركّز الواجهة I18n البرمجية في ريلز على: | ||
* تقديم الدعم للغة الإنجليزية واللغات المماثلة مباشرة. | * تقديم الدعم للغة الإنجليزية واللغات المماثلة مباشرة. | ||
* تسهيل تخصيص كل شيء للغات الأخرى وتوسيعها. | * تسهيل تخصيص كل شيء للغات الأخرى وتوسيعها. | ||
دُوّلت (internationalized) كل سلسلة نصية ثابتة في إطار ريلز كجزء من هذا الحل - مثل رسائل التحقق من | دُوّلت (internationalized) كل سلسلة نصية ثابتة في إطار ريلز كجزء من هذا الحل - مثل رسائل التحقق من [[Rails/active record|Active Record]]، وتنسيقات الوقت والتاريخ. يعني توطين تطبيق ريلز تعريف القيم المترجمة لهذه السلاسل في اللغات المطلوبة. | ||
لتوطين وتخزين وتحديث المحتوى في تطبيقك (ترجمة منشورات مدوّنة مثلًا)، راجع قسم ترجمة محتوى النماذج. | لتوطين وتخزين وتحديث المحتوى في تطبيقك (ترجمة منشورات مدوّنة مثلًا)، راجع قسم ترجمة محتوى النماذج. | ||
سطر 34: | سطر 34: | ||
تُقسّم جوهرة روبي I18n إلى قسمين: | تُقسّم جوهرة روبي I18n إلى قسمين: | ||
* واجهة برمجة التطبيقات العامة لإطار عمل i18n - وحدة روبي مع التوابع العامّة التي تحدد كيّفية عمل المكتبة. | * واجهة برمجة التطبيقات العامة لإطار عمل i18n - وحدة روبي مع التوابع العامّة التي تحدد كيّفية عمل المكتبة. | ||
* | * واجهة خلفية افتراضيّة (والتي تسمى عمدًا الواجهة الخلفية Simple "الواجهة الخلفية البسيطة") تُنفّذ هذه التوابع. | ||
بصفتك مستخدمًا، عليك دائمًا الوصول إلى التوابع العامّة عبر الوحدة I18n فقط، ولكن من المفيد معرفة قدرات | بصفتك مستخدمًا، عليك دائمًا الوصول إلى التوابع العامّة عبر الوحدة I18n فقط، ولكن من المفيد معرفة قدرات الواجهة الخلفية. | ||
من الممكن تبديل الواجهة الخلفية | من الممكن تبديل الواجهة الخلفية Simple التي شُحنت بأخرى أكثر فعاليّة، والتي ستخزن بيانات الترجمة في قاعدة بيانات علائقيّة، أو قاموس GetText أو ما شابه ذلك. انظر القسم استخدام الخلفيات المختلفة أدناه. | ||
=== الواجهة I18n البرمجية العامة === | === الواجهة I18n البرمجية العامة === | ||
سطر 67: | سطر 67: | ||
en: | en: | ||
hello: "Hello world" | hello: "Hello world" | ||
</syntaxhighlight>يعني هذا أنه في اللّغة <code>en:</code>، سيعيَّن المفتاح "hello" إلى السلسلة النصيّة "Hello world". تُدوّل كل سلسلة داخل ريلز بهذه الطريقة؛ انظر على سبيل المثال لرسائل التحقق من صحّة | </syntaxhighlight>يعني هذا أنه في اللّغة <code>en:</code>، سيعيَّن المفتاح "hello" إلى السلسلة النصيّة "Hello world". تُدوّل كل سلسلة داخل ريلز بهذه الطريقة؛ انظر على سبيل المثال لرسائل التحقق من صحّة [[Rails/active model|Active Model]] في الملف [https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml activemodel/lib/active_model/locale/en.yml] أو تنسيقات الوقت والتاريخ في الملف [https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml activesupport/lib/active_support/locale/en.yml]. يمكنك استخدام YAML أو [[Ruby/Hash|جداول Hash]] القياسيّة لتخزين الترجمات في الواجهة الخلفية الافتراضية (الواجهة الخلفية Simple). | ||
ستستخدم مكتبة I18n اللغة الإنجليزية كلغة افتراضية أي إن لم تُعيّن لغة مختلفة سيُستخدم <code>en:</code> للبحث عن الترجمات. | ستستخدم مكتبة I18n اللغة الإنجليزية كلغة افتراضية أي إن لم تُعيّن لغة مختلفة سيُستخدم <code>en:</code> للبحث عن الترجمات. | ||
'''ملاحظة''': تستعمل المكتبة i18n "مقاربة واقعية" (pragmatic approach) لمفاتيح | '''ملاحظة''': تستعمل المكتبة i18n "مقاربة واقعية" (pragmatic approach) لمفاتيح المحليَّة (بعد [https://groups.google.com/forum/#!topic/rails-i18n/FN7eLH2-lHA بعض النقاش]) بأخذها جزء اللّغة فقط بالحسبان مثل <code>en:</code> و <code>pl:</code> لا جزء المنطقة معها مثل <code>en-US:</code> أو <code>en-GB:</code> التي تُستخدَم عادةً لفصل "اللغات" و "الإعدادات الإقليميّة" أو "اللهجات". تستخدم عدّة تطبيقات دوليّة عنصر اللغة فقط مثل: <code>cs:</code> أو <code>th:</code> أو <code>es:</code> (للتشيكيّة، والتايلاندية والإسبانية على التوالي). ومع ذلك تظل هناك أيضًا اختلافات إقليميّة قد تكون مهمّة داخل مجموعات لغويّة مختلفة. على سبيل المثال في المحليَّة <code>en-US:</code> رمز العملة هو <code>$</code> بينما هو <code>£</code> في <code>en-GB:</code>. لا شيء يمنعك من فصل الإعدادات الإقليمية والأخرى بهذه الطريقة: عليك فقط توفير إعداد اللغة المحليّة "الإنجليزية - المملكة المتحدة" الكاملة في قاموس <code>en-GB:</code>. | ||
مسار تحميل الترجمات (I18n.load_path) هو مصفوفة مسارات إلى الملفّات التي ستُحمّل تلقائيًا. يتيح إعداد هذا المسار تخصيص بنية مُجلّد الترجمة ومخطّط تسمية الملفّات. | مسار تحميل الترجمات (I18n.load_path) هو مصفوفة مسارات إلى الملفّات التي ستُحمّل تلقائيًا. يتيح إعداد هذا المسار تخصيص بنية مُجلّد الترجمة ومخطّط تسمية الملفّات. | ||
سطر 93: | سطر 93: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== إدارة | === إدارة المحلية عبر الطلبات === | ||
تُستخدم | تُستخدم المحليّة الإفتراضيّة لكافة الترجمات ما لم يُضبط <code>I18n.locale</code> بشكل صريح. | ||
من المحتمل أن يحتاج التطبيق المترجم إلى توفير الدعم للعديد من اللغات. ولتحقيق ذلك يجب تعيين | من المحتمل أن يحتاج التطبيق المترجم إلى توفير الدعم للعديد من اللغات. ولتحقيق ذلك يجب تعيين المحليّة في بداية كل طلب بحيث تُترجم جميع السلاسل النصية باستخدام اللغة المطلوبة خلال دورة حياة هذا الطلب. | ||
يمكن تعيين اللغة في <code>before_action</code> في المتحكم <code>ApplicationController</code>:<syntaxhighlight lang="rails"> | يمكن تعيين اللغة في <code>before_action</code> في المتحكم <code>ApplicationController</code>:<syntaxhighlight lang="rails"> | ||
سطر 108: | سطر 108: | ||
يمكن تعيين اللغة باستخدام طرق مختلفة كثيرة. | يمكن تعيين اللغة باستخدام طرق مختلفة كثيرة. | ||
==== ضبط | ==== ضبط المحلية من اسم النطاق (Domain Name) ==== | ||
أحد الخيارات لديك هو تعيين اللغة من اسم النطاق حيث يشتغل تطبيقك. نريد مثلًا أن يُحمّل www.example.com اللغة الإنجليزية (أو اللغة الإفتراضيّة)، و www.example.es اللغة الإسبانيّة. بحيث يُستخدم اسم النطاق ذو المستوى الأعلى لإعداد | أحد الخيارات لديك هو تعيين اللغة من اسم النطاق حيث يشتغل تطبيقك. نريد مثلًا أن يُحمّل www.example.com اللغة الإنجليزية (أو اللغة الإفتراضيّة)، و www.example.es اللغة الإسبانيّة. بحيث يُستخدم اسم النطاق ذو المستوى الأعلى لإعداد المحليَّة. ولهذا الأسلوب العديد من المزايا: | ||
* يصبح المحليَّة جزءًا واضحًا من عنوان URL. | * يصبح المحليَّة جزءًا واضحًا من عنوان URL. | ||
* يفهم الناس تلقائيًّا بأي لغة سيُعرض المحتوى. | * يفهم الناس تلقائيًّا بأي لغة سيُعرض المحتوى. | ||
سطر 146: | سطر 146: | ||
لهذا الحل مزايا سالفة الذكر ومع ذلك قد لا تقدر أو لا ترغب في توفير توطينات مختلفة (بمعنى "نُسخ من اللغات") في نطاقات مختلفة. سيكون الحل البديهي تضمين رمز المحليَّة في المعاملات <code>params</code> للعنوان URL (أو مسار الطلب). | لهذا الحل مزايا سالفة الذكر ومع ذلك قد لا تقدر أو لا ترغب في توفير توطينات مختلفة (بمعنى "نُسخ من اللغات") في نطاقات مختلفة. سيكون الحل البديهي تضمين رمز المحليَّة في المعاملات <code>params</code> للعنوان URL (أو مسار الطلب). | ||
==== ضبط | ==== ضبط المحليَّة من معاملات العنوان URL ==== | ||
الطريقة الأكثر شيوعًا لتعيين (وتمرير) المحليَّة هي تضمينه في معاملات URL (أي <code>params</code>)، كما فعلنا في التابع <code>before_action</code> من <code>I18n.locale = params[:locale]</code> في المثال الأول. نرغب في هذه الحالة في الحصول على عناوين URL مثل www.example.com/books?locale=ja أو www.example.com/ja/books. | الطريقة الأكثر شيوعًا لتعيين (وتمرير) المحليَّة هي تضمينه في معاملات URL (أي <code>params</code>)، كما فعلنا في التابع <code>before_action</code> من <code>I18n.locale = params[:locale]</code> في المثال الأول. نرغب في هذه الحالة في الحصول على عناوين URL مثل www.example.com/books?locale=ja أو www.example.com/ja/books. | ||
سطر 160: | سطر 160: | ||
{ locale: I18n.locale } | { locale: I18n.locale } | ||
end | end | ||
</syntaxhighlight>كل تابع مساعد يعتمد على <code>url_for</code> (مثل مساعدي المسارات المسمّاة مثل <code>root_path</code> أو <code>root_url</code>، ومسارات الموارد مثل <code>books_path</code> أو <code>books_url</code> ...إلخ.) سيشمل الآن تلقائيًا | </syntaxhighlight>كل تابع مساعد يعتمد على <code>url_for</code> (مثل مساعدي المسارات المسمّاة مثل <code>root_path</code> أو <code>root_url</code>، ومسارات الموارد مثل <code>books_path</code> أو <code>books_url</code> ...إلخ.) سيشمل الآن تلقائيًا المحليَّة في سلسلة الاستعلام بالشكل <nowiki>http://localhost:3001/?locale=ja</nowiki>. | ||
قد تكون راضيًا عن هذا. إلا أنّه يؤثّر على سهولة قراءة عناوين URL عند "وضع" | |||
قد تكون راضيًا عن هذا. إلا أنّه يؤثّر على سهولة قراءة عناوين URL عند "وضع" المحليَّة في نهاية كل عنوان URL في تطبيقك. علاوة على ذلك من الناحيّة المعمارية، عادةً ما تكون اللغة أو المحليَّة أعلى هرميًّا من الأجزاء الأخرى بنطاق التطبيق: ويجب أن تعكس عناوين URL ذلك. | |||
سترغب غالبًا أن تبدو عناوين URL بالشكل: <nowiki>http://www.example.com/en/books</nowiki> (الذي يُحمّل اللغة الإنجليزية) و <nowiki>http://www.example.com/nl/books</nowiki> (الذي يُحمّل اللغة الهولندية). يمكن تحقيق ذلك من خلال استراتيجية " إعادة تعريف <code>default_url_options</code> " أعلاه: عليك فقط إعداد مساراتك باستخدام <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Routing/Mapper/Scoping.html scope]</code>:<syntaxhighlight lang="rails"> | سترغب غالبًا أن تبدو عناوين URL بالشكل: <nowiki>http://www.example.com/en/books</nowiki> (الذي يُحمّل اللغة الإنجليزية) و <nowiki>http://www.example.com/nl/books</nowiki> (الذي يُحمّل اللغة الهولندية). يمكن تحقيق ذلك من خلال استراتيجية " إعادة تعريف <code>default_url_options</code> " أعلاه: عليك فقط إعداد مساراتك باستخدام <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Routing/Mapper/Scoping.html scope]</code>:<syntaxhighlight lang="rails"> | ||
سطر 170: | سطر 171: | ||
</syntaxhighlight>الآن، عند استدعاءك التابع <code>books_path</code>، يجب أن تحصل على "/en/books" (للغة الافتراضيّة). يجب أن يُحمّل عنوان URL مثل <nowiki>http://localhost:3001/nl/books</nowiki> باللغة الهولندية، وبعد ذلك يجب أن تعيد النداءات إلى <code>books_path</code> السلسلة "/nl/books" (لأن اللغة تغيّرت). | </syntaxhighlight>الآن، عند استدعاءك التابع <code>books_path</code>، يجب أن تحصل على "/en/books" (للغة الافتراضيّة). يجب أن يُحمّل عنوان URL مثل <nowiki>http://localhost:3001/nl/books</nowiki> باللغة الهولندية، وبعد ذلك يجب أن تعيد النداءات إلى <code>books_path</code> السلسلة "/nl/books" (لأن اللغة تغيّرت). | ||
'''تحذير''': نظرًا لأن القيمة المعادة من <code>default_url_options</code> تُخّزن مؤقتًا لكل طلب، لا يمكن توليد عناوين URL في مُحدّد | '''تحذير''': نظرًا لأن القيمة المعادة من <code>default_url_options</code> تُخّزن مؤقتًا لكل طلب، لا يمكن توليد عناوين URL في مُحدّد المحليَّة (locale selector) تستدعي مساعدين بشكل حلقي (loop) تُعيِّن <code>I18n.locale</code> المقابل في كل تكرار (iteration). اترك بدلًا من ذلك <code>I18n.locale</code> دون تغيير، أو مرّر الخيار <code>locale:</code> بشكل صريح إلى المساعد، أو عدّل <code>request.original_fullpath</code>. | ||
إن لم ترغب في فرض استخدام محليَّة في مساراتك، تستطيع استخدام نطاق مسار اختياري (يُشار إليه بالأقواس) على النحو التالي:<syntaxhighlight lang="rails"> | إن لم ترغب في فرض استخدام محليَّة في مساراتك، تستطيع استخدام نطاق مسار اختياري (يُشار إليه بالأقواس) على النحو التالي:<syntaxhighlight lang="rails"> | ||
سطر 188: | سطر 189: | ||
'''ملاحظة''': ألقِ نظرة على مختلف الجواهر التي تبسّط العمل مع المسارات: <code>[https://github.com/svenfuchs/routing-filter/tree/master routing_filter]</code> و <code>[https://github.com/francesc/rails-translate-routes rails-translate-routes]</code> و <code>[https://github.com/enriclluelles/route_translator route_translator]</code>. | '''ملاحظة''': ألقِ نظرة على مختلف الجواهر التي تبسّط العمل مع المسارات: <code>[https://github.com/svenfuchs/routing-filter/tree/master routing_filter]</code> و <code>[https://github.com/francesc/rails-translate-routes rails-translate-routes]</code> و <code>[https://github.com/enriclluelles/route_translator route_translator]</code>. | ||
==== ضبط | ==== ضبط المحلية من تفضيلات المستخدم ==== | ||
قد يسمح تطبيق مع مستخدمين مُستَوثَقين لهم بضبط تفضيلات لغة من خلال واجهة التطبيق. بهذه الطريقة يظل تفضيله المحدّد للغة في قاعدة البيانات ويُستخدم لتحديد | قد يسمح تطبيق مع مستخدمين مُستَوثَقين لهم بضبط تفضيلات لغة من خلال واجهة التطبيق. بهذه الطريقة يظل تفضيله المحدّد للغة في قاعدة البيانات ويُستخدم لتحديد المحليَّة للطلبات المصادق عليها من طرف ذلك المستخدم.<syntaxhighlight lang="rails"> | ||
def set_locale | def set_locale | ||
I18n.locale = current_user.try(:locale) || I18n.default_locale | I18n.locale = current_user.try(:locale) || I18n.default_locale | ||
سطر 218: | سطر 219: | ||
هذه الطريقة بشكل عام أقل موثوقية بكثير من استخدام ترويسة اللغة ولا يوصى بها لمعظم تطبيقات الويب. | هذه الطريقة بشكل عام أقل موثوقية بكثير من استخدام ترويسة اللغة ولا يوصى بها لمعظم تطبيقات الويب. | ||
==== تخزين | ==== تخزين المحلية من الجلسة أو من ملفات تعريف الارتباط (Cookies) ==== | ||
'''تحذير''': قد تميل إلى تخزين | '''تحذير''': قد تميل إلى تخزين المحليَّة المختارة في جلسة عمل أو ملف تعريف ارتباط (Cookie) لكن لا تفعل هذا. يجب أن تكون المحليَّة واضحة وجزءًا من عنوان URL. هكذا لن تخالف افتراضات الناس الأساسيّة حول الويب نفسه: إن أرسلت عنوان URL إلى صديق، فيجب أن يشاهد نفس الصفحة والمحتوى الذي تراه. أفضل مصطلح لهذا الأمر هو أنك [[wikipedia:Representational_State_Transfer|RESTful]]. اقرأ المزيد عن مقاربة RESTful في [https://www.infoq.com/articles/rest-introduction مقالات ستيفان تيلكوف] (Stefan Tilkov). في بعض الأحيان، هناك استثناءات لهذه القاعدة وتلك التي سنناقشها أدناه. | ||
== التدويل والتوطين == | == التدويل والتوطين == | ||
سطر 252: | سطر 253: | ||
<p><%= flash[:notice] %></p> | <p><%= flash[:notice] %></p> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[ملف:demo_untranslated_rails.png | [[ملف:demo_untranslated_rails.png|تصغير|500بك|عرض لتطبيق ريلز بسيط غير مترجم.|بدون]] | ||
=== تجريد الشيفرات البرمجية المترجمة (Abstracting Localized Code) === | === تجريد الشيفرات البرمجية المترجمة (Abstracting Localized Code) === | ||
سطر 268: | سطر 269: | ||
</syntaxhighlight>والآن، عندما يُصيّر هذا العرض، سيُظهر لك رسالة خطأ تخبرك بأن ترجمات المفتاحين <code>:hello_world</code> و <code>:hello_flash</code> مفقودة. | </syntaxhighlight>والآن، عندما يُصيّر هذا العرض، سيُظهر لك رسالة خطأ تخبرك بأن ترجمات المفتاحين <code>:hello_world</code> و <code>:hello_flash</code> مفقودة. | ||
[[ملف:demo_translation_missing_rails.png | [[ملف:demo_translation_missing_rails.png|تصغير|500بك|ظهور رسالة خطأ تخبر بأن هنالك ترجمات مفقودة.|بدون]] | ||
'''ملاحظة''': تضيف ريلز المساعد <code>t</code> (أي <code>translate</code> [ترجمة]) لوحدة العرض، لذا لا تحتاج إلى كتابة <code>I18n.t</code> في كل مرة. إضافة إلى ذلك، هذا المساعد سيلتقط الترجمات ويغلف رسالة الخطأ الناتجة في <code><nowiki><span class="translation_missing"></nowiki></code>. | '''ملاحظة''': تضيف ريلز المساعد <code>t</code> (أي <code>translate</code> [ترجمة]) لوحدة العرض، لذا لا تحتاج إلى كتابة <code>I18n.t</code> في كل مرة. إضافة إلى ذلك، هذا المساعد سيلتقط الترجمات ويغلف رسالة الخطأ الناتجة في <code><nowiki><span class="translation_missing"></nowiki></code>. | ||
سطر 284: | سطر 285: | ||
hello_flash: Ahoy Flash | hello_flash: Ahoy Flash | ||
</syntaxhighlight>تستخدم الترجمات المحليَّة <code>en:</code> نظرًا لعدم تغيير <code>default_locale</code>، وتصيّر الاستجابة السلاسل النصية باللغة الإنجليزية: | </syntaxhighlight>تستخدم الترجمات المحليَّة <code>en:</code> نظرًا لعدم تغيير <code>default_locale</code>، وتصيّر الاستجابة السلاسل النصية باللغة الإنجليزية: | ||
[[ملف:demo_translated_en_rails.png | [[ملف:demo_translated_en_rails.png|تصغير|500بك|ظهور السلسلتين النصيتين باللغة الإنجليزية لأن المحلية الافتراضية لم تتغير وهي en:.|بدون]] | ||
إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي | إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي http://localhost:3000?locale=pirate<nowiki/>)، فستصير السلسلتين النصيتين إلى ما يقابلهما لتلك المحلية: | ||
[[ملف:demo_translated_pirate_rails.png | [[ملف:demo_translated_pirate_rails.png|تصغير|500بك|تغيّر السلاسل النصية بتغير المحلية إلى pirate.|بدون]] | ||
'''ملاحظة''': عليك إعادة تشغيل الخادم عند إضافة ملفّات جديدة للمحليّة. | '''ملاحظة''': عليك إعادة تشغيل الخادم عند إضافة ملفّات جديدة للمحليّة. | ||
سطر 350: | سطر 351: | ||
'''ملاحظة''': الكلمات الرئيسية مثل <code>default</code> و <code>scope</code> محجوزةً ولا يمكن استخدامها كأسماء للمتغيرات. وإن استُخدمت، يُرفَع الاستثناء <code>I18n::ReservedInterpolationKey</code>. إن توقّعت ترجمة متغير استيفاء (interpolation) ولكنه لم يُمرّر إلى <code>translate.</code>، يُرفع الاستثناء <code>I18n::MissingInterpolationArgument</code>. | '''ملاحظة''': الكلمات الرئيسية مثل <code>default</code> و <code>scope</code> محجوزةً ولا يمكن استخدامها كأسماء للمتغيرات. وإن استُخدمت، يُرفَع الاستثناء <code>I18n::ReservedInterpolationKey</code>. إن توقّعت ترجمة متغير استيفاء (interpolation) ولكنه لم يُمرّر إلى <code>translate.</code>، يُرفع الاستثناء <code>I18n::MissingInterpolationArgument</code>. | ||
=== إضافة تنسيقات التاريخ / الوقت === | === إضافة تنسيقات التاريخ/الوقت === | ||
حسنًا، فلنقم الآن بإضافة طابع زمني إلى العرض حتى نتمكّن من عرض ميزة توطين التاريخ/الوقت أيضًا. لتوطين تنسيق الوقت تُمرّر كائن الوقت إلى <code>I18n.l</code> أو (يُفضّل) استخدام المساعد <code>l.</code>. يمكنك اختيار تنسيق عبر تمرير الخيار <code>format:</code> - يُستخدم التنسيق <code>default:</code> افتراضيًا.<syntaxhighlight lang="html"> | حسنًا، فلنقم الآن بإضافة طابع زمني إلى العرض حتى نتمكّن من عرض ميزة توطين التاريخ/الوقت أيضًا. لتوطين تنسيق الوقت تُمرّر كائن الوقت إلى <code>I18n.l</code> أو (يُفضّل) استخدام المساعد <code>l.</code>. يمكنك اختيار تنسيق عبر تمرير الخيار <code>format:</code> - يُستخدم التنسيق <code>default:</code> افتراضيًا.<syntaxhighlight lang="html"> | ||
# app/views/home/index.html.erb | # app/views/home/index.html.erb | ||
سطر 363: | سطر 364: | ||
short: "arrrround %H'ish" | short: "arrrround %H'ish" | ||
</syntaxhighlight>بحيث يمنحك ذلك: | </syntaxhighlight>بحيث يمنحك ذلك: | ||
[[ملف:demo_localized_pirate_rails.png | [[ملف:demo_localized_pirate_rails.png|تصغير|500بك|شكل واجهة لعرض للتطبيق بعد إضافة تنسيق زمني.|بدون]] | ||
'''تنبيه''': قد تحتاج الآن لإضافة المزيد من تنسيقات التاريخ/الوقت بالترتيب لجعل عمل الواجهة الخلفية I18n مثل المتوقّع (على الأقل بالنسبة للغة "القرصنة"). | '''تنبيه''': قد تحتاج الآن لإضافة المزيد من تنسيقات التاريخ/الوقت بالترتيب لجعل عمل الواجهة الخلفية I18n مثل المتوقّع (على الأقل بالنسبة للغة "القرصنة"). | ||
هناك طبعًا فرصة كبيرة أن شخصًا ما أنجز بالفعل كل هذا العمل عن طريق ترجمة افتراضيّات ريلز للغتك المحليّة. راجع [https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale مستودع rails-i18n في GitHub] لأرشيف من ملفات | هناك طبعًا فرصة كبيرة أن شخصًا ما أنجز بالفعل كل هذا العمل عن طريق ترجمة افتراضيّات ريلز للغتك المحليّة. راجع [https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale مستودع rails-i18n في GitHub] لأرشيف من ملفات المحليَّة المختلفة. عند وضع مثل هذا الملف (أو الملفّات) في المجلّد /config/locales، سيكون جاهزًا للاستخدام تلقائيًا. | ||
=== قواعد | === قواعد العطف لمحلية أخرى === | ||
يسمح لك ريلز بتحديد قواعد | يسمح لك ريلز بتحديد قواعد العطف (أي inflection مثل قواعد المُفرد والجمع) للإعدادات المحليّة غير الإنجليزية. يمكنك تعريف هذه القواعد لعدّة لغات في config/initializers/inflections.rb. يحتوي المُهيئ على مثال افتراضي لتحديد قواعد إضافيّة للغة الإنجليزية؛ اتبع ذاك التنسيق للغات أخرى كما تراه مناسبا. | ||
=== العروض المترجمة === | === العروض المترجمة === | ||
فلنفترض أنك تمتلك وحدة التحكّم <code>BooksController</code> في تطبيقك. يُصيّر الإجراء <code>index</code> المحتوى في القالب app/views/books/index.html.erb. عندما تضع المتغير المترجم من هذا القالب: index.es.html.erb، سيُصيّر ريلز المحتوى في هذا القالب عندما تُعيّن | فلنفترض أنك تمتلك وحدة التحكّم <code>BooksController</code> في تطبيقك. يُصيّر الإجراء <code>index</code> المحتوى في القالب app/views/books/index.html.erb. عندما تضع المتغير المترجم من هذا القالب: index.es.html.erb، سيُصيّر ريلز المحتوى في هذا القالب عندما تُعيّن المحليَّة إلى <code>es:</code>. سيُستخدم العرض العادي index.html.erb عندما تُعيّن المحليَّة إلى المحلية الافتراضيّة. (قد تجلب إصدارات ريلز المستقبليّة هذا التوطين التلقائي للموارد في <code>public</code> ...إلخ.) | ||
يمكنك الاستفادة من هذه الميزة عند العمل مع الكثير من المحتوى الثابت؛ مثلًا، الذي سيكون من السذاجة لوضعه داخل قواميس YAML أو روبي. مع ذلك ضع في الحسبان أن من اللازم نشر أي تغيير ترغب فيه بالنموذج لاحقًا لجميع القوالب. | يمكنك الاستفادة من هذه الميزة عند العمل مع الكثير من المحتوى الثابت؛ مثلًا، الذي سيكون من السذاجة لوضعه داخل قواميس YAML أو روبي. مع ذلك ضع في الحسبان أن من اللازم نشر أي تغيير ترغب فيه بالنموذج لاحقًا لجميع القوالب. | ||
سطر 401: | سطر 402: | ||
|-----es.rb | |-----es.rb | ||
|-----en.rb | |-----en.rb | ||
</syntaxhighlight>تستطيع بهذه الطريقة فصل أسماء النموذج وخاصيات النموذج من النص داخل العروض، وكل هذا من " | </syntaxhighlight>تستطيع بهذه الطريقة فصل أسماء النموذج وخاصيات النموذج من النص داخل العروض، وكل هذا من "المحلية الافتراضيّة" (مثل تنسيقات التاريخ والوقت). يمكن أن توفّر المخازن الأخرى للمكتبة i18n وسائل مختلفة لهذا الفصل. | ||
'''ملاحظة''': لا تُحمّل آلية تحميل | '''ملاحظة''': لا تُحمّل آلية تحميل المحليَّة الافتراضيّة في ريلز ملفّات المحليَّة في القواميس المتداخلة كما هو الحال هنا. لذلك، لكي يعمل هذا، يجب أن نطلب من ريلز بوضوح أن ننظر أبعد من ذلك:<syntaxhighlight lang="rails"> | ||
# config/application.rb | # config/application.rb | ||
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] | config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] | ||
سطر 410: | سطر 411: | ||
== نظرة عامة على ميزات الواجهة I18n البرمجية == | == نظرة عامة على ميزات الواجهة I18n البرمجية == | ||
يجب أن يكون لديك | يجب أن يكون لديك فهمًا جيدًا لاستخدام المكتبة i18n الآن ومعرفةً تمكّنك من فهم كيفية تدويل تطبيق ريلز بسيط. سوف نغطّي في الفصول التالية الميزات بمزيد من العمق. | ||
ستعرض هذه الفصول أمثلة باستخدام كل من | ستعرض هذه الفصول أمثلة باستخدام كل من التابع <code>I18n.translate</code> إضافةً للتابع المساعد للعرض <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionView/Helpers/TranslationHelper.html#method-i-translate translate]</code> (مع ملاحظة الميزة الإضافية التي يوفّرها التابع المساعد للعرض). | ||
من الميزات المغطّاة هنا: | من الميزات المغطّاة هنا: | ||
سطر 419: | سطر 420: | ||
* جمع الترجمات | * جمع الترجمات | ||
* استخدام ترجمة HTML آمنة ( تابع العرض المساعد) | * استخدام ترجمة HTML آمنة ( تابع العرض المساعد) | ||
* توطين التواريخ والأرقام والعملة وما إلى ذلك | * توطين التواريخ والأرقام والعملة وما إلى ذلك | ||
=== البحث عن الترجمات === | === البحث عن الترجمات === | ||
==== البحث الأساسي، والنطاقات والمفاتيح المتداخلة | ==== البحث الأساسي، والنطاقات والمفاتيح المتداخلة ==== | ||
يُبحث عن الترجمات بالمفاتيح والتي قد تكون رموزًا أو سلاسل بحيث يكون هذان النداءان متساويَين: | يُبحث عن الترجمات بالمفاتيح والتي قد تكون رموزًا أو سلاسل بحيث يكون هذان النداءان متساويَين:<syntaxhighlight lang="rails"> | ||
I18n.t :message | |||
I18n.t 'message' | I18n.t 'message' | ||
</syntaxhighlight>يأخذ التابع <code>translate</code> أيضًا الخيار <code>scope:</code> الذي قد يحتوي على مفتاح إضافي أو أكثر سيُسخدم لتحديد "مجال اسم" أو نطاق لمفتاح الترجمة:<syntaxhighlight lang="rails"> | |||
يأخذ التابع translate أيضًا | I18n.t :record_invalid, scope: [:activerecord, :errors, :messages] | ||
</syntaxhighlight>يبحث هذا عن الرسالة <code>record_invalid:</code> في رسائل خطأ [[Rails/active record|Active Record]]. | |||
يبحث هذا عن الرسالة record_invalid: في رسائل خطأ | |||
إضافةً إلى ذلك، يمكن تحديد المفتاح والنطاقات كمفاتيح مفصولة بنقط كما في السطر التالي:<syntaxhighlight lang="rails"> | |||
I18n.translate "activerecord.errors.messages.record_invalid" | |||
</syntaxhighlight>وبالتالي فإن الاستدعاءات التالية متكافئة:<syntaxhighlight lang="rails"> | |||
I18n.t 'activerecord.errors.messages.record_invalid' | |||
I18n.t 'errors.messages.record_invalid', scope: :activerecord | I18n.t 'errors.messages.record_invalid', scope: :activerecord | ||
I18n.t :record_invalid, scope: 'activerecord.errors.messages' | I18n.t :record_invalid, scope: 'activerecord.errors.messages' | ||
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages] | |||
</syntaxhighlight> | |||
==== الافتراضات (Defaults) ==== | ==== الافتراضات (Defaults) ==== | ||
عندما يعطى | عندما يعطى الخيار <code>default:</code>، ستعاد قيمته إن كانت الترجمة مفقودة:<syntaxhighlight lang="rails"> | ||
I18n.t :missing, default: 'Not here' | |||
# => 'Not here' | |||
</syntaxhighlight>إذا كانت القيمة <code>default:</code> رمزًا، ستُترجم وتُستخدَم كمفتاح. يمكن للمرء تقديم عدّة قيم كإعداد افتراضي. سيعاد أول واحد منهم تنتج منه قيمة. | |||
< | في ما يلي محاولة أولى لترجمة المفتاح <code>missing:</code> ثم المفتاح <code>also_missing:</code>. نظرًا لأن كليهما لا يسفر عن نتيجة، فستعاد السلسلة "Not here":<syntaxhighlight lang="rails"> | ||
I18n.t :missing, default: [:also_missing, 'Not here'] | |||
# => 'Not here' | |||
</syntaxhighlight> | |||
==== البحث عن مجالات الأسماء والبحث بالجملة ==== | |||
يمكن تمرير مصفوفة من المفاتيح للبحث عن عدّة ترجمات في آن واحد:<syntaxhighlight lang="rails"> | |||
I18n.t [:odd, :even], scope: 'errors.messages' | |||
# => ["must be odd", "must be even"] | |||
</syntaxhighlight>يمكن أيضًا ترجمة المفتاح إلى [[Ruby/Hash|جدول Hash]] من الترجمات المجمّعة (قد تكون متداخلة). يمكن للمرء مثلًا تلقّي جميع رسائل خطأ [[Rails/active record|Active Record]] على أنها [[Ruby/Hash|جدول Hash]] مع:<syntaxhighlight lang="rails"> | |||
I18n.t 'activerecord.errors.messages' | |||
# => {:inclusion=>"is not included in the list", :exclusion=> ... } | |||
==== البحث عن مجالات الأسماء والبحث بالجملة | </syntaxhighlight> | ||
يمكن تمرير مصفوفة من المفاتيح للبحث عن عدّة ترجمات في آن واحد: | |||
يمكن أيضًا ترجمة المفتاح | |||
==== البحث "الكسول" ==== | ==== البحث "الكسول" ==== | ||
يعرّف ريلز | يعرّف ريلز استخدام طريقة ملائمة للبحث عن المحليَّة داخل [[Rails/action view overview|العروض]]. عندما تمتلك القاموس التالي:<syntaxhighlight lang="rails"> | ||
es: | |||
books: | |||
index: | |||
title: "Título" | |||
</syntaxhighlight>يمكنك البحث عن القيمة <code>books.index.title</code> داخل القالب app/views/books/index.html.erb (لاحظ النقطة):<syntaxhighlight lang="html"> | |||
<%= t '.title' %> | |||
</syntaxhighlight>'''ملاحظة''': يتوفّر تحديد الترجمة التلقائية (Automatic translation scoping) جزئيًا فقط من تابع العرض المساعد <code>translate</code>. | |||
يمكنك البحث عن القيمة books.index.title داخل القالب app/views/books/index.html.erb (لاحظ النقطة): | |||
يتوفّر تحديد الترجمة التلقائية (Automatic translation scoping) جزئيًا فقط من تابع العرض المساعد | |||
يمكن أيضًا استخدام البحث "الكسول" في وحدات التحكّم:<syntaxhighlight lang="rails"> | |||
en: | |||
books: | |||
create: | |||
success: Book created! | |||
</syntaxhighlight>هذا مفيد في ضبط الرسائل الوامضة مثلًا:<syntaxhighlight lang="rails"> | |||
class BooksController < ApplicationController | |||
def create | |||
# ... | |||
redirect_to books_url, notice: t('.success') | |||
end | |||
end | end | ||
</syntaxhighlight> | |||
=== صيغة الجمع === | === صيغة الجمع === | ||
في العديد من اللغات - بما في ذلك اللغة الإنجليزية - هناك صيغتان | في العديد من اللغات - بما في ذلك اللغة الإنجليزية -، هناك صيغتان فقط هما صيغة المفرد والجمع لكلمة معينة (سلسلة نصية)، مثل " 1 message" و "2 messages". تملك لغات أخرى ([http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar العربية] و[http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ja اليابانية] و[http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ru الروسية] وغيرها الكثير) قواعد نحويّة مختلفة تحتوي على [http://cldr.unicode.org/index/cldr-spec/plural-rules صيَغ جمع] أكثر أو أقل. وبالتالي توفّر واجهة برمجة التطبيقات (I18n) ميزة صيغ الجمع المرنة. | ||
يملك متغير الاستيفاء <code>count:</code> (أي interpolation variable) دورًا خاصًّا من حيث أنه يُقحَم بالترجمة ويُستخدَم في اختيار صيغ جمع من الترجمات وفقًا لقواعد الجمع المحددّة في واجهة الجمع الخلفية. بشكل افتراضي، تُطبّق قواعد الجمع للإنجليزية فقط.<syntaxhighlight lang="rails"> | |||
I18n.backend.store_translations :en, inbox: { | |||
zero: 'no messages', # optional | |||
one: 'one message', | |||
other: '%{count} messages' | |||
} | } | ||
I18n.translate :inbox, count: 2 | I18n.translate :inbox, count: 2 | ||
# => '2 messages' | |||
I18n.translate :inbox, count: 1 | I18n.translate :inbox, count: 1 | ||
# => 'one message' | |||
I18n.translate :inbox, count: 0 | I18n.translate :inbox, count: 0 | ||
# => 'no messages' | |||
</syntaxhighlight>خوارزميّة الجمع للمحليَّة <code>en:</code> بسيطة مثل:<syntaxhighlight lang="rails"> | |||
lookup_key = :zero if count == 0 && entry.has_key?(:zero) | |||
خوارزميّة | lookup_key ||= count == 1 ? :one : :other | ||
lookup_key | |||
|= count == 1 ? :one : :other | |||
entry[lookup_key] | entry[lookup_key] | ||
</syntaxhighlight>الترجمة المُرمّزة بالشكل <code>one:</code> تعد صيغة مفردة، و <code>other:</code> تعد صيغة جمع. إذا كان <code>count</code> صفرًا ووُجد إدخال <code>zero:</code>، سيُستخدَم بدلًا من <code>other:</code>. | |||
الترجمة المُرمّزة بالشكل one: | |||
إن لم | إن لم يعيد البحث عن المفتاح [[Ruby/Hash|جدول Hash]] قابل للجمع، يُرفَع الاستثناء <code>I18n::InvalidPluralizationData</code>. | ||
==== القواعد الخاصة بمحليَّة (Locale-specific rules) ==== | ==== القواعد الخاصة بمحليَّة (Locale-specific rules) ==== | ||
توفّر الجوهرة I18n خلفية صيغ جمع Pluralization التي يمكن استخدامها لتفعيل قواعد | توفّر الجوهرة I18n خلفية صيغ جمع (Pluralization backend) التي يمكن استخدامها لتفعيل قواعد خاصّة بإعدادات محليّة معيّنة. ضمنها إلى الخلفيّة Simple ثم أضف خوارزميات صيغ الجمع المُوطّنة إلى مخزن الترجمة كما مع <code>i18n.plural.rule</code>.<syntaxhighlight lang="rails"> | ||
I18n::Backend::Simple.include(I18n::Backend::Pluralization) | |||
I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } } | |||
I18n.backend.store_translations :pt, apples: { one: 'one or none', other: 'more than one' } | I18n.backend.store_translations :pt, apples: { one: 'one or none', other: 'more than one' } | ||
I18n.t :apples, count: 0, locale: :pt | I18n.t :apples, count: 0, locale: :pt | ||
# => 'one or none' | |||
</syntaxhighlight>بدلًا من ذلك، يمكن استخدام الجوهرة المنفصلة rail-i18n لتوفير مجموعة كاملة من قواعد الجمع الفريدة بمحلية معيّنة. | |||
=== ضبط و تمرير محلية === | |||
يمكن تعيين اللغة المحليّة إما على شكل <code>I18n.locale</code> (والذي يستخدم <code>[[Ruby/Thread/current|Thread.current]]</code> مثل <code>[[Ruby/Time/zone|Time.zone]]</code> مثلًا) أو يمكن تمريرها كخيار إلى <code>translate.</code> و <code>localize.</code>. | |||
إذا لم يُمرّر أي محليَّة، يُستخدم <code>I18n.locale</code>:<syntaxhighlight lang="rails"> | |||
I18n.locale = :de | |||
إذا لم يُمرّر أي | |||
I18n.t :foo | I18n.t :foo | ||
I18n.l Time.now | I18n.l Time.now | ||
</syntaxhighlight>تمرير محليَّة بشكل صريح:<syntaxhighlight lang="rails"> | |||
تمرير محليَّة بشكل صريح: | I18n.t :foo, locale: :de | ||
I18n.l Time.now, locale: :de | I18n.l Time.now, locale: :de | ||
</syntaxhighlight>الافتراضي بالنسبة إلى <code>I18n.locale</code> هو <code>I18n.default_locale</code> الذي بدوره يعتبر <code>en:</code> كقيمته الافتراضيّة. يمكن ضبط المحليَّة الافتراضية على النحو التالي:<syntaxhighlight lang="rails"> | |||
الافتراضي بالنسبة إلى I18n.locale هو I18n.default_locale الذي بدوره يعتبر en: كقيمته الافتراضيّة. يمكن ضبط المحليَّة | I18n.default_locale = :de | ||
</syntaxhighlight> | |||
=== استخدام ترجمات HTML آمنة === | === استخدام ترجمات HTML آمنة === | ||
تُميّز المفاتيح التي تحتوي على لاحقة "html_" | تُميّز المفاتيح التي تحتوي على لاحقة "html_" والمفاتيح المسمّاة "html" على أنها آمنة HTML. عند استخدامها في [[Rails/action view overview|العرض]]، لن يتم تهريبها وعدم تفسيرها على أنها ليست شيفرة [[HTML]].<syntaxhighlight lang="rails"> | ||
# config/locales/en.yml | |||
en: | |||
welcome: <b>welcome!</b> | |||
hello_html: <b>hello!</b> | |||
title: | |||
html: <b>title!</b> | |||
</syntaxhighlight><syntaxhighlight lang="html"> | |||
# app/views/home/index.html.erb | |||
<div><%= t('welcome') %></div> | |||
<div><%= raw t('welcome') %></div> | |||
<div><%= t('hello_html') %></div> | |||
<div><%= t('title.html') %></div> | |||
</syntaxhighlight>الاستيفاء يهرب عند الحاجة رغم ذلك. خذ مثلًا:<syntaxhighlight lang="rails"> | |||
en: | en: | ||
welcome_html: "<b>Welcome %{username}!</b>" | |||
</syntaxhighlight>يمكنك تمرير اسم المستخدم بأمان كما حدّده المستخدم:<syntaxhighlight lang="html"> | |||
<%# This is safe, it is going to be escaped if needed. %> | |||
يمكنك تمرير اسم المستخدم بأمان كما حدّده المستخدم: | |||
<%= t('welcome_html', username: @current_user.username) %> | <%= t('welcome_html', username: @current_user.username) %> | ||
</syntaxhighlight>تُستوفى السلاسل الآمنة من جهة أخرى كما هي حرفيًّا. | |||
'''ملاحظة''': يتوفّر التحويل التلقائي إلى شيفرة [[HTML]] آمنة للترجمة النصيّة فقط من تابع العرض المساعد <code>translate</code>. | |||
[[ملف:demo_html_safe_rails.png|بديل=كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.|تصغير|500بك|كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.|بدون]] | |||
=== ترجمات لنماذج Active Record === | |||
يمكنك استخدام التابعين <code>Model.model_name.human</code> و <code>Model.human_attribute_name(attribute)</code> للبحث عن الترجمات بشفافية لأسماء نموذجك وخاصياتك. | |||
عند إضافة الترجمات التالية مثلًا:<syntaxhighlight lang="rails"> | |||
en: | |||
activerecord: | |||
models: | |||
user: Dude | |||
attributes: | |||
user: | |||
login: "Handle" | |||
# will translate User attribute "login" as "Handle" | |||
</syntaxhighlight>ثم يعيد <code>User.model_name.human</code> الاسم "Dude" و <code>User.human_attribute_name("login")</code> يعيد "Handle". | |||
يمكنك أيضًا تعيين صيغة الجمع لأسماء النماذج بإضافة ما يلي:<syntaxhighlight lang="rails"> | |||
en: | |||
activerecord: | |||
models: | |||
user: | |||
one: Dude | |||
other: Dudes | |||
</syntaxhighlight>ثم <code>User.model_name.human(count: 2)</code> سيعيد "Dudes". أمّا مع <code>count: 1</code> أو بدون معاملات (<code>params</code>)، فسيعيد "Dude". | |||
في حال احتجت للوصول إلى خاصية متداخلة داخل نموذج معيّن، فعليك تضمينها (nest) تحت <code>model/attribute</code> على مستوى النموذج من ملف ترجمتك:<syntaxhighlight lang="rails"> | |||
en: | |||
activerecord: | |||
attributes: | |||
user/gender: | |||
female: "Female" | |||
male: "Male" | |||
</syntaxhighlight>ثم سيعيد <code>User.human_attribute_name("gender.female")</code> النتيجة "Female". | |||
في حال | |||
attributes: | |||
ثم | |||
إذا كنت تستخدم صنفًا يتضمّن ActiveModel ولا يرث من ActiveRecord:: | '''ملاحظة''': إذا كنت تستخدم صنفًا يتضمّن <code>ActiveModel</code> ولا يرث من <code>ActiveRecord::Base</code>، عوّض <code>activerecord</code> بـ <code>activemodel</code> في المسارات المفتاحيّة المذكورة أعلاه. | ||
==== | ==== مجالات رسالة الخطأ ==== | ||
يمكنك أيضًا ترجمة رسائل أخطاء التحقّق من | يمكنك أيضًا ترجمة رسائل أخطاء التحقّق من [[Rails/action view|Active Record]] بسهولة. يمنحك [[Rails/action view|Active Record]] بضع مجالات أسماء حيث يمكنك وضع ترجمات رسائلك من أجل توفير رسائل وترجمات مختلفة لبعض النماذج، والخاصيات، و/أو تحقيقات الصحة (validations). كما يأخذ شفيرة توريث الجدول الواحد (single table inheritance) في الحسبان بشفافيّة. | ||
هذا يمنحك وسيلة قويّة للغاية لضبط رسائلك حسب احتياجات تطبيقك بمرونة. | هذا يمنحك وسيلة قويّة للغاية لضبط رسائلك حسب احتياجات تطبيقك بمرونة. | ||
ضع في اعتبارك | ضع في اعتبارك النموذج <code>User</code> مع التحقّق من صحة الخاصية <code>name</code> كما يلي:<syntaxhighlight lang="rails"> | ||
class User < ApplicationRecord | |||
validates :name, presence: true | |||
end | end | ||
</syntaxhighlight>المفتاح لرسالة الخطأ في هذه الحالة هو <code>blank:</code>. سيبحث [[Rails/action view|Active Record]] عن هذا المفتاح في مجالات الأسماء:<syntaxhighlight lang="rails"> | |||
المفتاح لرسالة الخطأ في هذه الحالة هو blank:. سيبحث Active Record عن هذا المفتاح في مجالات الأسماء: | activerecord.errors.models.[model_name].attributes.[attribute_name] | ||
activerecord.errors.models.[model_name] | activerecord.errors.models.[model_name] | ||
activerecord.errors.messages | activerecord.errors.messages | ||
errors.attributes.[attribute_name] | errors.attributes.[attribute_name] | ||
errors.messages | errors.messages | ||
</syntaxhighlight>وبالتالي في مثالنا هذا، سيُجرّب المفاتيح التالية بهذا الترتيب ويعيد النتيجة الأولى:<syntaxhighlight lang="rails"> | |||
وبالتالي في مثالنا هذا، سيُجرّب المفاتيح التالية بهذا الترتيب | activerecord.errors.models.user.attributes.name.blank | ||
activerecord.errors.models.user.blank | activerecord.errors.models.user.blank | ||
activerecord.errors.messages.blank | activerecord.errors.messages.blank | ||
errors.attributes.name.blank | errors.attributes.name.blank | ||
errors.messages.blank | errors.messages.blank | ||
</syntaxhighlight>عندما تستخدم نماذجك إضافة على ذلك الميراث، يُبحث عن الرسائل في سلسلة الميراث. | |||
عندما تستخدم نماذجك إضافة على ذلك | |||
مثلًا، قد يكون لديك النموذج <code>Admin</code> يرث من <code>User</code>:<syntaxhighlight lang="rails"> | |||
class Admin < User | |||
validates :name, presence: true | |||
end | end | ||
</syntaxhighlight>بعد ذلك، سيبحث Active Record عن الرسائل بالترتيب التالي:<syntaxhighlight lang="rails"> | |||
activerecord.errors.models.admin.attributes.name.blank | |||
activerecord.errors.models.admin.blank | activerecord.errors.models.admin.blank | ||
activerecord.errors.models.user.attributes.name.blank | activerecord.errors.models.user.attributes.name.blank | ||
activerecord.errors.models.user.blank | activerecord.errors.models.user.blank | ||
activerecord.errors.messages.blank | activerecord.errors.messages.blank | ||
errors.attributes.name.blank | errors.attributes.name.blank | ||
errors.messages.blank | errors.messages.blank | ||
</syntaxhighlight>وبهذه الطريقة، يمكنك توفير ترجمات خاصة لرسائل خطأ مختلفة في نقاط مختلفة من سلسلة وراثة نماذجك وفي الخاصيات أو النماذج أو النطاقات الإفتراضيّة. | |||
وبهذه | ==== الاستيفاء مع رسالة الخطأ ==== | ||
دائمًا ما يكون اسم النموذج واسم الخاصية المترجمين والقيمة متاحين للاستيفاء عبر <code>model</code> و <code>attribute</code> و <code>value</code> على التوالي. | |||
==== رسالة | |||
دائمًا ما يكون اسم النموذج واسم | |||
لهذا السبب، بدلًا من رسالة الخطأ الافتراضيّة "cannot be blank" مثلًا، يمكنك استخدام اسم الخاصية كما يلي: <code>"Please fill in your %{attribute}"</code>. | |||
* يمكن استخدام <code>count</code> إن كان متاحًا لصيغة الجمع إن وُجدت: | |||
{| class="wikitable" | {| class="wikitable" | ||
| | !التحقق | ||
!مع الخيار | |||
!الرسالة | |||
!الاستيفاء | |||
|- | |||
|تأكيد | |||
|<nowiki>-</nowiki> | |||
|<code>:confirmation</code> | |||
|<code>attribute</code> | |||
|- | |||
|قبول | |||
|<nowiki>-</nowiki> | |||
|<code>:accepted</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|وجود | |||
|<nowiki>-</nowiki> | |||
|<code>:blank</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|غياب | |||
|<nowiki>-</nowiki> | |||
|<code>:present</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|طول | |||
|<code>:within</code>, <code>:in</code> | |||
|<code>:too_short</code> | |||
|<code>count</code> | |||
|- | |||
|طول | |||
|<code>:within</code>, <code>:in</code> | |||
|<code>:too_long</code> | |||
|<code>count</code> | |||
|- | |||
|طول | |||
|<code>:is</code> | |||
|<code>:wrong_length</code> | |||
|<code>count</code> | |||
|- | |||
|طول | |||
|<code>:minimum</code> | |||
|<code>:too_short</code> | |||
|<code>count</code> | |||
|- | |||
|طول | |||
|<code>:maximum</code> | |||
|<code>:too_long</code> | |||
|<code>count</code> | |||
|- | |||
|فردية | |||
|<nowiki>-</nowiki> | |||
|<code>:taken</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|تنسيق (format) | |||
|<nowiki>-</nowiki> | |||
|<code>:invalid</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|الشمول (inclusion) | |||
|<nowiki>-</nowiki> | |||
|<code>:inclusion</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|الاستثناء (exclusion) | |||
|<nowiki>-</nowiki> | |||
|<code>:exclusion</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|الترابط (associated) | |||
|<nowiki>-</nowiki> | |||
|<code>:invalid</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|الارتباط غير الاختياري (non-optional association) | |||
|<nowiki>-</nowiki> | |||
|<code>:required</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|عدد (Numericality) | |||
|<nowiki>-</nowiki> | |||
|<code>:not_a_number</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|عدد | |||
|<code>:greater_than</code> | |||
|<code>:greater_than</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:greater_than_or_equal_to</code> | |||
|<code>:greater_than_or_equal_to</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:equal_to</code> | |||
|<code>:equal_to</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:less_than</code> | |||
|<code>:less_than</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:less_than_or_equal_to</code> | |||
|<code>:less_than_or_equal_to</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:other_than</code> | |||
|<code>:other_than</code> | |||
|<code>count</code> | |||
|- | |||
|عدد | |||
|<code>:only_integer</code> | |||
|<code>:not_an_integer</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|عدد | |||
|<code>:odd</code> | |||
|<code>:odd</code> | |||
|<nowiki>-</nowiki> | |||
|- | |||
|عدد | |||
|<code>:even</code> | |||
|<code>:even</code> | |||
|<nowiki>-</nowiki> | |||
|} | |} | ||
{| class="wikitable" | {| class="wikitable" | ||
|} | |} | ||
=== | ==== ترجمات للمساعد <code>error_messages_for</code> الذي يخص Active Record ==== | ||
إن | إن كنت تستخدم المساعد <code>error_messages_for</code> الذي يخص [[Rails/active record|Active Record]]، ستحتاج لإضافة ترجمات له. | ||
{ | |||
يُشحن ريلز مع الترجمات التالية:<syntaxhighlight lang="rails"> | |||
en: | |||
activerecord: | |||
errors: | |||
template: | |||
header: | |||
one: "1 error prohibited this %{model} from being saved" | |||
other: "%{count} errors prohibited this %{model} from being saved" | |||
body: "There were problems with the following fields:" | |||
</syntaxhighlight>'''ملاحظة''': تحتاج لتثبيت جوهرة [https://github.com/joelmoss/dynamic_form DynamicForm] كي تستخدم هذا المساعد بإضافة هذا السطر إلى Gemfile:<syntaxhighlight lang="text"> | |||
gem 'dynamic_form' | |||
</syntaxhighlight> | |||
=== ترجمات لموضوعات البريد الاكتروني Action Mailer === | |||
إن لم تقم بتمرير موضوع للتابع <code>mail</code>، سيحاول [[Rails/action mailer|Action Mailer]] العثور عليه في ترجماتك. سيستخدم البحث المُنفّذ النمط <code><mailer_scope>.<action_name>.subject</code> لإنشاء المفتاح.<syntaxhighlight lang="rails"> | |||
# user_mailer.rb | |||
class UserMailer < ActionMailer::Base | class UserMailer < ActionMailer::Base | ||
def welcome(user) | |||
#... | |||
end | |||
end | end | ||
</syntaxhighlight><syntaxhighlight lang="rails"> | |||
en: | en: | ||
user_mailer: | |||
welcome: | |||
subject: "Welcome to Rails Guides!" | |||
welcome: | </syntaxhighlight>لإرسال المعاملات للاستيفاء، استخدم التابع <code>default_i18n_subject</code> على المُرسل (mailer).<syntaxhighlight lang="rails"> | ||
# user_mailer.rb | |||
لإرسال المعاملات | |||
class UserMailer < ActionMailer::Base | class UserMailer < ActionMailer::Base | ||
def welcome(user) | |||
mail(to: user.email, subject: default_i18n_subject(user: user.name)) | |||
end | |||
mail(to: user.email, subject: default_i18n_subject(user: user.name)) | |||
end | end | ||
</syntaxhighlight><syntaxhighlight lang="rails"> | |||
en: | en: | ||
user_mailer: | |||
welcome: | |||
subject: "%{user}, welcome to Rails Guides!" | |||
</syntaxhighlight> | |||
=== نظرة عامة على توابع مدمجة أخرى توفر دعم I18n === | |||
يستخدم ريلز سلاسل نصية ثابتة وتوطينات أخرى مثل سلاسل التنسيق ومعلومات تنسيق أخرى في بضعة مساعدين. فيما يلي نظرة عامة موجزة. | |||
=== نظرة عامة على توابع مدمجة أخرى | |||
يستخدم ريلز سلاسل ثابتة وتوطينات أخرى مثل سلاسل التنسيق ومعلومات تنسيق أخرى في بضعة مساعدين. فيما يلي نظرة عامة موجزة | |||
==== توابع مساعدة للإجراء <code>View</code> ==== | |||
* <code>distance_of_time_in_words</code>: يُترجم ويُجمَع نتائجه ويستوفي عدد الثواني والدقائق والساعات وما إلى ذلك. انظر ترجمات [https://github.com/rails/rails/blob/master/actionview/lib/action_view/locale/en.yml#L4 datetime.distance_in_words]. | |||
* <code>datetime_select</code> و <code>select_month</code>: يستخدمان أسماء الشهور المترجمة لملء الوسم <code>[[HTML/select|<select>]]</code> الناتج. راجع <code>[https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15 date.month_names]</code> للترجمات. يبحث <code>datetime_select</code> أيضًا عن الخيار <code>order</code> من <code>[https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18 date.order]</code> (إلا إذا مرّرت الخيار صراحةً). يُترجم جميع مساعدي اختيار التواريخ (date selection helpers) العبارة باستخدام الترجمات في النطاق <code>[https://github.com/rails/rails/blob/master/actionview/lib/action_view/locale/en.yml#L39 datetime.prompts]</code> إن أمكن. | |||
* <code>number_to_currency</code> و <code>number_with_precision</code> و <code>number_to_percentage</code> و <code>number_with_delimiter</code> و <code>number_to_human_size</code> و <code>number_to_human_size</code>: تستخدم إعدادات تنسيق الأرقام الموجودة في نطاق <code>[https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L37 number]</code>. | |||
==== توابع الإجراء <code>Model</code> ==== | |||
* <code>model_name.human</code> و <code>human_attribute_name</code>: يستخدمان الترجمات لأسماء نماذج وأسماء الخاصيات إن توفّرت في النطاق <code>[https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L36 activerecord.models]</code>. كما أنها تدعم ترجمة لأسماء الأصناف الموروثة (مثلًا للاستخدام مع STI) كما هو موضح أعلاه في القسم "مجالات رسالة الخطأ". | |||
* <code>ActiveModel::Errors.generate_message</code>: (الذي يُستخدَم من قبل [[Rails/active model basics#.D8.A7.D9.84.D8.AA.D8.AD.D9.82.D9.82 .D9.85.D9.86 .D8.A7.D9.84.D8.B5.D8.AD.D8.A9|تحققات Active Model]] ولكن قد يُستخدَم يدويًّا) يستخدم <code>model_name.human</code> و <code>human_attribute_name</code> (انظر أعلاه). يترجم أيضًا رسالة الخطأ ويدعم الترجمات من أجل أسماء الأصناف الموروثة كما شُرِح أعلاه في القسم "مجالات رسالة الخطأ". | |||
* <code>ActiveModel::Errors.full_messages</code>: يضيف اسم الخاصية إلى رسالة الخطأ باستخدام فاصل يُبحَث عنه من <code>[https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml#L4 error.format]</code> (وقيمته الافتراضيّة <code>"%{attribute} %{message}"</code>). | |||
==== توابع Active Support ==== | |||
* <code>Array.to_sentence</code>: يستخدم إعدادات التنسيق كما أُعطيَت في النطاق <code>[https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L33 support.array]</code>. | |||
== كيف تخزن ترجماتك المخصصة == | |||
تسمح لك الواجهة الخلفيّة Simple المشحونة مع [[Rails/active support|Active Support]] بتخزين الترجمات بتنسيق ruby و YAML. | |||
مثلًا، يمكن أن يبدو شكل [[Ruby/Hash|جدول Hash]] يوفر ترجمات على النحو التالي:<syntaxhighlight lang="rails"> | |||
{ | |||
pt: { | |||
foo: { | |||
bar: "baz" | |||
} | |||
} | |||
} | } | ||
</syntaxhighlight>سيبدو ملف YAML المكافئ كالتالي:<syntaxhighlight lang="rails"> | |||
pt: | |||
foo: | |||
bar: baz | |||
</syntaxhighlight>كما ترى، في كلتا الحالتين، يكون مفتاح المستوى الأعلى هو المحليّة. <code>foo:</code> هو مفتاح مجال الاسم و <code>bar:</code> هو مفتاح للترجمة "baz". | |||
في ما يلي مثال "حقيقي" من ملف YAML لترجمات [[Rails/active support|Active Support]] (الملف en.yml):<syntaxhighlight lang="rails"> | |||
en: | |||
date: | |||
formats: | |||
default: "%Y-%m-%d" | |||
short: "%b %d" | |||
long: "%B %d, %Y" | |||
في ما يلي مثال "حقيقي" من ملف | |||
formats: | |||
</syntaxhighlight>لذلك، ستعيد جميع عمليّات البحث الموافقة التالية التنسيق <code>short:</code> للتاريخ <code>"b %d%"</code>:<syntaxhighlight lang="rails"> | |||
I18n.t 'date.formats.short' | |||
I18n.t 'formats.short', scope: :date | I18n.t 'formats.short', scope: :date | ||
I18n.t :short, scope: 'date.formats' | |||
I18n.t :short, scope: [:date, :formats] | |||
</syntaxhighlight>نوصي بشكل عام باستخدام YAML كامتداد لتخزين الترجمات. مع ذلك، هناك حالات تريد تخزين تعابير lambda لروبي كجزء من بيانات المحليّة مثل من أجل تنسيقات التاريخ الخاصّة. | |||
== تخصيص إعداد I18n الخاص بك == | |||
=== استخدام واجهات خلفية متنوعة === | |||
| | لأسباب كثيرة، كل ما تفعله الواجهة الخلفيّة Simple التي تُشحن مع [[Rails/active support|Active Support]] هو "أبسط شيء يمكنه العمل" في ريلز مما يعني أنها مضمونة العمل باللغة الإنجليزية فقط، ومع اللغات المشابهة جدًا للغة الإنجليزية كتأثير جانبي. كما أن الواجهة الخلفيّة Simple قادرة على قراءة الترجمات فقط لا على تخزينها ديناميكيًا بأي شكل. | ||
لكن هذا لا يعني أنك عالق مع هذه القيود. تُسهّل جوهرة I18n لروبي استبدال تعريف استخدام الواجهة الخلفية Simple بشيء آخر يناسب احتياجاتك بشكل أفضل من خلال تمرير نسخة خلفيّة إلى <code>I18n.backend= setter</code>. | |||
على سبيل المثال، يمكنك استبدال الواجهة الخلفيّة Simple مع الواجهة الخلفية Chain لسَلسَلة عدّة واجهات خلفية معًا. يُفيدك هذا عندما تريد استخدام ترجمات قياسية مع الواجهة الخلفية Simple ولكن مع تخزين ترجمات تطبيق مخصّصة بقاعدة بيانات أو في الواجهات الخلفية الأخرى. | |||
تستطيع مع الواجهة الخلفية Chain استخدام الواجهة الخلفية [[Rails/active record|Active Record]] والعودة إلى الواجهة الخلفية Simple (الافتراضيّة):<syntaxhighlight lang="rails"> | |||
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend) | |||
</syntaxhighlight> | |||
على | === استخدام معالجات مختلفة للاستثناءات === | ||
تحدّد الواجهة البرمجية I18n الاستثناءات التالية التي ستُرفع من طرف الواجهات الخلفية عند حدوث الظروف غير المتوقعة المقابلة:<syntaxhighlight lang="rails"> | |||
# تعذر العثور على أي ترجمة للمفتاح المطلوب | |||
MissingTranslationData | |||
# nil غير صالحة أي مثلًا I18n.locale المحليَّة المعطاة إلى | |||
InvalidLocale | |||
# مُرِّر خيار عددي لكن الترجمة غير مناسبة للجمع | |||
InvalidPluralizationData | |||
# تتوقع الترجمة وسيط استيفاء لم يُمرّر | |||
MissingInterpolationArgument | |||
# تحوي الترجمة اسم متغير استيفاء محجوز | |||
ReservedInterpolationKey | |||
# I18n.load_path لا تعرف الواجهة الخلفية كيفية التعامل مع نوع الملف الذي أُضيف إلى | |||
UnknownFileType | |||
</syntaxhighlight>ستلتقط الواجهة البرمجية I18n كل هذه الاستثناءات عندما تُرمَى في الخلفيّة وتمرّرها إلى التابع <code>default_exception_handler</code>. سيعيد هذا التابع رمي كافّة الاستثناءات سوى استثناءات <code>MissingTranslationData</code>. عند التقاط الاستثناء <code>MissingTranslationData</code>، سيعيد سلسلة رسالة خطأ الاستثناء الذي يحتوي على المفتاح/النطاق المفقود. | |||
يرجع السبب في ذلك إلى أنه أثناء التطوير عادة ما ترغب في تصيير عروضك حتى وإن كانت الترجمة مفقودة. | يرجع السبب في ذلك إلى أنه أثناء التطوير عادة ما ترغب في تصيير عروضك حتى وإن كانت الترجمة مفقودة. | ||
مع ذلك قد ترغب في سياقات أخرى بتغيير هذا السلوك. | مع ذلك قد ترغب في سياقات أخرى بتغيير هذا السلوك. مثلًا، لا تسمح معالجة الاستثناءات الإفتراضيّة بالتقاط الترجمات المفقودة أثناء الاختبارات الآليّة بسهولة. لهذا الغرض، يمكن تحديد معالج استثناء مختلف. يجب أن يكون معالج الاستثناء المحدد تابعًا بوحدة I18n أو صنفًا مع التابع <code>call.</code>:<syntaxhighlight lang="rails"> | ||
module I18n | |||
class JustRaiseExceptionHandler < ExceptionHandler | |||
def call(exception, locale, key, options) | |||
if exception.is_a?(MissingTranslationData) | |||
raise exception.to_exception | |||
def call(exception, locale, key, options) | else | ||
super | |||
end | |||
end | |||
end | |||
end | end | ||
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new | I18n.exception_handler = I18n::JustRaiseExceptionHandler.new | ||
</syntaxhighlight>سيعيد هذا رمي الاستثناء <code>MissingTranslationData</code> فقط، مع تمرير كل الإدخالات (input) الأخرى إلى معالج الاستثناء الافتراضي. | |||
سيعيد هذا | |||
ولكن إن كنت تستخدم <code>I18n::Backend::Pluralization</code>، سيرمي هذا المعالج أيضًا استثناء:<syntaxhighlight lang="rails"> | |||
I18n::MissingTranslationData: translation missing: en.i18n.plural.rule | |||
</syntaxhighlight>الذي يجب تجاهله عادةً للعودة إلى قاعدة صيغ الجمع الافتراضيّة للغة الإنجليزية. تستطيع استخدام تحقّق (check) إضافي لمفتاح الترجمة لتجنّب هذا الاستثناء:<syntaxhighlight lang="rails"> | |||
if exception.is_a?(MissingTranslationData) && key.to_s != 'i18n.plural.rule' | |||
raise exception.to_exception | |||
else | else | ||
super | |||
end | end | ||
</syntaxhighlight>مثال آخر حيث السلوك الافتراضي غير مرغوب فيه هو <code>TranslationHelper</code> في ريلز الذي يوفر التابع <code>t.</code> (وكذلك <code>translate.</code>). عند وقوع الاستثناء <code>MissingTranslationData</code> في هذا السياق يلف هذا المساعد الرسالة في عنصر <code>[[HTML/span|<nowiki><span></nowiki>]]</code> باستخدام صنف [[CSS]] يدعى <code>translation_missing</code>. | |||
مثال آخر حيث السلوك الافتراضي غير مرغوب فيه هو TranslationHelper في ريلز الذي يوفر التابع t | |||
لفعل | لفعل ذلك، يجبر المساعد <code>I18n.translate</code> على رفع الاستثناءات بغض النظر عن معالج الاستثناء المُعرّف عن طريق تحديد الخيار <code>raise:</code>:<syntaxhighlight lang="rails"> | ||
I18n.t :foo, raise: true # always re-raises exceptions from the backend | |||
</syntaxhighlight> | |||
== ترجمة محتوى النموذج == | == ترجمة محتوى النموذج == | ||
الواجهة البرمجية I18n المُوصوفة في هذا الدليل تهدف بشكل أساسي لترجمة سلاسل الواجهة النصيّة. إن كنت تريد ترجمة محتوى نموذج (مثل مشاركات المدونّات)، فستحتاج إلى حل مختلف لأداء هذا الأمر. | |||
تستطيع عدّة جواهر المساعدة في ذلك: | تستطيع عدّة جواهر المساعدة في ذلك منها: | ||
* Globalize: | * [https://github.com/globalize/globalize Globalize]: تخزن الترجمات على جداول منفصلة للترجمة واحدًا لكل نموذج مترجم. | ||
* Mobility: يوفّر الدعم لتخزين الترجمات بأشكال عديدة بما في ذلك جداول الترجمة وأعمدة json (في Postgres) ، إلخ. | * [https://github.com/shioyama/mobility Mobility]: يوفّر الدعم لتخزين الترجمات بأشكال عديدة بما في ذلك جداول الترجمة وأعمدة json (في Postgres)، ...إلخ. | ||
* Traco: أعمدة قابلة للترجمة في ريلز 3 و 4 مخزّنة في جدول النموذج نفسه | * [https://github.com/barsoom/traco Traco]: أعمدة قابلة للترجمة في ريلز 3 و 4 مخزّنة في جدول النموذج نفسه. | ||
== الخلاصة == | == الخلاصة == | ||
في هذه المرحلة، يجب أن يكون لديك نظرة عامة جيدة حول كيفية دعم I18n في | في هذه المرحلة، يجب أن يكون لديك نظرة عامة جيدة حول كيفية دعم I18n في ريلز وأن تكون مستعدًا لبدء ترجمة مشروعك عبره. | ||
== | == مصادر مرجعية == | ||
* مجموعة Google | * [https://groups.google.com/forum/#!forum/rails-i18n مجموعة Google التي تخص rails-i18n] - القائمة البريدية للمشروع. | ||
* GitHub: rails-i18n - مستودع | * [https://github.com/svenfuchs/rails-i18n GitHub: rails-i18n] - مستودع الشيفرات ومتتبع إصدارات مشروع rails-i18n. الأهم من ذلك يمكنك [https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale العثور على الكثير من الترجمات] على سبيل المثال لريلز التي ينبغي أن تعمل بتطبيقك في معظم الحالات. | ||
* GitHub: i18n - مستودع الشيفرة ومتعقّب المشاكل لجوهرة i18n. | * [https://github.com/svenfuchs/i18n GitHub: i18n] - مستودع الشيفرة ومتعقّب المشاكل لجوهرة i18n. | ||
== | == المؤلفون == | ||
* Sven Fuchs (المؤلف الاول) | * [http://svenfuchs.com/ Sven Fuchs] (المؤلف الاول) | ||
* Karel Minařík | * [http://www.karmi.cz/ Karel Minařík] | ||
== | == مصادر == | ||
* | * [https://guides.rubyonrails.org/i18n.html صفحة Rails Internationalization (I18n) API في توثيق Ruby On Rails الرسمي.] |
المراجعة الحالية بتاريخ 06:30، 25 مارس 2019
توفّر جوهرة روبي I18n (اختزالٌ للمصطلح "التدويل" [internationalization]) التي تُشحن مع ريلز (بدءًا من الإصدار 2.2) إطارًا سهل الاستخدام وقابلًا للتوسعة لترجمة تطبيقك إلى لغة مخصصّة واحدة بخلاف الإنجليزية أو لتوفير دعم بلغات متعدّدة في تطبيقك.
عادةً ما تعني عمليّة "التدويل" تجريد كل السلاسل النصية والبتات الخاصة الأخرى ذات العلاقة باللغة (مثل تنسيقات التاريخ أو العملة) من تطبيقك. تعني عملية "التوطين" توفير الترجمات والصيغ المحلية لهذه البتات.
لذلك، يجب عليك في عمليّة تدويل تطبيقك ريلز:
- التأكّد من دعمك للواجهة البرمجية i18n.
- إخبار ريلز بمكان العثور على قواميس اللغة.
- إخبار ريلز بكيفيّة إعداد اللغة المحليّة والمحافظة عليها وتبديلها.
في عملية توطين تطبيقك، سترغب غالبًا بالقيام بالأشياء الثلاثة التالية:
- استبدال أو استكمال الإعدادات الافتراضية لريلز؛ على سبيل المثال، تنسيقات التاريخ والوقت، وأسماء الشهور، وأسماء نماذج Active Record، ...إلخ.
- تجريد السلاسل النصيّة في تطبيقك إلى قواميس مفتاحي؛ مثلًا الرسائل الوامضة (flash messages)، والنص الثابت في عروضك، ...إلخ.
- تخزين القواميس الناتجة في مكان ما.
سيرشدك هذا الدليل إلى استعمال الواجهة البرمجية I18n كما سيعلمك كيفيّة تدويل تطبيق ريلز من البداية.
ستتعلم بعد قراءة هذا الدليل:
- كيف تعمل الواجهة البرمجية I18n في ريلز.
- كيفيّة استخدام I18n بشكل صحيح في تطبيق RESTful بطرق مختلفة.
- كيفيّة استخدام I18n لترجمة أخطاء Active Record أو مواضيع البريد الإلكتروني Action Mailer.
- بعض الأدوات الأخرى لمواصلة ترجمة تطبيقك.
ملاحظة: يوفّر لك إطار روبي I18n جميع الوسائل اللازمة لتدويل / توطين تطبيقك ريلز. يمكنك أيضًا استخدام مختلف الجواهر المتاحة لإضافة وظائف أو ميزات إضافية. انظر جوهرة ريلز i18n لمزيد من المعلومات.
كيفية عمل الواجهة البرمجية I18n في ريلز
التدويل مشكلة معقدة. تختلف اللغات الطبيعية بطرق كثيرة (مثلًا في قواعد الجمع) بحيث يصعب توفير أدوات لحل جميع المشاكل في وقت واحد. لهذا السبب، تركّز الواجهة I18n البرمجية في ريلز على:
- تقديم الدعم للغة الإنجليزية واللغات المماثلة مباشرة.
- تسهيل تخصيص كل شيء للغات الأخرى وتوسيعها.
دُوّلت (internationalized) كل سلسلة نصية ثابتة في إطار ريلز كجزء من هذا الحل - مثل رسائل التحقق من Active Record، وتنسيقات الوقت والتاريخ. يعني توطين تطبيق ريلز تعريف القيم المترجمة لهذه السلاسل في اللغات المطلوبة.
لتوطين وتخزين وتحديث المحتوى في تطبيقك (ترجمة منشورات مدوّنة مثلًا)، راجع قسم ترجمة محتوى النماذج.
المعمارية العامة للمكتبة
تُقسّم جوهرة روبي I18n إلى قسمين:
- واجهة برمجة التطبيقات العامة لإطار عمل i18n - وحدة روبي مع التوابع العامّة التي تحدد كيّفية عمل المكتبة.
- واجهة خلفية افتراضيّة (والتي تسمى عمدًا الواجهة الخلفية Simple "الواجهة الخلفية البسيطة") تُنفّذ هذه التوابع.
بصفتك مستخدمًا، عليك دائمًا الوصول إلى التوابع العامّة عبر الوحدة I18n فقط، ولكن من المفيد معرفة قدرات الواجهة الخلفية.
من الممكن تبديل الواجهة الخلفية Simple التي شُحنت بأخرى أكثر فعاليّة، والتي ستخزن بيانات الترجمة في قاعدة بيانات علائقيّة، أو قاموس GetText أو ما شابه ذلك. انظر القسم استخدام الخلفيات المختلفة أدناه.
الواجهة I18n البرمجية العامة
أهم توابع الواجهة I18n البرمجية هي:
translate # تبحث عن ترجمة النص
localize # توطّن كائنات التاريخ والوقت لتنسيقات محليّة
يملك كل تابع من هذين التابعين اسمًا بديًلًا هو: t.
و l.
فتستطيع استخدامها بالشكل التالي:
I18n.t 'store.title'
I18n.l Time.now
توجد أيضًا قارئات وكاتبات خاصيات للخاصيات التالية:
load_path # لإعلان عن ترجمة ملفاتك المحلية
locale # جلب وضبط اللغة المحلية
default_locale # جلب وضبط اللغة المحلية الإفتراضيّة
available_locales # وضع اللغات المحليّة المتوفّرة لتطبيقك في قائمة بيضاء
enforce_available_locales # (true او false) أجبر وضع اللغات المحليّة في القائمة البيضاء
exception_handler # مختلفًا exception_handler استخدم
backend # استخدم خلفية مختلفة
لذا، فلنُدّول تطبيق ريلز بسيط من الألف إلى الياء في الفصول القادمة!
إعداد تطبيق ريلز للتدويل
توجد بعض الخطوات لتشغيل دعم I18n لتطبيق ريلز.
إعداد وحدة I18n
وفقّا لفلسفة ريلز الناصّة على أن "العرف فوق الضبط" (convention over configuration) يوفّر ريلز I18n سلاسل ترجمة نصيّة معقولة. يمكن إعادة تعريفها عندما تحتاج إلى سلاسل ترجمة مختلفة.
ويضيف ريلز كل من الملفّات بصيغة rb. و yml. من المجلّد config/locales إلى "مسار تحميل الترجمة" (translations load path) تلقائيًّا.
تحتوي اللغة المحليّة (locale) في هذا المجلّد على زوج من سلاسل الترجمة:
en:
hello: "Hello world"
يعني هذا أنه في اللّغة en:
، سيعيَّن المفتاح "hello" إلى السلسلة النصيّة "Hello world". تُدوّل كل سلسلة داخل ريلز بهذه الطريقة؛ انظر على سبيل المثال لرسائل التحقق من صحّة Active Model في الملف activemodel/lib/active_model/locale/en.yml أو تنسيقات الوقت والتاريخ في الملف activesupport/lib/active_support/locale/en.yml. يمكنك استخدام YAML أو جداول Hash القياسيّة لتخزين الترجمات في الواجهة الخلفية الافتراضية (الواجهة الخلفية Simple).
ستستخدم مكتبة I18n اللغة الإنجليزية كلغة افتراضية أي إن لم تُعيّن لغة مختلفة سيُستخدم en:
للبحث عن الترجمات.
ملاحظة: تستعمل المكتبة i18n "مقاربة واقعية" (pragmatic approach) لمفاتيح المحليَّة (بعد بعض النقاش) بأخذها جزء اللّغة فقط بالحسبان مثل en:
و pl:
لا جزء المنطقة معها مثل en-US:
أو en-GB:
التي تُستخدَم عادةً لفصل "اللغات" و "الإعدادات الإقليميّة" أو "اللهجات". تستخدم عدّة تطبيقات دوليّة عنصر اللغة فقط مثل: cs:
أو th:
أو es:
(للتشيكيّة، والتايلاندية والإسبانية على التوالي). ومع ذلك تظل هناك أيضًا اختلافات إقليميّة قد تكون مهمّة داخل مجموعات لغويّة مختلفة. على سبيل المثال في المحليَّة en-US:
رمز العملة هو $
بينما هو £
في en-GB:
. لا شيء يمنعك من فصل الإعدادات الإقليمية والأخرى بهذه الطريقة: عليك فقط توفير إعداد اللغة المحليّة "الإنجليزية - المملكة المتحدة" الكاملة في قاموس en-GB:
.
مسار تحميل الترجمات (I18n.load_path) هو مصفوفة مسارات إلى الملفّات التي ستُحمّل تلقائيًا. يتيح إعداد هذا المسار تخصيص بنية مُجلّد الترجمة ومخطّط تسمية الملفّات.
ملاحظة: تحمّل الواجهة الخلفيّة هذه الترجمات بكَسَل (lazy-load) عندما يُبحث في الترجمة للمرّة الأولى. يمكن تبديل هذه الواجهة الخلفيّة بشيء آخر حتى بعد الإعلان عن الترجمة بالفعل.
يمكنك تغيير اللغة الإفتراضيّة بالإضافة لإعداد مسارات تحميل الترجمات في الملف config/application.rb كما يلي:
config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.default_locale = :de
يجب تحديد مسار التحميل قبل البحث في أي ترجمات. لتغيير اللغة الافتراضيّة من مُهيّئ (initializer) بدلًا من الملف config/application.rb:
# config/initializers/locale.rb
# عن ملفّات الترجمة I18n المكان حيث يجب أن تبحث المكتبة
I18n.load_path += Dir[ريلز.root.join('lib', 'locale', '*.{rb,yml}')]
# ضع الإعدادات المحليّة المتوفّرة بقائمة بيضاء
I18n.available_locales = [:en, :pt]
# :en ضبط الإعداد المحلّي الإفتراضي كشيء مختلف عن
I18n.default_locale = :pt
إدارة المحلية عبر الطلبات
تُستخدم المحليّة الإفتراضيّة لكافة الترجمات ما لم يُضبط I18n.locale
بشكل صريح.
من المحتمل أن يحتاج التطبيق المترجم إلى توفير الدعم للعديد من اللغات. ولتحقيق ذلك يجب تعيين المحليّة في بداية كل طلب بحيث تُترجم جميع السلاسل النصية باستخدام اللغة المطلوبة خلال دورة حياة هذا الطلب.
يمكن تعيين اللغة في before_action
في المتحكم ApplicationController
:
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
يوضّح هذا المثال ذلك باستخدام معامل استعلام URL لتعيين الإعداد المعلّي (على سبيل المثال http://example.com/books?locale=pt). باستخدام هذه الطريقة، يُصيّر http://localhost:3000?locale=pt التوطين البرتغالي بينما يحمّل http://localhost:3000?locale=de loads توطينًا باللغة الألمانية.
يمكن تعيين اللغة باستخدام طرق مختلفة كثيرة.
ضبط المحلية من اسم النطاق (Domain Name)
أحد الخيارات لديك هو تعيين اللغة من اسم النطاق حيث يشتغل تطبيقك. نريد مثلًا أن يُحمّل www.example.com اللغة الإنجليزية (أو اللغة الإفتراضيّة)، و www.example.es اللغة الإسبانيّة. بحيث يُستخدم اسم النطاق ذو المستوى الأعلى لإعداد المحليَّة. ولهذا الأسلوب العديد من المزايا:
- يصبح المحليَّة جزءًا واضحًا من عنوان URL.
- يفهم الناس تلقائيًّا بأي لغة سيُعرض المحتوى.
- لتنفيذ هذا في ريلز في قمّة البساطة.
- يبدو أن محركات البحث تحبّ وجود المحتوى بلغات مختلفة في نطاقات مختلفة لكن مترابطة.
يمكنك تعريف استخدامه على هذا النحو في وحدة التحكم ApplicationController
:
before_action :set_locale
def set_locale
I18n.locale = extract_locale_from_tld || I18n.default_locale
end
# إن لم يوجد مثل هذا الإعداد +nil+ تحصّل على إعدادات محليّة من أعلى مستوى النطاق أو ردّ
# عليك وضع شيء مثل :
# 127.0.0.1 application.com
# 127.0.0.1 application.it
# 127.0.0.1 application.pl
# إن أردت تجربة هذا محليًّا /etc/hosts بملفّك
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
يمكننا أيضًا تعيين اللغة أو المحليَّة من النطاق الفرعي بطريقة مشابهة جدًا:
# ( http://it.application.local:3000 )تحصّل على كود الإعداد المحلّي من نطاق الطلب الفرعي مثل
# عليك وضع شيء مثل :
# 127.0.0.1 gr.application.local
# إن أردت تجربة هذا محليًّا /etc/hosts بملفّك
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
إذا تضمّن تطبيقك قائمة تبديل لغة، ستجد فيها شيئًا من هذا القبيل:
link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['PATH_INFO']}")
على افتراض أنك ستُعيّن APP_CONFIG[:deutsch_website_url]
إلى قيمة ما مثل http://www.application.de.
لهذا الحل مزايا سالفة الذكر ومع ذلك قد لا تقدر أو لا ترغب في توفير توطينات مختلفة (بمعنى "نُسخ من اللغات") في نطاقات مختلفة. سيكون الحل البديهي تضمين رمز المحليَّة في المعاملات params
للعنوان URL (أو مسار الطلب).
ضبط المحليَّة من معاملات العنوان URL
الطريقة الأكثر شيوعًا لتعيين (وتمرير) المحليَّة هي تضمينه في معاملات URL (أي params
)، كما فعلنا في التابع before_action
من I18n.locale = params[:locale]
في المثال الأول. نرغب في هذه الحالة في الحصول على عناوين URL مثل www.example.com/books?locale=ja أو www.example.com/ja/books.
يحتوي هذا التابع تقريبًا على نفس مجموعة المزايا من إعداد اللغة المحليّة من اسم النطاق: أي أنه RESTful و يتوافق مع باقي شبكة الويب العالمية. لكنّه يتطلّب المزيد من العمل لتعريف استخدامه.
الحصول على المحليَّة من params
وضبطه وفقًا لذلك ليس صعبًا؛ الصعب هو وضعه بكل عنوان URL وبالتالي تمريره عبر الطلبات. من الصعب والممل تضمين خيار صريح بكل عنوان URL مثلًا link_to(books_url(locale: I18n.locale))
إن لم يكن مستحيلًا بالطبع.
يحتوي ريلز على بنية أساسيّة من أجل "مركزية القرارات الديناميكية حول عناوين URL" (أي centralizing dynamic decisions about the URLs) في ApplicationController.default_url_options
، وهو مفيد تحديدًا في هذا السيناريو: فهو يمكّننا من تعيين "افتراضيّات" من أجل url_for
و التوابع المساعدة المعتمدات عليه (عن طريق تعريف استخدام/ إعادة تعريف default_url_options
).
يمكننا تضمين شيء من هذا القبيل في المتحكم ApplicationController
لدينا ثم:
# app/controllers/application_controller.rb
def default_url_options
{ locale: I18n.locale }
end
كل تابع مساعد يعتمد على url_for
(مثل مساعدي المسارات المسمّاة مثل root_path
أو root_url
، ومسارات الموارد مثل books_path
أو books_url
...إلخ.) سيشمل الآن تلقائيًا المحليَّة في سلسلة الاستعلام بالشكل http://localhost:3001/?locale=ja.
قد تكون راضيًا عن هذا. إلا أنّه يؤثّر على سهولة قراءة عناوين URL عند "وضع" المحليَّة في نهاية كل عنوان URL في تطبيقك. علاوة على ذلك من الناحيّة المعمارية، عادةً ما تكون اللغة أو المحليَّة أعلى هرميًّا من الأجزاء الأخرى بنطاق التطبيق: ويجب أن تعكس عناوين URL ذلك.
سترغب غالبًا أن تبدو عناوين URL بالشكل: http://www.example.com/en/books (الذي يُحمّل اللغة الإنجليزية) و http://www.example.com/nl/books (الذي يُحمّل اللغة الهولندية). يمكن تحقيق ذلك من خلال استراتيجية " إعادة تعريف default_url_options
" أعلاه: عليك فقط إعداد مساراتك باستخدام scope
:
# config/routes.rb
scope "/:locale" do
resources :books
end
الآن، عند استدعاءك التابع books_path
، يجب أن تحصل على "/en/books" (للغة الافتراضيّة). يجب أن يُحمّل عنوان URL مثل http://localhost:3001/nl/books باللغة الهولندية، وبعد ذلك يجب أن تعيد النداءات إلى books_path
السلسلة "/nl/books" (لأن اللغة تغيّرت).
تحذير: نظرًا لأن القيمة المعادة من default_url_options
تُخّزن مؤقتًا لكل طلب، لا يمكن توليد عناوين URL في مُحدّد المحليَّة (locale selector) تستدعي مساعدين بشكل حلقي (loop) تُعيِّن I18n.locale
المقابل في كل تكرار (iteration). اترك بدلًا من ذلك I18n.locale
دون تغيير، أو مرّر الخيار locale:
بشكل صريح إلى المساعد، أو عدّل request.original_fullpath
.
إن لم ترغب في فرض استخدام محليَّة في مساراتك، تستطيع استخدام نطاق مسار اختياري (يُشار إليه بالأقواس) على النحو التالي:
# config/routes.rb
scope "(:locale)", locale: /en|nl/ do
resources :books
end
باستخدام هذه الطريقة، لن تحصل على الخطأ "Routing Error" عند الوصول إلى مواردك مثل http://localhost:3001/books بدون محليَّة. هذا مفيد عندما تريد استخدام اللغة الافتراضيّة عند عدم تحديدها.
يجب أن تمنح عناية خاصة لجذر العنوان URL (عادةً ما تكون "الصفحة الرئيسية" أو "لوحة المعلومات") بالتطبيق. لن يعمل عنوان URL مثل http://localhost:3001/nl تلقائيًا، نظرًا لأن التصريح root to: "books#index"
في الملف routes.rb لا يأخذ اللغة في الحسبان. (ومعه الحق، إذ هناك عنوان URL "جذر" واحد فقط.)
ستحتاج غالبًا لتعيين عناوين URL مثل هذه:
# config/routes.rb
get '/:locale' => 'dashboard#index'
لا تهتم بشكل خاص بترتيب مسارات التوجيه الخاصة بك لأن التصريح بهذا الشكل لا يعني "أكل" المسارات الأخرى. (قد ترغب في إضافته مباشرة قبل التصريح root :to
.)
ملاحظة: ألقِ نظرة على مختلف الجواهر التي تبسّط العمل مع المسارات: routing_filter
و rails-translate-routes
و route_translator
.
ضبط المحلية من تفضيلات المستخدم
قد يسمح تطبيق مع مستخدمين مُستَوثَقين لهم بضبط تفضيلات لغة من خلال واجهة التطبيق. بهذه الطريقة يظل تفضيله المحدّد للغة في قاعدة البيانات ويُستخدم لتحديد المحليَّة للطلبات المصادق عليها من طرف ذلك المستخدم.
def set_locale
I18n.locale = current_user.try(:locale) || I18n.default_locale
end
اختيار محلية مضمنة
عندما لا تعيّن المحلية بصراحة لطلب ما (عبر إحدى التوابع المذكورة أعلاه مثلًا)، يجب أن يحاول التطبيق استنتاج المحلية المرغوبة بنفسه.
استنباط المحلية من ترويسة اللغة
تشير الترويسة Accept-Language في طلبيات HTTP إلى اللغة المفضّلة لاستجابة الطلب. تُعيّن المتصفّحات قيمة هذه الترويسة استنادًا لإعدادات تفضيل اللغة للمستخدم مما يجعله اختيارًا أوليًّا جيدًا لاستنباط لغة.
ما يلي مثال عن تعريف استخدام بسيط للترويسة Accept-Language
:
def set_locale
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
I18n.locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{I18n.locale}'"
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
من الضروري من الناحية العمليّة وجود تعليمات برمجيّة أكثر قوة لفعل هذا بذلك بشكل يعتمد عليه. مكتبة إيان هيكر (Iain Hecker) من أجل http_accept_language
أو برمجيّة رايان تومايكو (Ryan Tomayko) الوسيطة للمحلية مع Rack تقدّم حلولًا لهذه المشكلة.
استنباط المحلية من الموقع الجغرافي للعنوان IP
يمكن استخدام عنوان IP العميل الذي يقدم الطلب لاستنتاج منطقته وبالتالي إعداداته المحليّة. يمكن استخدام خدمات مثل GeoIP Lite Country أو جواهر مثل الجوهرة geocoder لتطبيق هذه الطريقة.
هذه الطريقة بشكل عام أقل موثوقية بكثير من استخدام ترويسة اللغة ولا يوصى بها لمعظم تطبيقات الويب.
تخزين المحلية من الجلسة أو من ملفات تعريف الارتباط (Cookies)
تحذير: قد تميل إلى تخزين المحليَّة المختارة في جلسة عمل أو ملف تعريف ارتباط (Cookie) لكن لا تفعل هذا. يجب أن تكون المحليَّة واضحة وجزءًا من عنوان URL. هكذا لن تخالف افتراضات الناس الأساسيّة حول الويب نفسه: إن أرسلت عنوان URL إلى صديق، فيجب أن يشاهد نفس الصفحة والمحتوى الذي تراه. أفضل مصطلح لهذا الأمر هو أنك RESTful. اقرأ المزيد عن مقاربة RESTful في مقالات ستيفان تيلكوف (Stefan Tilkov). في بعض الأحيان، هناك استثناءات لهذه القاعدة وتلك التي سنناقشها أدناه.
التدويل والتوطين
حسنًا، الآن هيّئت الدعم I18n لتطبيق ريلز الخاص بك وأعلمته عن محليَّة المستخدم وكيفيّة الحفاظ عليه بين الطلبات.
نحتاج بعد ذلك لتدويل طلبنا عن طريق تجريد كل عنصر خاص بالمحليّة. وأخيرًا، نحتاج لتوطينهم بتوفير الترجمات اللازمة لهذه المُجرّدات.
في المثال التالي:
# config/routes.rb
Rails.application.routes.draw do
root to: "home#index"
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = "Hello Flash"
end
end
# app/views/home/index.html.erb
<h1>Hello World</h1>
<p><%= flash[:notice] %></p>
تجريد الشيفرات البرمجية المترجمة (Abstracting Localized Code)
توجد سلسلتان نصيتان باللغة الإنجليزية في التطبيق كما يظهر لدينا سيُصيّرهما المستخدمون في ردّنا ("Hello Flash" و "Hello World"). لتدويل هذه الشيفرة، يجب استبدال هذه السلاسل ووضع استدعاءات إلى المساعد t.
مع مفتاح مناسب لكل سلسلة:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = t(:hello_flash)
end
end
# app/views/home/index.html.erb
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
والآن، عندما يُصيّر هذا العرض، سيُظهر لك رسالة خطأ تخبرك بأن ترجمات المفتاحين :hello_world
و :hello_flash
مفقودة.
ملاحظة: تضيف ريلز المساعد t
(أي translate
[ترجمة]) لوحدة العرض، لذا لا تحتاج إلى كتابة I18n.t
في كل مرة. إضافة إلى ذلك، هذا المساعد سيلتقط الترجمات ويغلف رسالة الخطأ الناتجة في <span class="translation_missing">
.
توفير ترجمات للسلاسل النصية المدولة
أضف الترجمات المفقودة إلى ملفات قاموس الترجمة:
# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!
# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
تستخدم الترجمات المحليَّة en:
نظرًا لعدم تغيير default_locale
، وتصيّر الاستجابة السلاسل النصية باللغة الإنجليزية:
إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي http://localhost:3000?locale=pirate)، فستصير السلسلتين النصيتين إلى ما يقابلهما لتلك المحلية:
ملاحظة: عليك إعادة تشغيل الخادم عند إضافة ملفّات جديدة للمحليّة.
يمكنك استخدام ملفات YAML (ذات الامتداد yml.) أو ملفات روبي العادية (ذات الامتداد rb.) لتخزين ترجماتك في SimpleStore. لكن YAML هو الخيار المفضّل بين مطورّي ريلز. ومع ذلك، لديه عيب واحد كبير هو أنَّ YAML حساس جدًا للمسافات البيضاء والمحارف الخاصّة، لذلك قد لا يُحمّل التطبيق القاموس بشكل صحيح. ستعطّل ملفّات روبي تطبيقك من الطلب الأول بحيث يمكنك العثور على الخطأ بسهولة. (إذا واجهت أي "مشكلات غريبة" مع قواميس YAML، حاول وضع الجزء ذي الصلة منها في ملف روبي.)
إن خُزّنت ترجماتك في ملفات YAML، يجب تهريب (escape) بعض المفاتيح وهم:
true
-on
-yes
false
-off
-no
إليك الأمثلة التالية:
# config/locales/en.yml
en:
success:
'true': 'True!'
'on': 'On!'
'false': 'False!'
failure:
true: 'True!'
off: 'Off!'
false: 'False!'
I18n.t 'success.true' # => 'True!'
I18n.t 'success.on' # => 'On!'
I18n.t 'success.false' # => 'False!'
I18n.t 'failure.false' # => Translation Missing
I18n.t 'failure.off' # => Translation Missing
I18n.t 'failure.true' # => Translation Missing
تمرير المتغيرات للترجمات
أحد الاعتبارات الرئيسية للنجاح في تدويل طلب ما هو تجنّب عمل افتراضات خاطئة حول القواعد النحوية عند تجريد الشيفرة المُوطّنة. القواعد النحوية التي تبدو أساسية في لغة ما قد لا تكون صحيحة في لغة أخرى.
يظهر التجريد غير السليم في المثال التالي، حيث وُضعَت افتراضات حول ترتيب أجزاء الترجمة المختلفة. لاحظ أن ريلز يوفّر المساعد number_to_currency
لمعالجة الحالة التالية:
# app/views/products/show.html.erb
<%= "#{t('currency')}#{@product.price}" %>
# config/locales/en.yml
en:
currency: "$"
# config/locales/es.yml
es:
currency: "€"
إذا كان سعر المنتج 10 فإن الترجمة الإسبانية المناسبة هي: " € 10" بدلًا من "10€" لكن التجريد عاجز عن تقديمها.
لإنشاء تجريد صحيح، تُشحن جواهر I18n مع ميزة تسمى "الاستيفاء المتغيّر" (variable interpolation) الذي يسمح لك باستخدام المتغيّرات في تعريفات الترجمة وتمرير القيم لهذه المتغيّرات إلى تابع الترجمة.
يظهر التجريد الصحيح في المثال التالي:
# app/views/products/show.html.erb
<%= t('product_price', price: @product.price) %>
# config/locales/en.yml
en:
product_price: "$%{price}"
# config/locales/es.yml
es:
product_price: "%{price} €"
تُتّخذ جميع قرارات النحو وعلامات الترقيم في التعريف نفسه وبالتالي يمكن أن يعطي التجريد ترجمةً مناسبةً.
ملاحظة: الكلمات الرئيسية مثل default
و scope
محجوزةً ولا يمكن استخدامها كأسماء للمتغيرات. وإن استُخدمت، يُرفَع الاستثناء I18n::ReservedInterpolationKey
. إن توقّعت ترجمة متغير استيفاء (interpolation) ولكنه لم يُمرّر إلى translate.
، يُرفع الاستثناء I18n::MissingInterpolationArgument
.
إضافة تنسيقات التاريخ/الوقت
حسنًا، فلنقم الآن بإضافة طابع زمني إلى العرض حتى نتمكّن من عرض ميزة توطين التاريخ/الوقت أيضًا. لتوطين تنسيق الوقت تُمرّر كائن الوقت إلى I18n.l
أو (يُفضّل) استخدام المساعد l.
. يمكنك اختيار تنسيق عبر تمرير الخيار format:
- يُستخدم التنسيق default:
افتراضيًا.
# app/views/home/index.html.erb
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
<p><%= l Time.now, format: :short %></p>
وفي ملف الترجمات الخاص بالمحلية pirate، فلنضف تنسيقًا زمنيًا (يوجد بالفعل في إعدادات ريلز الإفتراضيّة للغة الإنجليزية):
# config/locales/pirate.yml
pirate:
time:
formats:
short: "arrrround %H'ish"
بحيث يمنحك ذلك:
تنبيه: قد تحتاج الآن لإضافة المزيد من تنسيقات التاريخ/الوقت بالترتيب لجعل عمل الواجهة الخلفية I18n مثل المتوقّع (على الأقل بالنسبة للغة "القرصنة").
هناك طبعًا فرصة كبيرة أن شخصًا ما أنجز بالفعل كل هذا العمل عن طريق ترجمة افتراضيّات ريلز للغتك المحليّة. راجع مستودع rails-i18n في GitHub لأرشيف من ملفات المحليَّة المختلفة. عند وضع مثل هذا الملف (أو الملفّات) في المجلّد /config/locales، سيكون جاهزًا للاستخدام تلقائيًا.
قواعد العطف لمحلية أخرى
يسمح لك ريلز بتحديد قواعد العطف (أي inflection مثل قواعد المُفرد والجمع) للإعدادات المحليّة غير الإنجليزية. يمكنك تعريف هذه القواعد لعدّة لغات في config/initializers/inflections.rb. يحتوي المُهيئ على مثال افتراضي لتحديد قواعد إضافيّة للغة الإنجليزية؛ اتبع ذاك التنسيق للغات أخرى كما تراه مناسبا.
العروض المترجمة
فلنفترض أنك تمتلك وحدة التحكّم BooksController
في تطبيقك. يُصيّر الإجراء index
المحتوى في القالب app/views/books/index.html.erb. عندما تضع المتغير المترجم من هذا القالب: index.es.html.erb، سيُصيّر ريلز المحتوى في هذا القالب عندما تُعيّن المحليَّة إلى es:
. سيُستخدم العرض العادي index.html.erb عندما تُعيّن المحليَّة إلى المحلية الافتراضيّة. (قد تجلب إصدارات ريلز المستقبليّة هذا التوطين التلقائي للموارد في public
...إلخ.)
يمكنك الاستفادة من هذه الميزة عند العمل مع الكثير من المحتوى الثابت؛ مثلًا، الذي سيكون من السذاجة لوضعه داخل قواميس YAML أو روبي. مع ذلك ضع في الحسبان أن من اللازم نشر أي تغيير ترغب فيه بالنموذج لاحقًا لجميع القوالب.
تنظيم ملفات المحلية
عند استخدام SimpleStore الافتراضي المشحون مع مكتبة i18n، تُخزّن القواميس في ملفات نصيّة عاديّة على القرص. قد تصعب إدارة الترجمة لجميع أجزاء تطبيقك في ملف واحد لكل لغة/محليَّة. يمكنك تخزين هذه الملفات في تسلسل هرمي منطقي لك.
يمكن أن يبدو مجلّد config/locales كالتالي مثلًا:
|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb
تستطيع بهذه الطريقة فصل أسماء النموذج وخاصيات النموذج من النص داخل العروض، وكل هذا من "المحلية الافتراضيّة" (مثل تنسيقات التاريخ والوقت). يمكن أن توفّر المخازن الأخرى للمكتبة i18n وسائل مختلفة لهذا الفصل. ملاحظة: لا تُحمّل آلية تحميل المحليَّة الافتراضيّة في ريلز ملفّات المحليَّة في القواميس المتداخلة كما هو الحال هنا. لذلك، لكي يعمل هذا، يجب أن نطلب من ريلز بوضوح أن ننظر أبعد من ذلك:
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
نظرة عامة على ميزات الواجهة I18n البرمجية
يجب أن يكون لديك فهمًا جيدًا لاستخدام المكتبة i18n الآن ومعرفةً تمكّنك من فهم كيفية تدويل تطبيق ريلز بسيط. سوف نغطّي في الفصول التالية الميزات بمزيد من العمق.
ستعرض هذه الفصول أمثلة باستخدام كل من التابع I18n.translate
إضافةً للتابع المساعد للعرض translate
(مع ملاحظة الميزة الإضافية التي يوفّرها التابع المساعد للعرض).
من الميزات المغطّاة هنا:
- البحث عن الترجمات
- استيفاء البيانات في الترجمات (interpolating data into translations)
- جمع الترجمات
- استخدام ترجمة HTML آمنة ( تابع العرض المساعد)
- توطين التواريخ والأرقام والعملة وما إلى ذلك
البحث عن الترجمات
البحث الأساسي، والنطاقات والمفاتيح المتداخلة
يُبحث عن الترجمات بالمفاتيح والتي قد تكون رموزًا أو سلاسل بحيث يكون هذان النداءان متساويَين:
I18n.t :message
I18n.t 'message'
يأخذ التابع translate
أيضًا الخيار scope:
الذي قد يحتوي على مفتاح إضافي أو أكثر سيُسخدم لتحديد "مجال اسم" أو نطاق لمفتاح الترجمة:
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
يبحث هذا عن الرسالة record_invalid:
في رسائل خطأ Active Record.
إضافةً إلى ذلك، يمكن تحديد المفتاح والنطاقات كمفاتيح مفصولة بنقط كما في السطر التالي:
I18n.translate "activerecord.errors.messages.record_invalid"
وبالتالي فإن الاستدعاءات التالية متكافئة:
I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
الافتراضات (Defaults)
عندما يعطى الخيار default:
، ستعاد قيمته إن كانت الترجمة مفقودة:
I18n.t :missing, default: 'Not here'
# => 'Not here'
إذا كانت القيمة default:
رمزًا، ستُترجم وتُستخدَم كمفتاح. يمكن للمرء تقديم عدّة قيم كإعداد افتراضي. سيعاد أول واحد منهم تنتج منه قيمة.
في ما يلي محاولة أولى لترجمة المفتاح missing:
ثم المفتاح also_missing:
. نظرًا لأن كليهما لا يسفر عن نتيجة، فستعاد السلسلة "Not here":
I18n.t :missing, default: [:also_missing, 'Not here']
# => 'Not here'
البحث عن مجالات الأسماء والبحث بالجملة
يمكن تمرير مصفوفة من المفاتيح للبحث عن عدّة ترجمات في آن واحد:
I18n.t [:odd, :even], scope: 'errors.messages'
# => ["must be odd", "must be even"]
يمكن أيضًا ترجمة المفتاح إلى جدول Hash من الترجمات المجمّعة (قد تكون متداخلة). يمكن للمرء مثلًا تلقّي جميع رسائل خطأ Active Record على أنها جدول Hash مع:
I18n.t 'activerecord.errors.messages'
# => {:inclusion=>"is not included in the list", :exclusion=> ... }
البحث "الكسول"
يعرّف ريلز استخدام طريقة ملائمة للبحث عن المحليَّة داخل العروض. عندما تمتلك القاموس التالي:
es:
books:
index:
title: "Título"
يمكنك البحث عن القيمة books.index.title
داخل القالب app/views/books/index.html.erb (لاحظ النقطة):
<%= t '.title' %>
ملاحظة: يتوفّر تحديد الترجمة التلقائية (Automatic translation scoping) جزئيًا فقط من تابع العرض المساعد translate
.
يمكن أيضًا استخدام البحث "الكسول" في وحدات التحكّم:
en:
books:
create:
success: Book created!
هذا مفيد في ضبط الرسائل الوامضة مثلًا:
class BooksController < ApplicationController
def create
# ...
redirect_to books_url, notice: t('.success')
end
end
صيغة الجمع
في العديد من اللغات - بما في ذلك اللغة الإنجليزية -، هناك صيغتان فقط هما صيغة المفرد والجمع لكلمة معينة (سلسلة نصية)، مثل " 1 message" و "2 messages". تملك لغات أخرى (العربية واليابانية والروسية وغيرها الكثير) قواعد نحويّة مختلفة تحتوي على صيَغ جمع أكثر أو أقل. وبالتالي توفّر واجهة برمجة التطبيقات (I18n) ميزة صيغ الجمع المرنة.
يملك متغير الاستيفاء count:
(أي interpolation variable) دورًا خاصًّا من حيث أنه يُقحَم بالترجمة ويُستخدَم في اختيار صيغ جمع من الترجمات وفقًا لقواعد الجمع المحددّة في واجهة الجمع الخلفية. بشكل افتراضي، تُطبّق قواعد الجمع للإنجليزية فقط.
I18n.backend.store_translations :en, inbox: {
zero: 'no messages', # optional
one: 'one message',
other: '%{count} messages'
}
I18n.translate :inbox, count: 2
# => '2 messages'
I18n.translate :inbox, count: 1
# => 'one message'
I18n.translate :inbox, count: 0
# => 'no messages'
خوارزميّة الجمع للمحليَّة en:
بسيطة مثل:
lookup_key = :zero if count == 0 && entry.has_key?(:zero)
lookup_key ||= count == 1 ? :one : :other
entry[lookup_key]
الترجمة المُرمّزة بالشكل one:
تعد صيغة مفردة، و other:
تعد صيغة جمع. إذا كان count
صفرًا ووُجد إدخال zero:
، سيُستخدَم بدلًا من other:
.
إن لم يعيد البحث عن المفتاح جدول Hash قابل للجمع، يُرفَع الاستثناء I18n::InvalidPluralizationData
.
القواعد الخاصة بمحليَّة (Locale-specific rules)
توفّر الجوهرة I18n خلفية صيغ جمع (Pluralization backend) التي يمكن استخدامها لتفعيل قواعد خاصّة بإعدادات محليّة معيّنة. ضمنها إلى الخلفيّة Simple ثم أضف خوارزميات صيغ الجمع المُوطّنة إلى مخزن الترجمة كما مع i18n.plural.rule
.
I18n::Backend::Simple.include(I18n::Backend::Pluralization)
I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
I18n.backend.store_translations :pt, apples: { one: 'one or none', other: 'more than one' }
I18n.t :apples, count: 0, locale: :pt
# => 'one or none'
بدلًا من ذلك، يمكن استخدام الجوهرة المنفصلة rail-i18n لتوفير مجموعة كاملة من قواعد الجمع الفريدة بمحلية معيّنة.
ضبط و تمرير محلية
يمكن تعيين اللغة المحليّة إما على شكل I18n.locale
(والذي يستخدم Thread.current
مثل Time.zone
مثلًا) أو يمكن تمريرها كخيار إلى translate.
و localize.
.
إذا لم يُمرّر أي محليَّة، يُستخدم I18n.locale
:
I18n.locale = :de
I18n.t :foo
I18n.l Time.now
تمرير محليَّة بشكل صريح:
I18n.t :foo, locale: :de
I18n.l Time.now, locale: :de
الافتراضي بالنسبة إلى I18n.locale
هو I18n.default_locale
الذي بدوره يعتبر en:
كقيمته الافتراضيّة. يمكن ضبط المحليَّة الافتراضية على النحو التالي:
I18n.default_locale = :de
استخدام ترجمات HTML آمنة
تُميّز المفاتيح التي تحتوي على لاحقة "html_" والمفاتيح المسمّاة "html" على أنها آمنة HTML. عند استخدامها في العرض، لن يتم تهريبها وعدم تفسيرها على أنها ليست شيفرة HTML.
# config/locales/en.yml
en:
welcome: <b>welcome!</b>
hello_html: <b>hello!</b>
title:
html: <b>title!</b>
# app/views/home/index.html.erb
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>
الاستيفاء يهرب عند الحاجة رغم ذلك. خذ مثلًا:
en:
welcome_html: "<b>Welcome %{username}!</b>"
يمكنك تمرير اسم المستخدم بأمان كما حدّده المستخدم:
<%# This is safe, it is going to be escaped if needed. %>
<%= t('welcome_html', username: @current_user.username) %>
تُستوفى السلاسل الآمنة من جهة أخرى كما هي حرفيًّا.
ملاحظة: يتوفّر التحويل التلقائي إلى شيفرة HTML آمنة للترجمة النصيّة فقط من تابع العرض المساعد translate
.
ترجمات لنماذج Active Record
يمكنك استخدام التابعين Model.model_name.human
و Model.human_attribute_name(attribute)
للبحث عن الترجمات بشفافية لأسماء نموذجك وخاصياتك.
عند إضافة الترجمات التالية مثلًا:
en:
activerecord:
models:
user: Dude
attributes:
user:
login: "Handle"
# will translate User attribute "login" as "Handle"
ثم يعيد User.model_name.human
الاسم "Dude" و User.human_attribute_name("login")
يعيد "Handle".
يمكنك أيضًا تعيين صيغة الجمع لأسماء النماذج بإضافة ما يلي:
en:
activerecord:
models:
user:
one: Dude
other: Dudes
ثم User.model_name.human(count: 2)
سيعيد "Dudes". أمّا مع count: 1
أو بدون معاملات (params
)، فسيعيد "Dude".
في حال احتجت للوصول إلى خاصية متداخلة داخل نموذج معيّن، فعليك تضمينها (nest) تحت model/attribute
على مستوى النموذج من ملف ترجمتك:
en:
activerecord:
attributes:
user/gender:
female: "Female"
male: "Male"
ثم سيعيد User.human_attribute_name("gender.female")
النتيجة "Female".
ملاحظة: إذا كنت تستخدم صنفًا يتضمّن ActiveModel
ولا يرث من ActiveRecord::Base
، عوّض activerecord
بـ activemodel
في المسارات المفتاحيّة المذكورة أعلاه.
مجالات رسالة الخطأ
يمكنك أيضًا ترجمة رسائل أخطاء التحقّق من Active Record بسهولة. يمنحك Active Record بضع مجالات أسماء حيث يمكنك وضع ترجمات رسائلك من أجل توفير رسائل وترجمات مختلفة لبعض النماذج، والخاصيات، و/أو تحقيقات الصحة (validations). كما يأخذ شفيرة توريث الجدول الواحد (single table inheritance) في الحسبان بشفافيّة.
هذا يمنحك وسيلة قويّة للغاية لضبط رسائلك حسب احتياجات تطبيقك بمرونة.
ضع في اعتبارك النموذج User
مع التحقّق من صحة الخاصية name
كما يلي:
class User < ApplicationRecord
validates :name, presence: true
end
المفتاح لرسالة الخطأ في هذه الحالة هو blank:
. سيبحث Active Record عن هذا المفتاح في مجالات الأسماء:
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
وبالتالي في مثالنا هذا، سيُجرّب المفاتيح التالية بهذا الترتيب ويعيد النتيجة الأولى:
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
عندما تستخدم نماذجك إضافة على ذلك الميراث، يُبحث عن الرسائل في سلسلة الميراث.
مثلًا، قد يكون لديك النموذج Admin
يرث من User
:
class Admin < User
validates :name, presence: true
end
بعد ذلك، سيبحث Active Record عن الرسائل بالترتيب التالي:
activerecord.errors.models.admin.attributes.name.blank
activerecord.errors.models.admin.blank
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
وبهذه الطريقة، يمكنك توفير ترجمات خاصة لرسائل خطأ مختلفة في نقاط مختلفة من سلسلة وراثة نماذجك وفي الخاصيات أو النماذج أو النطاقات الإفتراضيّة.
الاستيفاء مع رسالة الخطأ
دائمًا ما يكون اسم النموذج واسم الخاصية المترجمين والقيمة متاحين للاستيفاء عبر model
و attribute
و value
على التوالي.
لهذا السبب، بدلًا من رسالة الخطأ الافتراضيّة "cannot be blank" مثلًا، يمكنك استخدام اسم الخاصية كما يلي: "Please fill in your %{attribute}"
.
- يمكن استخدام
count
إن كان متاحًا لصيغة الجمع إن وُجدت:
التحقق | مع الخيار | الرسالة | الاستيفاء |
---|---|---|---|
تأكيد | - | :confirmation
|
attribute
|
قبول | - | :accepted
|
- |
وجود | - | :blank
|
- |
غياب | - | :present
|
- |
طول | :within , :in
|
:too_short
|
count
|
طول | :within , :in
|
:too_long
|
count
|
طول | :is
|
:wrong_length
|
count
|
طول | :minimum
|
:too_short
|
count
|
طول | :maximum
|
:too_long
|
count
|
فردية | - | :taken
|
- |
تنسيق (format) | - | :invalid
|
- |
الشمول (inclusion) | - | :inclusion
|
- |
الاستثناء (exclusion) | - | :exclusion
|
- |
الترابط (associated) | - | :invalid
|
- |
الارتباط غير الاختياري (non-optional association) | - | :required
|
- |
عدد (Numericality) | - | :not_a_number
|
- |
عدد | :greater_than
|
:greater_than
|
count
|
عدد | :greater_than_or_equal_to
|
:greater_than_or_equal_to
|
count
|
عدد | :equal_to
|
:equal_to
|
count
|
عدد | :less_than
|
:less_than
|
count
|
عدد | :less_than_or_equal_to
|
:less_than_or_equal_to
|
count
|
عدد | :other_than
|
:other_than
|
count
|
عدد | :only_integer
|
:not_an_integer
|
- |
عدد | :odd
|
:odd
|
- |
عدد | :even
|
:even
|
- |
ترجمات للمساعد error_messages_for
الذي يخص Active Record
إن كنت تستخدم المساعد error_messages_for
الذي يخص Active Record، ستحتاج لإضافة ترجمات له.
يُشحن ريلز مع الترجمات التالية:
en:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
body: "There were problems with the following fields:"
ملاحظة: تحتاج لتثبيت جوهرة DynamicForm كي تستخدم هذا المساعد بإضافة هذا السطر إلى Gemfile:
gem 'dynamic_form'
ترجمات لموضوعات البريد الاكتروني Action Mailer
إن لم تقم بتمرير موضوع للتابع mail
، سيحاول Action Mailer العثور عليه في ترجماتك. سيستخدم البحث المُنفّذ النمط <mailer_scope>.<action_name>.subject
لإنشاء المفتاح.
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
#...
end
end
en:
user_mailer:
welcome:
subject: "Welcome to Rails Guides!"
لإرسال المعاملات للاستيفاء، استخدم التابع default_i18n_subject
على المُرسل (mailer).
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(to: user.email, subject: default_i18n_subject(user: user.name))
end
end
en:
user_mailer:
welcome:
subject: "%{user}, welcome to Rails Guides!"
نظرة عامة على توابع مدمجة أخرى توفر دعم I18n
يستخدم ريلز سلاسل نصية ثابتة وتوطينات أخرى مثل سلاسل التنسيق ومعلومات تنسيق أخرى في بضعة مساعدين. فيما يلي نظرة عامة موجزة.
توابع مساعدة للإجراء View
distance_of_time_in_words
: يُترجم ويُجمَع نتائجه ويستوفي عدد الثواني والدقائق والساعات وما إلى ذلك. انظر ترجمات datetime.distance_in_words.datetime_select
وselect_month
: يستخدمان أسماء الشهور المترجمة لملء الوسم<select>
الناتج. راجعdate.month_names
للترجمات. يبحثdatetime_select
أيضًا عن الخيارorder
منdate.order
(إلا إذا مرّرت الخيار صراحةً). يُترجم جميع مساعدي اختيار التواريخ (date selection helpers) العبارة باستخدام الترجمات في النطاقdatetime.prompts
إن أمكن.number_to_currency
وnumber_with_precision
وnumber_to_percentage
وnumber_with_delimiter
وnumber_to_human_size
وnumber_to_human_size
: تستخدم إعدادات تنسيق الأرقام الموجودة في نطاقnumber
.
توابع الإجراء Model
model_name.human
وhuman_attribute_name
: يستخدمان الترجمات لأسماء نماذج وأسماء الخاصيات إن توفّرت في النطاقactiverecord.models
. كما أنها تدعم ترجمة لأسماء الأصناف الموروثة (مثلًا للاستخدام مع STI) كما هو موضح أعلاه في القسم "مجالات رسالة الخطأ".ActiveModel::Errors.generate_message
: (الذي يُستخدَم من قبل تحققات Active Model ولكن قد يُستخدَم يدويًّا) يستخدمmodel_name.human
وhuman_attribute_name
(انظر أعلاه). يترجم أيضًا رسالة الخطأ ويدعم الترجمات من أجل أسماء الأصناف الموروثة كما شُرِح أعلاه في القسم "مجالات رسالة الخطأ".ActiveModel::Errors.full_messages
: يضيف اسم الخاصية إلى رسالة الخطأ باستخدام فاصل يُبحَث عنه منerror.format
(وقيمته الافتراضيّة"%{attribute} %{message}"
).
توابع Active Support
Array.to_sentence
: يستخدم إعدادات التنسيق كما أُعطيَت في النطاقsupport.array
.
كيف تخزن ترجماتك المخصصة
تسمح لك الواجهة الخلفيّة Simple المشحونة مع Active Support بتخزين الترجمات بتنسيق ruby و YAML.
مثلًا، يمكن أن يبدو شكل جدول Hash يوفر ترجمات على النحو التالي:
{
pt: {
foo: {
bar: "baz"
}
}
}
سيبدو ملف YAML المكافئ كالتالي:
pt:
foo:
bar: baz
كما ترى، في كلتا الحالتين، يكون مفتاح المستوى الأعلى هو المحليّة. foo:
هو مفتاح مجال الاسم و bar:
هو مفتاح للترجمة "baz".
في ما يلي مثال "حقيقي" من ملف YAML لترجمات Active Support (الملف en.yml):
en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
لذلك، ستعيد جميع عمليّات البحث الموافقة التالية التنسيق short:
للتاريخ "b %d%"
:
I18n.t 'date.formats.short'
I18n.t 'formats.short', scope: :date
I18n.t :short, scope: 'date.formats'
I18n.t :short, scope: [:date, :formats]
نوصي بشكل عام باستخدام YAML كامتداد لتخزين الترجمات. مع ذلك، هناك حالات تريد تخزين تعابير lambda لروبي كجزء من بيانات المحليّة مثل من أجل تنسيقات التاريخ الخاصّة.
تخصيص إعداد I18n الخاص بك
استخدام واجهات خلفية متنوعة
لأسباب كثيرة، كل ما تفعله الواجهة الخلفيّة Simple التي تُشحن مع Active Support هو "أبسط شيء يمكنه العمل" في ريلز مما يعني أنها مضمونة العمل باللغة الإنجليزية فقط، ومع اللغات المشابهة جدًا للغة الإنجليزية كتأثير جانبي. كما أن الواجهة الخلفيّة Simple قادرة على قراءة الترجمات فقط لا على تخزينها ديناميكيًا بأي شكل.
لكن هذا لا يعني أنك عالق مع هذه القيود. تُسهّل جوهرة I18n لروبي استبدال تعريف استخدام الواجهة الخلفية Simple بشيء آخر يناسب احتياجاتك بشكل أفضل من خلال تمرير نسخة خلفيّة إلى I18n.backend= setter
.
على سبيل المثال، يمكنك استبدال الواجهة الخلفيّة Simple مع الواجهة الخلفية Chain لسَلسَلة عدّة واجهات خلفية معًا. يُفيدك هذا عندما تريد استخدام ترجمات قياسية مع الواجهة الخلفية Simple ولكن مع تخزين ترجمات تطبيق مخصّصة بقاعدة بيانات أو في الواجهات الخلفية الأخرى.
تستطيع مع الواجهة الخلفية Chain استخدام الواجهة الخلفية Active Record والعودة إلى الواجهة الخلفية Simple (الافتراضيّة):
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
استخدام معالجات مختلفة للاستثناءات
تحدّد الواجهة البرمجية I18n الاستثناءات التالية التي ستُرفع من طرف الواجهات الخلفية عند حدوث الظروف غير المتوقعة المقابلة:
# تعذر العثور على أي ترجمة للمفتاح المطلوب
MissingTranslationData
# nil غير صالحة أي مثلًا I18n.locale المحليَّة المعطاة إلى
InvalidLocale
# مُرِّر خيار عددي لكن الترجمة غير مناسبة للجمع
InvalidPluralizationData
# تتوقع الترجمة وسيط استيفاء لم يُمرّر
MissingInterpolationArgument
# تحوي الترجمة اسم متغير استيفاء محجوز
ReservedInterpolationKey
# I18n.load_path لا تعرف الواجهة الخلفية كيفية التعامل مع نوع الملف الذي أُضيف إلى
UnknownFileType
ستلتقط الواجهة البرمجية I18n كل هذه الاستثناءات عندما تُرمَى في الخلفيّة وتمرّرها إلى التابع default_exception_handler
. سيعيد هذا التابع رمي كافّة الاستثناءات سوى استثناءات MissingTranslationData
. عند التقاط الاستثناء MissingTranslationData
، سيعيد سلسلة رسالة خطأ الاستثناء الذي يحتوي على المفتاح/النطاق المفقود.
يرجع السبب في ذلك إلى أنه أثناء التطوير عادة ما ترغب في تصيير عروضك حتى وإن كانت الترجمة مفقودة.
مع ذلك قد ترغب في سياقات أخرى بتغيير هذا السلوك. مثلًا، لا تسمح معالجة الاستثناءات الإفتراضيّة بالتقاط الترجمات المفقودة أثناء الاختبارات الآليّة بسهولة. لهذا الغرض، يمكن تحديد معالج استثناء مختلف. يجب أن يكون معالج الاستثناء المحدد تابعًا بوحدة I18n أو صنفًا مع التابع call.
:
module I18n
class JustRaiseExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslationData)
raise exception.to_exception
else
super
end
end
end
end
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new
سيعيد هذا رمي الاستثناء MissingTranslationData
فقط، مع تمرير كل الإدخالات (input) الأخرى إلى معالج الاستثناء الافتراضي.
ولكن إن كنت تستخدم I18n::Backend::Pluralization
، سيرمي هذا المعالج أيضًا استثناء:
I18n::MissingTranslationData: translation missing: en.i18n.plural.rule
الذي يجب تجاهله عادةً للعودة إلى قاعدة صيغ الجمع الافتراضيّة للغة الإنجليزية. تستطيع استخدام تحقّق (check) إضافي لمفتاح الترجمة لتجنّب هذا الاستثناء:
if exception.is_a?(MissingTranslationData) && key.to_s != 'i18n.plural.rule'
raise exception.to_exception
else
super
end
مثال آخر حيث السلوك الافتراضي غير مرغوب فيه هو TranslationHelper
في ريلز الذي يوفر التابع t.
(وكذلك translate.
). عند وقوع الاستثناء MissingTranslationData
في هذا السياق يلف هذا المساعد الرسالة في عنصر <span>
باستخدام صنف CSS يدعى translation_missing
.
لفعل ذلك، يجبر المساعد I18n.translate
على رفع الاستثناءات بغض النظر عن معالج الاستثناء المُعرّف عن طريق تحديد الخيار raise:
:
I18n.t :foo, raise: true # always re-raises exceptions from the backend
ترجمة محتوى النموذج
الواجهة البرمجية I18n المُوصوفة في هذا الدليل تهدف بشكل أساسي لترجمة سلاسل الواجهة النصيّة. إن كنت تريد ترجمة محتوى نموذج (مثل مشاركات المدونّات)، فستحتاج إلى حل مختلف لأداء هذا الأمر.
تستطيع عدّة جواهر المساعدة في ذلك منها:
- Globalize: تخزن الترجمات على جداول منفصلة للترجمة واحدًا لكل نموذج مترجم.
- Mobility: يوفّر الدعم لتخزين الترجمات بأشكال عديدة بما في ذلك جداول الترجمة وأعمدة json (في Postgres)، ...إلخ.
- Traco: أعمدة قابلة للترجمة في ريلز 3 و 4 مخزّنة في جدول النموذج نفسه.
الخلاصة
في هذه المرحلة، يجب أن يكون لديك نظرة عامة جيدة حول كيفية دعم I18n في ريلز وأن تكون مستعدًا لبدء ترجمة مشروعك عبره.
مصادر مرجعية
- مجموعة Google التي تخص rails-i18n - القائمة البريدية للمشروع.
- GitHub: rails-i18n - مستودع الشيفرات ومتتبع إصدارات مشروع rails-i18n. الأهم من ذلك يمكنك العثور على الكثير من الترجمات على سبيل المثال لريلز التي ينبغي أن تعمل بتطبيقك في معظم الحالات.
- GitHub: i18n - مستودع الشيفرة ومتعقّب المشاكل لجوهرة i18n.
المؤلفون
- Sven Fuchs (المؤلف الاول)
- Karel Minařík