الفرق بين المراجعتين ل"Rails/i18n"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط
 
(3 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة)
سطر 11: سطر 11:
 
* إخبار ريلز بكيفيّة إعداد اللغة المحليّة والمحافظة عليها وتبديلها.
 
* إخبار ريلز بكيفيّة إعداد اللغة المحليّة والمحافظة عليها وتبديلها.
 
في عملية توطين تطبيقك، سترغب غالبًا بالقيام بالأشياء الثلاثة التالية:
 
في عملية توطين تطبيقك، سترغب غالبًا بالقيام بالأشياء الثلاثة التالية:
* استبدال أو استكمال الإعدادات الافتراضية لريلز؛ على سبيل المثال، تنسيقات التاريخ والوقت، وأسماء الشهور، وأسماء نماذج السجلات الفعالة (Active Record)، ...إلخ.
+
* استبدال أو استكمال الإعدادات الافتراضية لريلز؛ على سبيل المثال، تنسيقات التاريخ والوقت، وأسماء الشهور، وأسماء نماذج [[Rails/active record|Active Record]]، ...إلخ.
 
* تجريد السلاسل النصيّة في تطبيقك إلى قواميس مفتاحي؛ مثلًا الرسائل الوامضة (flash messages)، والنص الثابت في عروضك، ...إلخ.
 
* تجريد السلاسل النصيّة في تطبيقك إلى قواميس مفتاحي؛ مثلًا الرسائل الوامضة (flash messages)، والنص الثابت في عروضك، ...إلخ.
 
* تخزين القواميس الناتجة في مكان ما.
 
* تخزين القواميس الناتجة في مكان ما.
سطر 19: سطر 19:
 
* كيف تعمل الواجهة البرمجية I18n في ريلز.
 
* كيف تعمل الواجهة البرمجية I18n في ريلز.
 
* كيفيّة استخدام I18n بشكل صحيح في تطبيق RESTful بطرق مختلفة.
 
* كيفيّة استخدام I18n بشكل صحيح في تطبيق RESTful بطرق مختلفة.
* كيفيّة استخدام I18n لترجمة أخطاء [[Rails/active record basics|السجل الفعال]] أو مواضيع البريد الإلكتروني للإجراء <code>[[Rails/action mailer basics|Mailer]]</code>
+
* كيفيّة استخدام 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 البرمجية في ريلز على:
 
التدويل مشكلة معقدة. تختلف اللغات الطبيعية بطرق كثيرة (مثلًا في قواعد الجمع) بحيث يصعب توفير أدوات لحل جميع المشاكل في وقت واحد. لهذا السبب، تركّز الواجهة I18n البرمجية في ريلز على:
 
* تقديم الدعم للغة الإنجليزية واللغات المماثلة مباشرة.
 
* تقديم الدعم للغة الإنجليزية واللغات المماثلة مباشرة.
 
* تسهيل تخصيص كل شيء للغات الأخرى وتوسيعها.
 
* تسهيل تخصيص كل شيء للغات الأخرى وتوسيعها.
دُوّلت (internationalized) كل سلسلة نصية ثابتة في إطار ريلز كجزء من هذا الحل - مثل رسائل التحقق من السجلّات الفعالة، وتنسيقات الوقت والتاريخ. يعني توطين تطبيق ريلز تعريف القيم المترجمة لهذه السلاسل في اللغات المطلوبة.
+
دُوّلت (internationalized) كل سلسلة نصية ثابتة في إطار ريلز كجزء من هذا الحل - مثل رسائل التحقق من [[Rails/active record|Active Record]]، وتنسيقات الوقت والتاريخ. يعني توطين تطبيق ريلز تعريف القيم المترجمة لهذه السلاسل في اللغات المطلوبة.
  
 
لتوطين وتخزين وتحديث المحتوى في تطبيقك (ترجمة منشورات مدوّنة مثلًا)، راجع قسم ترجمة محتوى النماذج.
 
لتوطين وتخزين وتحديث المحتوى في تطبيقك (ترجمة منشورات مدوّنة مثلًا)، راجع قسم ترجمة محتوى النماذج.
سطر 34: سطر 34:
 
تُقسّم جوهرة روبي I18n إلى قسمين:
 
تُقسّم جوهرة روبي I18n إلى قسمين:
 
* واجهة برمجة التطبيقات العامة لإطار عمل i18n - وحدة روبي مع التوابع العامّة التي تحدد كيّفية عمل المكتبة.
 
* واجهة برمجة التطبيقات العامة لإطار عمل i18n - وحدة روبي مع التوابع العامّة التي تحدد كيّفية عمل المكتبة.
* خلفيّة افتراضيّة (والتي تسمى عمدًا خلفية "بسيطة") تُنفّذ هذه التوابع
+
* واجهة خلفية افتراضيّة (والتي تسمى عمدًا الواجهة الخلفية Simple "الواجهة الخلفية البسيطة") تُنفّذ هذه التوابع.
بصفتك مستخدمًا، عليك دائمًا الوصول إلى التوابع العامّة عبر الوحدة I18n فقط، ولكن من المفيد معرفة قدرات الخلفّية.
+
بصفتك مستخدمًا، عليك دائمًا الوصول إلى التوابع العامّة عبر الوحدة I18n فقط، ولكن من المفيد معرفة قدرات الواجهة الخلفية.
  
من الممكن تبديل الواجهة الخلفية البسيطة التي شُحنت بأخرى أكثر فعاليّة، والتي ستخزن بيانات الترجمة في قاعدة بيانات علائقيّة، أو قاموس GetText أو ما شابه ذلك. انظر القسم استخدام الخلفيات المختلفة أدناه.
+
من الممكن تبديل الواجهة الخلفية Simple التي شُحنت بأخرى أكثر فعاليّة، والتي ستخزن بيانات الترجمة في قاعدة بيانات علائقيّة، أو قاموس GetText أو ما شابه ذلك. انظر القسم استخدام الخلفيات المختلفة أدناه.
  
 
=== الواجهة I18n البرمجية العامة ===
 
=== الواجهة I18n البرمجية العامة ===
سطر 67: سطر 67:
 
en:
 
en:
 
   hello: "Hello world"
 
   hello: "Hello world"
