‏%@ و%1$@ و%lld: محددات التنسيق التي تنكسر عند الترجمة

آخر تحديث: · LocalePilot

يحتوي نص iOS الذي فيه متغير على %@ أو %lld. على المترجم أن يحافظ عليه، في المكان الصحيح وبالنوع الصحيح. إليك الأخطاء التي تتكرر، وأحدها خاص بالفرنسية، وطريقة رصدها.

مستعجل؟ افحص متغيرات ترجمة ما مباشرة في متصفحك: مجانًا، دون تسجيل، ودون رفع أي شيء.

من أين تأتي علامة %

في تطبيق iOS، يصبح النص المترجم الذي يحتوي على متغير نصَّ تنسيق (format string). وفي SwiftUI يحدث ذلك ضمنيًا:

Text("\(count) items left")                  // key: "%lld items left"
Text("Welcome back, \(name)!")               // key: "Welcome back, %@!"
String(localized: "Total: \(price) €")       // price is a Double: "Total: %lf €"

قيمة Int تعطي %lld، وقيمة String تعطي %@، وقيمة Double تعطي %lf. ومع الواجهة الأقدم تكتب التنسيق بنفسك: String(format: NSLocalizedString("items_left", comment: ""), count)، ويحتوي ملف .strings على %d أو %ld.

في الحالتين، يتلقى المترجم نصًا في وسطه علامات %، وهنا تنكسر الأمور.

ثلاث طرق لكسر ترجمة

حذف المحدد. "Invitez à modifier cette liste" بدل "Invitez %@ à modifier cette liste". لا شيء يتعطل: يُتجاهل المعامل ببساطة ويختفي اسم الشخص من الشاشة. إنه الخطأ الأكثر شيوعًا والأكثر صمتًا.

تغيير النوع. استبدال %lld بـ%@، أو العكس. يقرأ التنسيق حينها المعامل بنوع خاطئ: في أحسن الأحوال رقم بلا معنى، وفي أسوئها تعطل التطبيق، لأن %@ ينتظر كائنًا (object) فيتلقى عددًا صحيحًا.

تغيير الترتيب بصمت. النص "%@ shared %@" يتحول، في لغة بترتيب كلمات مختلف، إلى جملة يتبادل فيها الاسمان موضعيهما. الصياغة سليمة، والمعنى لا. وهذا شائع في العربية، حيث يأتي الفعل غالبًا قبل الفاعل.

إعادة الترتيب بالمواضع

لتغيير الترتيب، رقّم المعاملات: %1$@ هو المعامل الأول، و%2$@ الثاني، أينما وقعا في الجملة.

/* source */      "%@ shared %@ with you" = "%1$@ shared %2$@ with you";
/* French */      "%@ shared %@ with you" = "%1$@ vous a partagé %2$@";
/* other order */ "%@ shared %@ with you" = "%2$@ : partagé par %1$@";

قاعدة واحدة يجب تذكرها: داخل النص الواحد، إما أن يُرقَّم كل شيء أو لا شيء. خلط %1$@ و%@ سلوك غير معرّف (undefined behavior).

بالعربية مثلًا: "%@ shared %@ with you" = "شارك %1$@ معك %2$@"; يبقى الترقيم صحيحًا مهما كان اتجاه النص.

فخ فرنسي: «20 % de remise»

تضع قواعد الطباعة الفرنسية مسافة قبل علامة النسبة المئوية: «20 % de remise» (خصم 20%). وفي نص التنسيق هذه مشكلة، لأن % d (علامة النسبة، مسافة، d) محدد صالح: المسافة علم (flag) في printf («أضف مسافة قبل الأعداد الموجبة») وd تحويل العدد الصحيح. فالجملة

"Profitez de %lld % de remise"

تحتوي إذن على محددين، %lld و% d، لمعامل واحد. والثاني يقرأ قيمة غير موجودة. الصيغة الصحيحة تضاعف العلامة:

"Profitez de %lld %% de remise"

لا يخص هذا إلا النصوص التي تمر عبر التنسيق، أي النصوص التي فيها متغير واحد على الأقل. عبارة ثابتة «20 % de remise» بلا معامل تُعرض كما كُتبت. لكن الترجمات تتطور: يوم يضيف أحدهم متغيرًا إلى هذه الجملة، تصبح علامة % المنفردة خطأً برمجيًا.

صيغ الجمع

لعبارة مثل «1 item / 3 items»، لا تكتب مفتاحين وشرط if في الكود. في String Catalog، يحصل المفتاح %lld items left على تنويعات جمع (one وother، إضافة إلى few أو many بحسب اللغة؛ وتستخدم العربية الفئات الست: zero وone وtwo وfew وmany وother)، ولكل منها %lld الخاص بها. وعندما تحتوي الجملة على عددين يتغيران باستقلال، يستخدم Xcode الاستبدالات (%#@items@): حالة أندر، تُحرَّر في Xcode.

الفحص قبل النشر

الفحص الأساسي آلي: لكل ترجمة، يجب أن تطابق قائمة المحددات (النوع والموضع) قائمة المصدر. هذا ما يفعله فحص الجودة في LocalePilot على ملفات .strings و.xcstrings: يتعرف على %@ و%1$@ و%d و%ld و%lld و%.2f و%u و%i و%s، ويعامل %% على أنه نص، ويرصد المحدد الناقص أو الزائد قبل التصدير. ويتجاهل عمدًا التحويلات النادرة مثل %e أو %x، تحديدًا كي لا يُعدّ «20%en» خطأً. أما حالة «% d» أعلاه فتحتاج إلى مراجعة بشرية: ابحث في ترجماتك الفرنسية عن % تليها مسافة.

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

ما الفرق بين %d و%ld و%lld؟

حجم العدد الصحيح المتوقع. يستخدم SwiftUI المحدد %lld لقيمة Int مُدرجة؛ وفي ملف .strings مكتوب يدويًا، احتفظ بمحدد المصدر.

كيف أكتب علامة % في نص مترجم؟

في نص يحتوي على متغيرات، ضاعفها: %%. وإلا فقد تُقرأ "% d" أو "% s" على أنها محددات.

هل يمكن للمترجم تغيير ترتيب المتغيرات؟

نعم، باستخدام المواضع: %2$@ ثم %1$@. لكن يجب حينها ترقيم النص كله.