</syntaxhighlight>يعني هذا أنه في اللّغة <code>en:</code>، سيعيَّن المفتاح "hello" إلى السلسلة النصيّة "Hello world". تُدوّل كل سلسلة داخل ريلز بهذه الطريقة؛ انظر على سبيل المثال لرسائل التحقق من صحّة النموذج الفعال في الملف [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]] القياسيّة لتخزين الترجمات في الخلفيّة (البسيطة) الافتراضية.
+
</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) لمفاتيح الإعدادات المحلية (بعد [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 "مقاربة واقعية" (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>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) ====
+
==== ضبط المحلية من اسم النطاق (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 ====
 
الطريقة الأكثر شيوعًا لتعيين (وتمرير) المحليَّة هي تضمينه في معاملات 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> ...إلخ.) سيشمل الآن تلقائيًا الإعدادات المحليّة في سلسلة الاستعلام بالشكل <nowiki>http://localhost:3001/?locale=ja</nowiki>.
+
</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 في تطبيقك. علاوة على ذلك من الناحيّة المعمارية، عادةً ما تكون اللغة أو المحليَّة أعلى هرميًّا من الأجزاء الأخرى بنطاق التطبيق: ويجب أن تعكس عناوين 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 في مُحدّد الإعدادات المحليّة (locale selector) تستدعي مساعدين بشكل حلقي (loop) تُعيِّن <code>I18n.locale</code> المقابل في كل تكرار (iteration). اترك  بدلًا من ذلك <code>I18n.locale</code> دون تغيير، أو مرّر الخيار <code>locale:</code> بشكل صريح إلى المساعد، أو عدّل <code>request.original_fullpath</code>.
+
'''تحذير''': نظرًا لأن القيمة المعادة من <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">
+
قد يسمح تطبيق مع مستخدمين مُستَوثَقين لهم بضبط تفضيلات لغة من خلال واجهة التطبيق. بهذه الطريقة يظل تفضيله المحدّد للغة في قاعدة البيانات ويُستخدم لتحديد المحليَّة للطلبات المصادق عليها من طرف ذلك المستخدم.<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) ====
+
==== تخزين المحلية من الجلسة أو من ملفات تعريف الارتباط (Cookies) ====
'''تحذير''': قد تميل إلى تخزين الإعدادات المحليّة المختارة في جلسة عمل أو ملف تعريف ارتباط (Cookie) لكن لا تفعل هذا. يجب أن تكون المحليَّة واضحة وجزءًا من عنوان URL. هكذا لن تخالف افتراضات الناس الأساسيّة حول الويب نفسه: إن أرسلت عنوان URL إلى صديق، فيجب أن يشاهد نفس الصفحة والمحتوى الذي تراه. أفضل مصطلح لهذا الأمر هو أنك [[wikipedia:Representational_State_Transfer|RESTful]]. اقرأ المزيد عن مقاربة RESTful في [https://www.infoq.com/articles/rest-introduction مقالات ستيفان تيلكوف] (Stefan Tilkov). في بعض الأحيان، هناك استثناءات لهذه القاعدة وتلك التي سنناقشها أدناه.
+
'''تحذير''': قد تميل إلى تخزين المحليَّة المختارة في جلسة عمل أو ملف تعريف ارتباط (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|بديل:عرض لتطبيق ريلز بسيط غير مترجم.|تصغير|500بك|عرض لتطبيق ريلز بسيط غير مترجم.]]
+
[[ملف: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|بديل:ظهور رسالة خطأ تخبر بأن هنالك ترجمات مفقودة.|تصغير|500بك|ظهور رسالة خطأ تخبر بأن هنالك ترجمات مفقودة.]]
+
[[ملف: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|بديل:ظهور السلسلتين النصيتين باللغة الإنجليزية لأن المحلية الافتراضية لم تتغير وهي en:.|تصغير|500بك|ظهور السلسلتين النصيتين باللغة الإنجليزية لأن المحلية الافتراضية لم تتغير وهي en:.]]
+
[[ملف:demo_translated_en_rails.png|تصغير|500بك|ظهور السلسلتين النصيتين باللغة الإنجليزية لأن المحلية الافتراضية لم تتغير وهي en:.|بدون]]
  
إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي [http://localhost:3000?locale=pirate<nowiki>http://localhost:3000?locale=pirate</nowiki>)،] فستصير السلسلتين النصيتين إلى ما يقابلهما لتلك المحلية:
+
إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي http://localhost:3000?locale=pirate<nowiki/>)، فستصير السلسلتين النصيتين إلى ما يقابلهما لتلك المحلية:
  
[[ملف:demo_translated_pirate_rails.png|بديل:تغيّر السلاسل النصية بتغير المحلية إلى pirate.|تصغير|500بك|تغيّر السلاسل النصية بتغير المحلية إلى pirate.]]
+
[[ملف: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|بديل:شكل واجهة لعرض للتطبيق بعد إضافة تنسيق زمني.|تصغير|500بك|شكل واجهة لعرض للتطبيق بعد إضافة تنسيق زمني.]]
+
[[ملف:demo_localized_pirate_rails.png|تصغير|500بك|شكل واجهة لعرض للتطبيق بعد إضافة تنسيق زمني.|بدون]]
  
 
'''تنبيه''': قد تحتاج الآن لإضافة المزيد من تنسيقات التاريخ/الوقت بالترتيب لجعل عمل الواجهة الخلفية I18n مثل المتوقّع (على الأقل بالنسبة للغة "القرصنة").
 
'''تنبيه''': قد تحتاج الآن لإضافة المزيد من تنسيقات التاريخ/الوقت بالترتيب لجعل عمل الواجهة الخلفية I18n مثل المتوقّع (على الأقل بالنسبة للغة "القرصنة").
  
هناك طبعًا فرصة كبيرة أن شخصًا ما أنجز بالفعل كل هذا العمل عن طريق ترجمة افتراضيّات ريلز للغتك المحليّة. راجع [https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale مستودع rails-i18n في GitHub] لأرشيف من ملفات الإعدادات المحليّة المختلفة. عند وضع مثل هذا الملف (أو الملفّات) في المجلّد /config/locales، سيكون جاهزًا للاستخدام تلقائيًا.
+
هناك طبعًا فرصة كبيرة أن شخصًا ما أنجز بالفعل كل هذا العمل عن طريق ترجمة افتراضيّات ريلز للغتك المحليّة. راجع [https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale مستودع rails-i18n في GitHub] لأرشيف من ملفات المحليَّة المختلفة. عند وضع مثل هذا الملف (أو الملفّات) في المجلّد /config/locales، سيكون جاهزًا للاستخدام تلقائيًا.
  
=== قواعد انعطاف لإعدادات محليّة أخرى ===
+
=== قواعد العطف لمحلية أخرى ===
يسمح لك ريلز بتحديد قواعد الإنعطاف (أي inflection مثل قواعد المُفرد والجمع) للإعدادات المحليّة غير الإنجليزية. يمكنك تعريف هذه القواعد لعدّة لغات في config/initializers/inflections.rb. يحتوي المُهيئ على مثال افتراضي لتحديد قواعد إضافيّة للغة الإنجليزية؛ اتبع ذاك التنسيق للغات أخرى كما تراه مناسبا.
+
يسمح لك ريلز بتحديد قواعد العطف (أي inflection مثل قواعد المُفرد والجمع) للإعدادات المحليّة غير الإنجليزية. يمكنك تعريف هذه القواعد لعدّة لغات في config/initializers/inflections.rb. يحتوي المُهيئ على مثال افتراضي لتحديد قواعد إضافيّة للغة الإنجليزية؛ اتبع ذاك التنسيق للغات أخرى كما تراه مناسبا.
  
 
=== العروض المترجمة ===
 
=== العروض المترجمة ===
فلنفترض أنك تمتلك وحدة التحكّم <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> ...إلخ.)
+
فلنفترض أنك تمتلك وحدة التحكّم <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>تستطيع بهذه الطريقة فصل أسماء النموذج وخاصيات النموذج من النص داخل العروض، وكل هذا من "الإعدادات الافتراضيّة" (مثل تنسيقات التاريخ والوقت). يمكن أن توفّر المخازن الأخرى للمكتبة i18n وسائل مختلفة لهذا الفصل.
+
</syntaxhighlight>تستطيع بهذه الطريقة فصل أسماء النموذج وخاصيات النموذج من النص داخل العروض، وكل هذا من "المحلية الافتراضيّة" (مثل تنسيقات التاريخ والوقت). يمكن أن توفّر المخازن الأخرى للمكتبة i18n وسائل مختلفة لهذا الفصل.
  
'''ملاحظة''': لا تُحمّل آلية تحميل الإعدادات المحليّة الإفتراضيّة في ريلز ملفّات الإعدادات المحليّة في القواميس المتداخلة كما هو الحال هنا. لذلك، لكي يعمل هذا، يجب أن نطلب من ريلز بوضوح أن ننظر أبعد من ذلك:<syntaxhighlight lang="rails">
+
'''ملاحظة''': لا تُحمّل آلية تحميل المحليَّة الافتراضيّة في ريلز ملفّات المحليَّة في القواميس المتداخلة كما هو الحال هنا. لذلك، لكي يعمل هذا، يجب أن نطلب من ريلز بوضوح أن ننظر أبعد من ذلك:<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 الآن ومعرفة تمكّنك من فهم كيفية تدويل تطبيق ريلز بسيط. سوف نغطّي في الفصول التالية ميزاته بمزيد من العمق.
+
يجب أن يكون لديك فهمًا جيدًا لاستخدام المكتبة i18n الآن ومعرفةً تمكّنك من فهم كيفية تدويل تطبيق ريلز بسيط. سوف نغطّي في الفصول التالية الميزات بمزيد من العمق.
  
ستعرض هذه الفصول أمثلة باستخدام كل من تابع I18n.translate إضافة للتابع المساعد للعرض translate (مع ملاحظة الميزة الإضافية التي يوفّرها التابع المساعد للعرض).
+
ستعرض هذه الفصول أمثلة باستخدام كل من التابع <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 آمنة ( تابع العرض المساعد)
* توطين التواريخ والأرقام والعملة وما إلى ذلك.
+
* توطين التواريخ والأرقام والعملة وما إلى ذلك
  
 
=== البحث عن الترجمات ===
 
=== البحث عن الترجمات ===
  
==== البحث الأساسي، والنطاقات والمفاتيح المتداخلة (Basic Lookup, Scopes and Nested Keys) ====
+
==== البحث الأساسي، والنطاقات والمفاتيح المتداخلة ====
يُبحث عن الترجمات بالمفاتيح والتي قد تكون رموزًا أو سلاسل بحيث يكون هذان النداءان متساويَين:
+
يُبحث عن الترجمات بالمفاتيح والتي قد تكون رموزًا أو سلاسل بحيث يكون هذان النداءان متساويَين:<syntaxhighlight lang="rails">
{| class="wikitable"
+
I18n.t :message
|I18n.t :message
 
 
 
 
I18n.t 'message'
 
I18n.t 'message'
|}
+
</syntaxhighlight>يأخذ التابع <code>translate</code> أيضًا الخيار <code>scope:</code> الذي قد يحتوي على مفتاح إضافي أو أكثر سيُسخدم لتحديد "مجال اسم" أو نطاق لمفتاح الترجمة:<syntaxhighlight lang="rails">
يأخذ التابع translate أيضًا خيار scope: الذي قد يحتوي على مفتاح إضافي أو أكثر ستُسخدم لتحديد "مساحة اسم" أو نطاق لمفتاح الترجمة:
+
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
{| class="wikitable"
+
</syntaxhighlight>يبحث هذا عن الرسالة <code>record_invalid:</code> في رسائل خطأ [[Rails/active record|Active Record]].
|<nowiki>I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]</nowiki>
 
|}
 
يبحث هذا عن الرسالة record_invalid: في رسائل خطأ السجلّات النشطة Active Record.
 
 
 
إضافة إلى ذلك يمكن تحديد المفتاح والنطاقات كمفاتيح مفصولة بنقط كما في:
 
{| class="wikitable"
 
|I18n.translate "activerecord.errors.messages.record_invalid"
 
|}
 
وبالتالي فإن المكالمات التالية متكافئة:
 
{| class="wikitable"
 
|I18n.t 'activerecord.errors.messages.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]
<nowiki>I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]</nowiki>
+
</syntaxhighlight>
|}
 
  
 
==== الافتراضات (Defaults) ====
 
==== الافتراضات (Defaults) ====
عندما يعطى خيار default: ستُردّ قيمته إن كانت الترجمة مفقودة:
+
عندما يعطى الخيار <code>default:</code>، ستعاد قيمته إن كانت الترجمة مفقودة:<syntaxhighlight lang="rails">
{| class="wikitable"
+
I18n.t :missing, default: 'Not here'
|I18n.t :missing, default: 'Not here'
+
# => 'Not here'
 +
</syntaxhighlight>إذا كانت القيمة <code>default:</code> رمزًا، ستُترجم وتُستخدَم كمفتاح. يمكن للمرء تقديم عدّة قيم كإعداد افتراضي. سيعاد أول واحد منهم تنتج منه قيمة.
  
<nowiki>#</nowiki> => 'Not here'
+
في ما يلي محاولة أولى لترجمة المفتاح <code>missing:</code> ثم المفتاح <code>also_missing:</code>. نظرًا لأن كليهما لا يسفر عن نتيجة، فستعاد السلسلة "Not here":<syntaxhighlight lang="rails">
|}
+
I18n.t :missing, default: [:also_missing, 'Not here']
إذا كانت القيمة default: رمزًا ستُترجم وتُستخدم كمفتاح. يمكن للمرء تقديم عدّة قيم كإعداد افتراضي. سيُردّ أول واحد منهم تنتج منه قيمة.
+
# => 'Not here'
 +
</syntaxhighlight>
  
في ما يلي محاولة أولى لترجمة المفتاح missing: ثم المفتاح also_missing:. نظرًا لأن كليهما لا يسفر عن نتيجة ، فسترد السلسلة "ليس هنا":
+
==== البحث عن مجالات الأسماء والبحث بالجملة ====
{| class="wikitable"
+
يمكن تمرير مصفوفة من المفاتيح للبحث عن عدّة ترجمات في آن واحد:<syntaxhighlight lang="rails">
|<nowiki>I18n.t :missing, default: [:also_missing, 'Not here']</nowiki>
+
I18n.t [:odd, :even], scope: 'errors.messages'
 
+
# => ["must be odd", "must be even"]
<nowiki>#</nowiki> => 'Not here'
+
</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=> ... }
==== البحث عن مجالات الأسماء والبحث بالجملة (ٍBulk and Namespace Lookup) ====
+
</syntaxhighlight>
يمكن تمرير مصفوفة من المفاتيح للبحث عن عدّة ترجمات في آن واحد:
 
{| class="wikitable"
 
|<nowiki>I18n.t [:odd, :even], scope: 'errors.messages'</nowiki>
 
 
 
<nowiki>#</nowiki> => ["must be odd", "must be even"]
 
|}
 
يمكن أيضًا ترجمة المفتاح كتجزئة (قد تكون متداخلة) للترجمات المجمّعة. يمكن للمرء مثلًا تلقّي جميع رسائل خطأ Active Record على أنها تجزئة مع:
 
{| class="wikitable"
 
|I18n.t 'activerecord.errors.messages'
 
 
 
<nowiki>#</nowiki> => {:inclusion=>"is not included in the list", :exclusion=> ... }
 
|}
 
  
 
==== البحث "الكسول" ====
 
==== البحث "الكسول" ====
يعرّف ريلز إستخدام طريقة ملائمة للبحث عن المحليَّة داخل العروض. عندما تمتلك القاموس التالي:
+
يعرّف ريلز استخدام طريقة ملائمة للبحث عن المحليَّة داخل [[Rails/action view overview|العروض]]. عندما تمتلك القاموس التالي:<syntaxhighlight lang="rails">
{| class="wikitable"
+
es:
|es:
+
  books:
 +
    index:
 +
      title: "Título"
  
 books:
+
</syntaxhighlight>يمكنك البحث عن القيمة <code>books.index.title</code> داخل القالب app/views/books/index.html.erb (لاحظ النقطة):<syntaxhighlight lang="html">
 
+
<%= t '.title' %>
   index:
+
</syntaxhighlight>'''ملاحظة''': يتوفّر تحديد الترجمة التلقائية (Automatic translation scoping) جزئيًا فقط من تابع العرض المساعد <code>translate</code>.
 
 
     title: "Título"
 
|}
 
يمكنك البحث عن القيمة books.index.title داخل القالب app/views/books/index.html.erb (لاحظ النقطة):
 
{| class="wikitable"
 
|<%= t '.title' %>
 
|}
 
يتوفّر تحديد الترجمة التلقائية (Automatic translation scoping) جزئيًا فقط من تابع العرض المساعد translate.
 
 
 
يمكن أيضًا استخدام البحث "الكسول" في وحدات التحكّم:
 
{| class="wikitable"
 
|en:
 
 
 
 books:
 
 
 
   create:
 
 
 
     success: Book created!
 
|}
 
هذا مفيد في ضبط رسائل الفلاش مثلًا:
 
{| class="wikitable"
 
|class BooksController < ApplicationController
 
 
 
 def create
 
 
 
<nowiki>#</nowiki> ...
 
 
 
redirect_to books_url, notice: t('.success')
 
 
 
 end
 
  
 +
يمكن أيضًا استخدام البحث "الكسول" في وحدات التحكّم:<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". ملك لغات أخرى ( العربية واليابانية والروسية وغيرها الكثير) قواعد نحويّة مختلفة تحتوي على صيَغ جمع أكثر أو أقل. وبالتالي توفّر واجهة برمجة التطبيقات (I18n) ميزة صيغ الجمع المرنة.
+
في العديد من اللغات - بما في ذلك اللغة الإنجليزية -، هناك صيغتان فقط هما صيغة المفرد والجمع لكلمة معينة (سلسلة نصية)، مثل " 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) ميزة صيغ الجمع المرنة.
 
 
يملك متغير الإستيفاء count: (أي interpolation variable) دورًا خاصًّا من حيث أنه يُقحم بالترجمة ويُستخدم في اختيار صيغ جمع من الترجمات وفقًا لقواعد الجمع المحددّة في خلفيّة الجمع. بشكل افتراضي تُطبّق قواعد الجمع الإنجليزية فقط.
 
{| class="wikitable"
 
|I18n.backend.store_translations :en, inbox: {
 
 
 
 zero: 'no messages', # optional
 
 
 
 one: 'one message',
 
 
 
 other: '%{count} messages'
 
  
 +
يملك متغير الاستيفاء <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'
<nowiki>#</nowiki> => '2 messages'
+
 
 
 
I18n.translate :inbox, count: 1
 
I18n.translate :inbox, count: 1
 
+
# => 'one message'
<nowiki>#</nowiki> => 'one message'
+
 
 
 
I18n.translate :inbox, count: 0
 
I18n.translate :inbox, count: 0
 
+
# => 'no messages'
<nowiki>#</nowiki> => 'no messages'
+
</syntaxhighlight>خوارزميّة الجمع للمحليَّة <code>en:</code> بسيطة مثل:<syntaxhighlight lang="rails">
|}
+
lookup_key = :zero if count == 0 && entry.has_key?(:zero)
خوارزميّة المحليَّة en: بسيطة مثل:
+
lookup_key ||= count == 1 ? :one : :other
{| class="wikitable"
 
|lookup_key = :zero if count == 0 && entry.has_key?(:zero)
 
 
 
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: تعتبر مفردة، و other: في الجمع. إذا كان العدد صفرًا ووُجد إدخال zero: سيُستخدم بدلاً من other:.
 
  
إن لم يرد البحث عن المفتاح تجزئة قابلة للجمع  يُرفع استثناء I18n::InvalidPluralizationData.
+
إن لم يعيد البحث عن المفتاح [[Ruby/Hash|جدول Hash]] قابل للجمع، يُرفَع الاستثناء <code>I18n::InvalidPluralizationData</code>.
  
 
==== القواعد الخاصة بمحليَّة (Locale-specific rules) ====
 
==== القواعد الخاصة بمحليَّة (Locale-specific rules) ====
توفّر الجوهرة I18n خلفية صيغ جمع Pluralization التي يمكن استخدامها لتفعيل قواعد الخاصّة بإعدادات محليّة معيّنة. ضمّها إلى الخلفيّة البسيطة ثم أضف خوارزميات صبغ الجمع المُوطّنة إلى مخزن الترجمة كما مع i18n.plural.rule.
+
توفّر الجوهرة I18n خلفية صيغ جمع (Pluralization backend) التي يمكن استخدامها لتفعيل قواعد خاصّة بإعدادات محليّة معيّنة. ضمنها إلى الخلفيّة Simple ثم أضف خوارزميات صيغ الجمع المُوطّنة إلى مخزن الترجمة كما مع <code>i18n.plural.rule</code>.<syntaxhighlight lang="rails">
{| class="wikitable"
+
I18n::Backend::Simple.include(I18n::Backend::Pluralization)
|I18n::Backend::Simple.include(I18n::Backend::Pluralization)
+
I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
 
 
<nowiki>I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }</nowiki>
 
 
 
 
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>.
  
<nowiki>#</nowiki> => 'one or none'
+
إذا لم يُمرّر أي محليَّة، يُستخدم <code>I18n.locale</code>:<syntaxhighlight lang="rails">
|}
+
I18n.locale = :de
بدلاً من ذلك يمكن استخدام الجوهرة المنفصلة rail-i18n لتوفير مجموعة كاملة من قواعد الجمع الفريدة بإعدادات محليّة معيّنة.
 
 
 
=== ضبط و تمرير محليَّة ===
 
يمكن تعيين اللغة المحليّة إما على شكل I18n.locale (والذي يستخدم Thread.current مثل Time.zone مثلاً) أو يمكن تمريرها كخيار إلى translate# و localize#.
 
 
 
إذا لم يُمرّر أي محليَّة يُستخدم I18n.locale:
 
{| class="wikitable"
 
|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
{| class="wikitable"
 
|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
{| class="wikitable"
+
</syntaxhighlight>
|I18n.default_locale = :de
 
|}
 
  
 
=== استخدام ترجمات HTML آمنة ===
 
=== استخدام ترجمات HTML آمنة ===
تُميّز المفاتيح التي تحتوي على لاحقة "html_" ومفاتيح المسمّاة "html" على أنها آمنة HTML. عند استخدامها في توابع العرض ، لن يتم الإفلات من HTML. عند استخدامها في العروض لن تُهرّب من HTML.
+
تُميّز المفاتيح التي تحتوي على لاحقة "html_" والمفاتيح المسمّاة "html" على أنها آمنة HTML. عند استخدامها في [[Rails/action view overview|العرض]]، لن يتم تهريبها وعدم تفسيرها على أنها ليست شيفرة [[HTML]].<syntaxhighlight lang="rails">
{| class="wikitable"
+
# config/locales/en.yml
|# 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>"
 welcome: <nowiki><b>welcome!</b></nowiki>
+
</syntaxhighlight>يمكنك تمرير اسم المستخدم بأمان كما حدّده المستخدم:<syntaxhighlight lang="html">
 
+
<%# This is safe, it is going to be escaped if needed. %>
 hello_html: <nowiki><b>hello!</b></nowiki>
 
 
 
 title:
 
 
 
   html: <nowiki><b>title!</b></nowiki>
 
 
 
<nowiki>#</nowiki> app/views/home/index.html.erb
 
 
 
<nowiki><div><%= t('welcome') %></div></nowiki>
 
 
 
<nowiki><div><%= raw t('welcome') %></div></nowiki>
 
 
 
<nowiki><div><%= t('hello_html') %></div></nowiki>
 
 
 
<nowiki><div><%= t('title.html') %></div></nowiki>
 
|}
 
الاستيفاء يهرب عند الحاجة رغم ذلك. خذ مثلًا:
 
{| class="wikitable"
 
|en:
 
 
 
 welcome_html: "<nowiki><b>Welcome %{username}!</b></nowiki>"
 
|}
 
يمكنك تمرير اسم المستخدم بأمان كما حدّده المستخدم:
 
{| class="wikitable"
 
|<%# 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) %>
|}
 
تُستوفى السلاسل آمنة من جهة أخرى كما هي حرفيًّا.
 
 
يتوفّر التحويل التلقائي إلى HTML آمن للترجمة النصيّة فقط من تابع العرض المساعد translate.
 
  
=== ترجمات لنماذج السجلّات النشطة ===
+
</syntaxhighlight>تُستوفى السلاسل الآمنة من جهة أخرى كما هي حرفيًّا.
يمكنك استخدام التوابع Model.model_name.human و Model.human_attribute_name(attribute) للبحث عن التراجم بشفافية لأسماء نموذجك وسماتك.
 
  
عند إضافة الترجمات التالية مثلًا:
+
'''ملاحظة''': يتوفّر التحويل التلقائي إلى شيفرة [[HTML]] آمنة للترجمة النصيّة فقط من تابع العرض المساعد <code>translate</code>.
{| class="wikitable"
 
|en:
 
  
 activerecord:
+
[[ملف:demo_html_safe_rails.png|بديل=كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.|تصغير|500بك|كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.|بدون]]
  
models:
+
=== ترجمات لنماذج Active Record ===
 +
يمكنك استخدام التابعين <code>Model.model_name.human</code> و <code>Model.human_attribute_name(attribute)‎</code> للبحث عن الترجمات بشفافية لأسماء نموذجك وخاصياتك.
  
  user: Dude
+
عند إضافة الترجمات التالية مثلًا:<syntaxhighlight lang="rails">
 +
en:
 +
  activerecord:
 +
    models:
 +
      user: Dude
 +
    attributes:
 +
      user:
 +
        login: "Handle"
 +
      # will translate User attribute "login" as "Handle"
  
attributes:
+
</syntaxhighlight>ثم يعيد <code>User.model_name.human</code> الاسم "Dude" و <code>User.human_attribute_name("login")‎</code> يعيد "Handle".
  
  user:
+
يمكنك أيضًا تعيين صيغة الجمع لأسماء النماذج بإضافة ما يلي:<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".
  
    login: "Handle"
+
في حال احتجت للوصول إلى خاصية متداخلة داخل نموذج معيّن، فعليك تضمينها (nest) تحت <code>model/attribute</code> على مستوى النموذج من ملف ترجمتك:<syntaxhighlight lang="rails">
 
+
en:
  # will translate User attribute "login" as "Handle"
+
  activerecord:
|}
+
    attributes:
ثم يرد User.model_name.human الإسم "Dude" و User.human_attribute_name("login") يرد "Handle".
+
      user/gender:
 
+
        female: "Female"
يمكنك أيضًا تعيين صيغة الجمع لأسماء النماذج بإضافة ما يلي:
+
        male: "Male"
{| class="wikitable"
+
</syntaxhighlight>ثم سيعيد <code>User.human_attribute_name("gender.female")‎</code> النتيجة "Female".
|en:
 
 
 
 activerecord:
 
 
 
models:
 
 
 
  user:
 
 
 
    one: Dude
 
 
 
    other: Dudes
 
|}
 
ثم User.model_name.human(count: 2) سيرد "Dudes". أمّا مع count: 1 أو بدون params سيرد "Dude".
 
 
 
في حال إحتجت للوصول إلى سمات متداخلة داخل نموذج معيّن عليك تضمينها (nest) تحت model/attribute على مستوى النموذج  من ملف ترجمتك:
 
{| class="wikitable"
 
|en:
 
 
 
 activerecord:
 
 
 
attributes:
 
 
 
  user/gender:
 
 
 
    female: "Female"
 
 
 
    male: "Male"
 
|}
 
ثم سيرد User.human_attribute_name("gender.female") النتيجة "Female".
 
  
إذا كنت تستخدم صنفًا يتضمّن ActiveModel ولا يرث من ActiveRecord::Base، عوّض activerecord بـ activemodel في المسارات المفتاحيّة المذكورة أعلاه.
+
'''ملاحظة''': إذا كنت تستخدم صنفًا يتضمّن <code>ActiveModel</code> ولا يرث من <code>ActiveRecord::Base</code>، عوّض <code>activerecord</code> بـ <code>activemodel</code> في المسارات المفتاحيّة المذكورة أعلاه.
  
==== نطاقات رسالة الخطأ ====
+
==== مجالات رسالة الخطأ ====
يمكنك أيضًا ترجمة رسائل أخطاء التحقّق من السجل النشطة بسهولة. يمنحك Active Record بضع مجالات أسماء حيث يمكنك وضع ترجمات رسائلك من أجل توفير رسائل وترجمات مختلفة لبعض النماذج، والسمات ، و/أو تحقيقات الصحة (validations). كما يأخذ بشفرة توريث الجدول الواحد (single table inheritance) في الاعتبار بشفافيّة.
+
يمكنك أيضًا ترجمة رسائل أخطاء التحقّق من [[Rails/action view|Active Record]] بسهولة. يمنحك [[Rails/action view|Active Record]] بضع مجالات أسماء حيث يمكنك وضع ترجمات رسائلك من أجل توفير رسائل وترجمات مختلفة لبعض النماذج، والخاصيات، و/أو تحقيقات الصحة (validations). كما يأخذ شفيرة توريث الجدول الواحد (single table inheritance) في الحسبان بشفافيّة.
  
 
هذا يمنحك وسيلة قويّة للغاية لضبط رسائلك حسب احتياجات تطبيقك بمرونة.
 
هذا يمنحك وسيلة قويّة للغاية لضبط رسائلك حسب احتياجات تطبيقك بمرونة.
  
ضع في اعتبارك نموذج مستخدم مع تحقّق من صحة سمة الاسم كما يلي:
+
ضع في اعتبارك النموذج <code>User</code> مع التحقّق من صحة الخاصية <code>name</code> كما يلي:<syntaxhighlight lang="rails">
{| class="wikitable"
+
class User < ApplicationRecord
|class User < ApplicationRecord
+
  validates :name, presence: true
 
 
 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]
{| class="wikitable"
 
|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
{| class="wikitable"
 
|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>عندما تستخدم نماذجك إضافة على ذلك الميراث، يُبحث عن الرسائل في سلسلة الميراث.
عندما تستخدم نماذجك إضافة على ذلك الميراث يُبحث عن الرسائل في سلسلة الميراث.
 
 
 
مثلًا قد يكون لديك نموذج مُشرف يرث من المستخدم:
 
{| class="wikitable"
 
|class Admin < User
 
 
 
 validates :name, presence: true
 
  
 +
مثلًا، قد يكون لديك النموذج <code>Admin</code> يرث من <code>User</code>:<syntaxhighlight lang="rails">
 +
class Admin < User
 +
  validates :name, presence: true
 
end
 
end
|}
 
بعد ذلك سيبحث السجل النشط عن الرسائل بالترتيب التالي:
 
{| class="wikitable"
 
|activerecord.errors.models.admin.attributes.name.blank
 
  
 +
</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> على التوالي.
==== رسالة خطأ الاستيفاء ====
 
دائمًا ما يكون اسم النموذج واسم السمة المترجمين والقيمة متاحين للإستيفاء كـ model و attribute و value على التوالي.
 
 
 
لهذا السبب بدلاً من رسالة الخطأ الإفتراضيّة "cannot be blank" مثلًا يمكنك استخدام اسم السمة كما يلي: "Please fill in your %{attribute}"
 
* يمكن استخدام count إن كان متاحًا لصيغة الجمع إن وُجدت:
 
// جدول 30 * 4 هنا //
 
 
 
==== ترجمة السجل النشط للمُساعد error_messages_for ====
 
إن كنت تستخدم مُساعد السجل النشط error_messages_for ستحتاج لإضافة ترجمات له.
 
  
يُشحن ريلز مع الترجمات التالية:
+
لهذا السبب، بدلًا من رسالة الخطأ الافتراضيّة "cannot be blank" مثلًا، يمكنك استخدام اسم الخاصية كما يلي: <code>"Please fill in your %{attribute}‎"</code>.
 +
* يمكن استخدام <code>count</code> إن كان متاحًا لصيغة الجمع إن وُجدت:
 
{| class="wikitable"
 
{| class="wikitable"
|en:
+
!التحقق
 
+
!مع الخيار
 activerecord:
+
!الرسالة
 
+
!الاستيفاء
errors:
+
|-
 
+
|تأكيد
  template:
+
|<nowiki>-</nowiki>
 
+
|<code>‎:confirmation</code>
    header:
+
|<code>attribute</code>
 
+
|-
      one:   "1 error prohibited this %{model} from being saved"
+
|قبول
 
+
|<nowiki>-</nowiki>
      other: "%{count} errors prohibited this %{model} from being saved"
+
|<code>‎:accepted</code>
 
+
|<nowiki>-</nowiki>
    body: "There were problems with the following fields:"
+
|-
 +
|وجود
 +
|<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>
 
|}
 
|}
تحتاج لتثبيت جوهرة DynamicForm كي تستخدم هذا المساعد بإضافة هذا السطر إلى
 
 
{| class="wikitable"
 
{| class="wikitable"
|Gemfile: gem 'dynamic_form' .
 
 
|}
 
|}
  
=== الترجمات لموضوعات بريد إلكتروني مُرسل الأفعال Action Mailer ===
+
==== ترجمات للمساعد <code>error_messages_for</code> الذي يخص  Active Record  ====
إن لم تقم بتمرير موضوع للتابع mail سيحاول Action Mailer العثور عليه في ترجماتك. سيستخدم البحث المُنفّذ النمط <mailer_scope>.<action_name>.subject لإنشاء المفتاح.
+
إن كنت تستخدم المساعد <code>error_messages_for</code> الذي يخص  [[Rails/active record|Active Record]]، ستحتاج لإضافة ترجمات له.
{| class="wikitable"
+
 
|# user_mailer.rb
+
يُشحن ريلز مع الترجمات التالية:<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)
 def welcome(user)
+
    #...
 
+
  end
<nowiki>#</nowiki>...
 
 
 
 end
 
 
 
 
end
 
end
 
+
</syntaxhighlight><syntaxhighlight lang="rails">
 
en:
 
en:
 
+
  user_mailer:
 user_mailer:
+
    welcome:
 
+
      subject: "Welcome to Rails Guides!"
welcome:
+
</syntaxhighlight>لإرسال المعاملات للاستيفاء، استخدم التابع <code>default_i18n_subject</code> على المُرسل (mailer).<syntaxhighlight lang="rails">
 
+
# user_mailer.rb
  subject: "Welcome to ريلز Guides!"
 
|}
 
لإرسال المعاملات للاستيفاء استخدم التابع default_i18n_subject على المُرسل (mailer).
 
{| class="wikitable"
 
|# user_mailer.rb
 
 
 
 
class UserMailer < ActionMailer::Base
 
class UserMailer < ActionMailer::Base
 
+
  def welcome(user)
 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
 
end
  
 +
</syntaxhighlight><syntaxhighlight lang="rails">
 
en:
 
en:
 +
  user_mailer:
 +
    welcome:
 +
      subject: "%{user}, welcome to Rails Guides!"
 +
</syntaxhighlight>
  
 user_mailer:
+
=== نظرة عامة على توابع مدمجة أخرى توفر دعم I18n ===
 
+
يستخدم ريلز سلاسل نصية ثابتة وتوطينات أخرى مثل سلاسل التنسيق ومعلومات تنسيق أخرى في بضعة مساعدين. فيما يلي نظرة عامة موجزة.
welcome:
 
 
 
  subject: "%{user}, welcome to ريلز Guides!"
 
|}
 
 
 
=== نظرة عامة على توابع مدمجة أخرى توفّر دعم I18n ===
 
يستخدم ريلز سلاسل ثابتة وتوطينات أخرى مثل سلاسل التنسيق ومعلومات تنسيق أخرى في بضعة مساعدين. فيما يلي نظرة عامة موجزة.
 
 
 
==== توابع Action View المساعدة ====
 
* distance_of_time_in_words تُترجم وتَجمَع نتائجها وتستوفي عدد الثواني والدقائق والساعات وما إلى ذلك. انظر ترجمات datetime.distance_in_words.
 
* يستخدم datetime_select و select_month أسماء الشهور المترجمة لملء وسم التحديد (select tag) الناتج. راجع date.month_names للترجمات. يبحث datetime_select أيضًا عن خيار الأمر من 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 إعدادات تنسيق الأرقام الموجودة في نطاق الأرقام.
 
 
 
==== توابع النموذج النشط ====
 
* يستخدم model_name.human و human_attribute_name الترجمات لأسماء نماذج وأسماء السمات إن توفّرت في نطاق activerecord.models. كما أنها تدعم ترجمة لأسماء الفئات الموروثة (مثلًا للاستخدام مع STI) كما هو موضح أعلاه في "نطاقات رسالة الخطأ".
 
* يضيف ActiveModel::Errors#full_messages اسم السمة إلى رسالة الخطأ باستخدام فاصل يُبحث عنه من error.format (وقيمته الافتراضيّة "%{attribute} %{message}" ).
 
 
 
==== توابع الدعم النشطة ====
 
* يستخدم Array#to_sentence إعدادات التنسيق كما أُعطيَت في النطاق support.array.
 
 
 
== كيف تخزّن ترجماتك المخصّصة ==
 
تسمح لك الواجهة الخلفيّة البسيطة المشحونة مع Active Support بتخزين الترجمات بتنسيق ruby ​​و YAML.
 
  
مثلًا يمكن أن يبدو شكل تجزئة Ruby تُقدّم ترجمات على النحو التالي:
+
==== توابع مساعدة للإجراء <code>View</code> ====
{| class="wikitable"
+
* <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>.
  
 pt: {
+
==== توابع الإجراء <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>).
  
foo: {
+
==== توابع 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>.
  
  bar: "baz"
+
== كيف تخزن ترجماتك المخصصة ==
 +
تسمح لك الواجهة الخلفيّة 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:
سيبدو ملف YAML المكافئ كالتالي:
+
      default: "%Y-%m-%d"
{| class="wikitable"
+
      short: "%b %d"
|pt:
+
      long: "%B %d, %Y"
 
 
 foo:
 
 
 
   bar: baz
 
|}
 
كما ترى، في كلتا الحالتين يكون مفتاح المستوى الأعلى هو اللغة المحليّة. foo: هو مفتاح مجال الاسم و bar: هو مفتاح لترجمة "baz".
 
 
 
في ما يلي مثال "حقيقي" من ملف ترجمة YAML للدعم النشط en.yml.
 
{| class="wikitable"
 
|en:
 
 
 
 date:
 
 
 
formats:
 
 
 
  default: "%Y-%m-%d"
 
 
 
  short: "%b %d"
 
 
 
  long: "%B %d, %Y"
 
|}
 
لذلك سترد جميع عمليّات البحث الموافقة التالية تنسيق short: للتاريخ "b %d%":
 
{| class="wikitable"
 
|I18n.t 'date.formats.short'
 
  
 +
</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]
  
I18n.t :short, scope: 'date.formats'
+
</syntaxhighlight>نوصي بشكل عام باستخدام YAML كامتداد لتخزين الترجمات. مع ذلك، هناك حالات تريد تخزين تعابير lambda لروبي كجزء من بيانات المحليّة مثل من أجل تنسيقات التاريخ الخاصّة.
 +
== تخصيص إعداد I18n الخاص بك ==
  
<nowiki>I18n.t :short, scope: [:date, :formats]</nowiki>
+
=== استخدام واجهات خلفية متنوعة ===
|}
+
لأسباب كثيرة، كل ما تفعله الواجهة الخلفيّة Simple التي تُشحن مع [[Rails/active support|Active Support]] هو "أبسط شيء يمكنه العمل" في ريلز مما يعني أنها مضمونة العمل باللغة الإنجليزية فقط، ومع اللغات المشابهة جدًا للغة الإنجليزية كتأثير جانبي. كما أن الواجهة الخلفيّة Simple قادرة على قراءة الترجمات فقط لا على تخزينها ديناميكيًا بأي شكل.
نوصي بشكل عام باستخدام YAML كامتداد لتخزين الترجمات. مع ذلك هناك حالات حيث تريد تخزين Ruby lambdas كجزء من بيانات الإعدادات المحليّة. لتنسيقات التاريخ الخاصّة مثلًا.
 
  
== بتخصيص إعدادك I18n ==
+
لكن هذا لا يعني أنك عالق مع هذه القيود. تُسهّل جوهرة I18n لروبي استبدال تعريف استخدام الواجهة الخلفية Simple بشيء آخر يناسب احتياجاتك بشكل أفضل من خلال تمرير نسخة خلفيّة إلى <code>I18n.backend= setter</code>.
  
=== استخدام خلفيّات متنوّعة ===
+
على سبيل المثال، يمكنك استبدال الواجهة الخلفيّة Simple مع الواجهة الخلفية Chain لسَلسَلة عدّة واجهات خلفية معًا. يُفيدك هذا عندما تريد استخدام ترجمات قياسية مع الواجهة الخلفية Simple ولكن مع تخزين ترجمات تطبيق مخصّصة بقاعدة بيانات أو في الواجهات الخلفية الأخرى.
لأسباب كثيرة كل ما تفعله الواجهة الخلفيّة البسيطة التي تُشحن مع Active Support هو "أبسط شيء يمكنه العمل" في Ruby on ريلز ... مما يعني أنها مضمونة العمل باللغة الإنجليزية فقط، ومع اللغات المشابهة جدا للغة الإنجليزية كتأثير جانبي. كما أن الواجهة الخلفيّة البسيطة قادرة على قراءة الترجمات فقط لا على تخزينها ديناميكيًا بأي شكل.
 
  
لكن هذا لا يعني أنك عالق مع هذه القيود. تُسهّل جوهرة I18n Ruby إستبدال تعريف إستخدام الخلفيّة البسيطة بشيء آخر يناسب احتياجاتك بشكل أفضل، من خلال تمرير نسخة خلفيّة إلى I18n.backend= setter.
+
تستطيع مع الواجهة الخلفية 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
  
تستطيع مع الخلفيّة السلسلة استخدام خلفيّة السجل النشط Active Record والعودة إلى الخلفيّة (الافتراضيّة) البسيطة:
+
#  nil غير صالحة أي مثلًا I18n.locale المحليَّة المعطاة إلى
{| class="wikitable"
+
InvalidLocale
|I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
 
|}
 
  
=== استخدام معالجات استثناء مختلفة ===
+
# مُرِّر خيار عددي لكن الترجمة غير مناسبة للجمع
تحدّد واجهة برمجة التطبيقات I18n API الاستثناءات التالية التي ستُرفع من طرف الخلفيّات عند حدوث الظروف غير المتوقعة المقابلة:
+
InvalidPluralizationData
{| class="wikitable"
 
|MissingTranslationData    # تعذر العثور على أي ترجمة للمفتاح المطلوب
 
  
InvalidLocale             #  nil غير صالح أي مثلًا I18n.locale المحليَّة المُعطى إلى
+
# تتوقع الترجمة وسيط استيفاء لم يُمرّر
 +
MissingInterpolationArgument
  
InvalidPluralizationData # مُرّر خيار عددي لكن الترجمة غير مناسبة للجمع
+
# تحوي الترجمة اسم متغير استيفاء محجوز
 +
ReservedInterpolationKey
  
MissingInterpolationArgument # تتوقع الترجمة متغيّرًا وسيط مستوفى لم يُمرّر
+
# I18n.load_path لا تعرف الواجهة الخلفية كيفية التعامل مع نوع الملف الذي أُضيف إلى
 +
UnknownFileType            
  
ReservedInterpolationKey # the translation contains a reserved interpolation variable name (i.e. one of: scope, default)
 
  
UnknownFileType            # I18n.load_path  لا تعرف الخلفيّة  كيفية التعامل مع نوع الملف الذي أُضيف إلى
+
</syntaxhighlight>ستلتقط الواجهة البرمجية I18n كل هذه الاستثناءات عندما تُرمَى في الخلفيّة وتمرّرها إلى التابع <code>default_exception_handler</code>. سيعيد هذا التابع رمي كافّة الاستثناءات سوى استثناءات <code>MissingTranslationData</code>. عند التقاط الاستثناء <code>MissingTranslationData</code>، سيعيد سلسلة رسالة خطأ الاستثناء الذي يحتوي على المفتاح/النطاق المفقود.
|}
 
سيلتقط I18n API كل هذه الاستثناءات عندما تُطرح في الخلفيّة يُمرّرها إلى التابع default_exception_handler. سيعيد هذا التابع رفع كافّة الاستثناءات باستثناء استثناءات MissingTranslationData. عند إلتقاط استثناء MissingTranslationData ،سيرد سلسلة رسالة خطأ الاستثناء الذي يحتوي على المفتاح/النطاق المفقود.
 
  
 
يرجع السبب في ذلك إلى أنه أثناء التطوير عادة ما ترغب في تصيير عروضك حتى وإن كانت الترجمة مفقودة.
 
يرجع السبب في ذلك إلى أنه أثناء التطوير عادة ما ترغب في تصيير عروضك حتى وإن كانت الترجمة مفقودة.
  
مع ذلك قد ترغب في سياقات أخرى بتغيير هذا السلوك. مثلًا لا تسمح معالجة الاستثناءات الإفتراضيّة بالتقاط الترجمات المفقودة أثناء الاختبارات الاليّة بسهولة. لهذا الغرض يمكن تحديد معالج استثناء مختلف. يجب أن يكون معالج الاستثناء المحدد تابعًا بوحدة I18n أو صنف مع تابع call#:
+
مع ذلك قد ترغب في سياقات أخرى بتغيير هذا السلوك. مثلًا، لا تسمح معالجة الاستثناءات الإفتراضيّة بالتقاط الترجمات المفقودة أثناء الاختبارات الآليّة بسهولة. لهذا الغرض، يمكن تحديد معالج استثناء مختلف. يجب أن يكون معالج الاستثناء المحدد تابعًا بوحدة I18n أو صنفًا مع التابع <code>call.</code>:<syntaxhighlight lang="rails">
{| class="wikitable"
+
module I18n
|module I18n
+
  class JustRaiseExceptionHandler < ExceptionHandler
 
+
    def call(exception, locale, key, options)
 class JustRaiseExceptionHandler < ExceptionHandler
+
      if exception.is_a?(MissingTranslationData)
 
+
        raise exception.to_exception
def call(exception, locale, key, options)
+
      else
 
+
        super
  if exception.is_a?(MissingTranslationData)
+
      end
 
+
    end
    raise exception.to_exception
+
  end
 
 
  else
 
 
 
    super
 
 
 
  end
 
 
 
 
end
 
end
 
+
 end
 
 
 
end
 
 
 
 
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new
 
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new
|}
+
</syntaxhighlight>سيعيد هذا رمي الاستثناء <code>MissingTranslationData</code> فقط، مع تمرير كل الإدخالات (input) الأخرى إلى معالج الاستثناء الافتراضي.
سيعيد هذا رفع الاستثناء MissingTranslationData فقط، مع تمرير كل الإدخالات (input) الأخرى إلى معالج الاستثناء الافتراضي.
 
 
 
ولكن إن كنت تستخدم I18n::Backend::Pluralization سيرفع هذا المعالج أيضًا إستثناء
 
{| class="wikitable"
 
|I18n::MissingTranslationData: translation missing: en.i18n.plural.rule
 
|}
 
الذي يجب تجاهله عادةً عودة إلى قاعدة صيغ الجمع الإفتراضيّة للغة الإنجليزية. تستطيع استخدام تحقّق (check) إضافي لمفتاح الترجمة لتجنّب هذا:
 
{| class="wikitable"
 
|if exception.is_a?(MissingTranslationData) && key.to_s != 'i18n.plural.rule'
 
 
 
 raise exception.to_exception
 
  
 +
ولكن إن كنت تستخدم <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
 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# (وكذلك #translate ). عند وقوع استثناء MissingTranslationData في هذا السياق يلف هذا المساعد الرسالة في نطاق باستخدام صنف  CSS مسمّى  translation_missing.
 
  
لفعل ذلك يجبر المساعد I18n#translate على رفع الاستثناءات بغض النظر عن معالج الاستثناء المُعرّف عن طريق تحديد الخيار raise: :
+
لفعل ذلك، يجبر المساعد <code>I18n.translate</code> على رفع الاستثناءات بغض النظر عن معالج الاستثناء المُعرّف عن طريق تحديد الخيار <code>raise:</code>:<syntaxhighlight lang="rails">
{| class="wikitable"
+
I18n.t :foo, raise: true # always re-raises exceptions from the backend
|I18n.t :foo, raise: true # always re-raises exceptions from the backend
+
</syntaxhighlight>
|}
 
  
 
== ترجمة محتوى النموذج ==
 
== ترجمة محتوى النموذج ==
واجهة برمجة تطبيقات I18n المُوصوفة في هذا الدليل تهدف بشكل أساسي لترجمة سلاسل الواجهة النصيّة. إن كنت تريد ترجمة محتوى نموذج (مثل مشاركات المدونّات) ستحتاج إلى حل مختلف للمساعدة على ذلك.
+
الواجهة البرمجية 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 في Ruby on ريلز وأن تكون مستعدًا لبدء ترجمة ‎مشروعك.
+
في هذه المرحلة، يجب أن يكون لديك نظرة عامة جيدة حول كيفية دعم I18n في ريلز وأن تكون مستعدًا لبدء ترجمة ‎مشروعك عبره.
  
== موارد ==
+
== مصادر مرجعية ==
* مجموعة Google: rails-i18n - القائمة البريدية للمشروع.
+
* [https://groups.google.com/forum/#!forum/rails-i18n مجموعة Google التي تخص rails-i18n] - القائمة البريدية للمشروع.
* GitHub: rails-i18n - مستودع الشفرات ومتتبع إصدارات مشروع 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، وتصيّر الاستجابة السلاسل النصية باللغة الإنجليزية:

ظهور السلسلتين النصيتين باللغة الإنجليزية لأن المحلية الافتراضية لم تتغير وهي en:.

إن عيّنت المحليّة عبر عنوان URL إلى المحلية pirate (أي http://localhost:3000?locale=pirate)، فستصير السلسلتين النصيتين إلى ما يقابلهما لتلك المحلية:

تغيّر السلاسل النصية بتغير المحلية إلى 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.

كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.
كيفية عرض السلاسل الآمنة وتفسيرها إلى شيفرة HTML وعدمه.

ترجمات لنماذج 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 في ريلز وأن تكون مستعدًا لبدء ترجمة ‎مشروعك عبره.

مصادر مرجعية

المؤلفون

مصادر