BookTranslator
BookTranslator

كيفية ترجمة ملف لغة JSON دون كسر المفاتيح أو العناصر النائبة

تعرّف على القيم التي تُترجم في ملف en.json، وما الذي يعيد مترجم الملفات كتابته (التعليقات، ترتيب المفاتيح، المعرّفات الكبيرة)، وكيفية التحقق من العناصر النائبة وصيغ الجمع في ICU والمفاتيح باستخدام نص برمجي مجاني وملف اختبار.

BookTranslator

BookTranslator Team

قراءة 11 دقيقة

لترجمة ملف لغة JSON مثل en.json، سلّم الملف بأكمله إلى مترجم يفهم JSON. لصق النص الخام في نافذة محادثة أو مترجم ويب هو الطريقة المحفوفة بالمخاطر. المترجم الذي يفهم JSON يقرأ الملف، ويترجم القيم النصية فقط، ويعيد كتابة المفاتيح نفسها، حتى يتمكن تطبيقك من تحميل الملف. النسخ واللصق قد يترجم مفتاحًا، أو يغيّر علامة اقتباس، أو يُسقط قوسًا. أي من هذه يفسد الملف.

الحفاظ على المفاتيح هو الجزء السهل. معظم المشاكل تأتي بعد ذلك: عنصر نائب {name} يعود كـ {nom}، وصيغة جمع ICU تفقد حالة other، وعنوان URL يُرسل للترجمة كأي نص آخر، وبعض التغييرات التي يُجريها المترجم على الملف نفسه. هذه هي ما يغطيه هذا الدليل.

أنشأنا ملف اختبار يحتوي على كل تلك الحالات، وكتبنا أداة تحقق مجانية تقارن الملف المصدر بترجمته. كما شغّلنا ملف الاختبار عبر نسخة محلية من كود تحليل وكتابة JSON الذي يستخدمه BookTranslator، مع علامة بدلًا من نموذج، حتى ترى بالضبط أي القيم تُرسل وكيف يعود الملف.

إجابة سريعة: ماذا يحدث لكل جزء من الملف

جزء الملفمثال من اختبارناما يفعله BookTranslatorما يجب التحقق منه
المفاتيح"greeting", "error"تُحفظ كما هيلا شيء
القيم النصية العادية"Reading list"تُترجمالمعنى، الطول على الشاشة
القيم التي تحتوي على عناصر نائبة"Welcome back, {name}!", "Saved %s of %d chapters"تُترجم، مع تضمين العناصر النائبة في النص المُرسلأسماء العناصر النائبة وأعدادها (تتولى أداة التحقق ذلك)
رسائل جمع ICU"{count, plural, one {# book} other {# books}}"تُرسل كسلسلة واحدةحالة other، وفئات الجمع للغة الهدف
HTML المضمّن"accept the <b>Terms</b>."يُترجم؛ يُتحقق من عدد الوسوم بعد الترجمةأسماء الوسوم وأعدادها (يتحقق منها المدقق)
عناوين URL المخزنة كنص"https://example.com/help"تُرسل للترجمةأنها عادت دون تغيير
القيم التي لا تحتوي على حروف"2.4.1", "—", ""تُركت كما هيلا شيء
الأرقام، true/false، null20, trueتُركت كما هيمعرّفات كبيرة جدًا (انظر أدناه)
مصفوفات النصوص["Open", "Save", "Export as EPUB"]تُترجم في الملف المترجم؛ وتُترك دون ترجمة في الملف ثنائي اللغةالملف ثنائي اللغة، إذا استخدمته
التعليقات (JSONC)// Shown on the home screenتُزال. إذا كانت القيمة تحتوي أيضًا على //، فقد يفشل تحميل الملفأزل التعليقات قبل الرفع

يوضح باقي هذا الدليل مصدر كل صف وما ينبغي فعله بشأنه.

الخطوة 1: تحقق من الملف المصدر قبل الرفع

شغّل المدقق على الملف الذي تخطط لترجمته. يحتاج إلى Python 3 ولا شيء غير ذلك:

python3 check_i18n_json.py en.json

في ملف الاختبار الخاص بنا، طبع:

WARN  limits.projectId: 9007199254740993 is above 2^53 and can change in a JavaScript round trip; store IDs as strings
WARN  error.network: a flat key with dots and a nested key resolve to the same dot path; tools that address strings by dot path can mix them up
INFO  (root): 3 keys look like integers; JavaScript sorts those first and in numeric order when it rewrites the object
INFO  source: 17 strings, 2 numbers

التحذيران والملاحظة حول المفاتيح الشبيهة بالأعداد الصحيحة يتوافق كل منها مع تغيير لاحظناه عندما مر ملف الاختبار الخاص بنا عبر المترجم. إذا لم يحصل ملفك على أي منها، يمكنك الانتقال إلى الخطوة 3.

كما يرفض المدقق ملفًا ليس JSON صارمًا، مثل ملف يحتوي على تعليقات أو فواصل زائدة، لأن تلك هي المشكلة التالية.

ما عاد من ملف الاختبار الخاص بنا

أُجري الاختبار في 2 نوفمبر 2026، على معالجة JSON الحالية في الواجهة الخلفية لـ BookTranslator. بدلًا من استدعاء نموذج، استبدل الاختبار كل قيمة كان سيتم إرسالها للترجمة بـ [ZH] متبوعة بالنص الأصلي. يحافظ ذلك على قابلية التنبؤ بالنتيجة: أي شيء يسبقه [ZH] أُرسل، وأي شيء لا يسبقه لم يُرسل. كلا المخرجين منشوران: الملف المترجم والملف ثنائي اللغة.

الملف المترجم

عاد كل مفتاح دون تغيير، وظل الملف قابلاً للتحليل. أُرسلت القيم التي تحتوي على حروف، بما في ذلك {name} و%s ورسالة ICU ووسم <b> ورابط المساعدة. لم تُرسل سلسلة الإصدار "2.4.1" ولا الشرطة ولا السلسلة الفارغة، لأنها لا تحتوي على حروف. تُركت الأرقام والقيم المنطقية دون مساس.

تغيّرت أربعة أمور أخرى:

  1. انتقلت سلسلة نصية إلى مفتاح خاطئ. في اختبارنا، كان في ملفنا مفتاح مسطح "error.network" يحتوي على النص "Check your connection." وأيضاً مفتاح متداخل "error": { "network": "Network error" }. يتعرف المترجم على كل سلسلة نصية عبر مسارها مع نقاط بين الأجزاء، لذا كان لكلا السلسلتين العنوان نفسه، error.network. في المخرجات، حمل كلا المفتاحين ترجمة "Network error". اختفى "Check your connection.". يتطلب هذا وجود النمطين في الملف نفسه، وهو أمر نادر، لكنه يفشل بصمت. ينبهك المدقق إلى ذلك قبل الرفع.
  2. أُعيد ترتيب المفاتيح الشبيهة بالأعداد الصحيحة. عاد "steps": { "10": ..., "2": ..., "1": ... } بالشكل "1"، "2"، "10". تسرد JavaScript دائماً المفاتيح الشبيهة بالأعداد الصحيحة أولاً، بترتيب عددي، عند إعادة بناء كائن. لا تزال المفاتيح والقيم متطابقة، لذا يعمل تطبيقك بشكل جيد، لكن الفرق مقابل الملف المصدر سيكون مشوشاً.
  3. فقد معرّف كبير دقته. عاد 9007199254740993 بالشكل 9007199254740992. تخزن JavaScript الأرقام كأعداد عائمة 64 بت، ولا يمكن تمثيل جميع الأعداد الصحيحة فوق 2^53 بدقة (MDN حول Number.MAX_SAFE_INTEGER). خزّن المعرّفات الطويلة كسلاسل نصية، أو أبقها خارج ملف اللغة.
  4. أُعيدت كتابة التنسيق. تستخدم المخرجات مسافة بادئة من مسافتين وتنتهي بسطر جديد. إذا استخدم مصدرك علامات جدولة أو أربع مسافات، فتوقع تغييرات في المسافات البيضاء ضمن الفرق.

لم يكن في ملف اختبارنا مفاتيح مكررة، لكن من المفيد معرفتها: يحتفظ محلل JSON في JavaScript بالقيمة الأخيرة فقط للمفتاح المتكرر، لذا لا يُترجم النص السابق أبداً. سيحمّل تطبيقك القيمة الأخيرة نفسها، لذا لا شيء مرئي ينكسر، لكن المدقق يسرد المفاتيح المكررة لتتمكن من تنظيفها.

الملف ثنائي اللغة

يحتفظ الملف ثنائي اللغة بكل قيمة أصلية ويضيف مفتاحاً شقيقاً ينتهي بـ _translated:

"greeting": "Welcome back, {name}!",
"greeting_translated": "[ZH] Welcome back, {name}!",

يفيد ذلك في المراجعة، لأن المصدر والترجمة متجاوران. لكنه ليس ملفاً يمكن لتطبيقك تحميله، لأن كل مفتاح أصبح له توأم. استخدم الملف المترجم في تطبيقك.

ثمة ثغرة: السلاسل النصية داخل المصفوفات لا تحصل على شقيق _translated، لأن عناصر المصفوفة لا تملك مفتاحاً لإضافة واحد إليه. في اختبارنا، عاد "menu": ["Open", "Save", "Export as EPUB"] بالإنجليزية في الملف ثنائي اللغة، لكنه مترجم في الملف المترجم. إذا راجعت من الملف ثنائي اللغة، فتحقق من المصفوفات في الملف المترجم.

الملفات التي تحتوي على تعليقات

تحتفظ مشاريع كثيرة بتعليقات في ملفات لغتها (JSONC)، وهو ما لا يسمح به JSON الصارم. يحاول المترجم أولاً JSON الصارم. إذا فشل ذلك، يزيل التعليقات والفواصل اللاحقة ويحاول مجدداً. إزالة التعليقات تعني حذف كل ما يلي // في السطر، وهذا يشمل // في https://.

لم يُحمَّل ملفنا الذي يحتوي على تعليق ورابط URL إطلاقاً؛ فشل التحليل عند أول تعليق. أما الملف الذي يحتوي على تعليقات لكن دون // داخل أي قيمة فقد حُمّل بسلاسة، وأُسقطت التعليقات من المخرجات.

إذن: إذا كان ملفك يحتوي على تعليقات، فأزلها قبل الرفع، أو احتفظ بنسخة مع التعليقات واقبل أن الترجمة لن تحتوي عليها.

الخطوة 2: جهّز الملف

يمكنك إصلاح معظم هذه المشكلات قبل الترجمة:

  • اجعله JSON صارماً. أزل التعليقات والفواصل اللاحقة. إذا كانت أداة البناء لديك تقرأ JSONC، فاحتفظ بالملف المعلّق كمصدر وصدّر نسخة نظيفة للترجمة.
  • استخدم نمط مفتاح واحداً. إما أن تتداخل المفاتيح أو تسطحها بالنقاط، وليس كليهما في ملف واحد. إذا كنت تستخدم i18next، فإن خيار keySeparator يتحكم في كيفية قراءة النقاط في المفاتيح، لذا تحقق من ذلك الإعداد قبل تسطيح أي شيء.
  • انقل البيانات غير النصية إلى الخارج. المعرّفات وعناوين URL وأعلام الميزات والحدود تنتمي إلى الإعدادات، وليس إلى ملف اللغة. إذا كان لا بد من بقاء عنوان URL، فأبقه، لكن توقع التحقق منه بعد الترجمة. يغطي دليلنا لما لا يجب ترجمته كيفية تحديد ما ينبغي أن يبقى أيضاً باللغة الأصلية.
  • اكتب العناصر النائبة بالطريقة نفسها في كل مكان. نمط واحد، مثل {name}، أسهل في التحقق من مزيج من {name} و{{name}} و%s.

الخطوة 3: ترجم الملف بأكمله

ارفع الملف المنظف إلى مترجم المستندات من BookTranslator، الذي يسرد .json مع صيغ التوطين والنصوص المهيكلة الأخرى التي يقبلها. يظهر السعر قبل الدفع. عند انتهاء المهمة، تعرض صفحة سجلك الملف المترجم والملف ثنائي اللغة كتنزيلين منفصلين.

هناك أمران يتعلقان بكيفية ترجمة النص يؤثران على النتيجة:

  • لا يرى النموذج مفاتيحك. تُرسل كل قيمة كجزء مرقّم، دون اسم المفتاح. إذا كان "button.open" و "status.open" كلاهما يعني "Open"، فلا سبيل للنموذج لمعرفة أن أحدهما فعل والآخر صفة. السلاسل القصيرة الغامضة هي التي يجب فحصها يدويًا.
  • تُترجم كل قيمة على حدة. لا شيء يربط مفتاحين معًا، لذا يمكن أن تعود الكلمة نفسها في موضعين بصيغتين مختلفتين، ويمكن أن تعود كلمة تحتاج إلى ترجمتين مختلفتين بالصيغة نفسها. يجمع القاموس التلقائي الاختياري في BookTranslator الأسماء والأماكن والمصطلحات المتخصصة ويحافظ على اتساقها، لكن لا يمكنك تزويده بترجمات ثابتة من عندك. بالنسبة للمصطلحات التي يجب ترجمتها بطريقة واحدة محددة، احتفظ بقائمة مصطلحات وافحص تلك السلاسل بعد الترجمة؛ يشرح دليل القاموس كيفية التخطيط لواحد.

تُحسب وسوم HTML المضمّنة بعد الترجمة، وتُعاد محاولة ترجمة الجزء الذي لا تتطابق وسومه. تُرسل العناصر النائبة ذات الأقواس المعقوفة، والعناصر النائبة بنمط %s، وصيغة ICU كجزء من النص ولكن لا يتم فحصها بالطريقة نفسها. نفّذ الخطوة 4 على كل ترجمة.

إذا كنت تفضل الترجمة باستخدام نموذج محادثة أو نموذج محلي، فأرسل القيم فقط، وليس الملف الخام أبدًا، ثم أعدها إلى مواضعها حسب المفتاح باستخدام برنامج نصي. تنطبق الفحوصات أدناه في كلتا الحالتين.

الخطوة 4: فحص الملف المترجم

قارن الترجمة بالمصدر:

python3 check_i18n_json.py en.json fr.json

لإظهار ما يكتشفه، أنشأنا ملفًا فرنسيًا بأخطاء مزروعة. إنه مصنوع يدويًا، وليس ناتج نموذج. بعد نفس تحذيرات المصدر كما في الخطوة 1، طبع المدقق:

ERROR app.greeting: placeholders differ; lost ['name'] extra ['nom']
ERROR app.help_url: urls differ; lost ['https://example.com/help'] extra ['https://example.com/aide']
ERROR app.items: ICU message has no 'other' case
ERROR app.legacy: placeholders differ; lost ['%d'] extra []
ERROR limits.maxFiles: type changed from int to str
ERROR limits.projectId: non-text value changed: 9007199254740993 -> 9007199254740992
INFO  menu[1]: identical to source: 'Save'
INFO  compared 20 source values, 1 unchanged strings

كل واحد من هذه سيظهر كخطأ في التطبيق: اسم فارغ، رابط معطّل، تعطل أو رسالة خام عند عرض صيغة الجمع، عدد فصول مفقود، حد يُقارن كنص. يسرد سطر INFO السلاسل التي عادت دون تغيير. بعضها مقبول، مثل أسماء العلامات التجارية؛ أما "Save" في قائمة فرنسية فليس كذلك.

يقارن المدقق المفاتيح وأنواع القيم والقيم غير النصية والعناصر النائبة ({name}، {{name}}، %s، %1$s، %@) وأسماء وسائط ICU ووسوم HTML وعناوين URL. وهو لا يحكم على الترجمة نفسها.

من المفترض أن تتغير حالات الجمع

تسرد صيغة الجمع في ICU رسالة واحدة لكل فئة جمع. الإنجليزية لها فئتان، one و other، لكن العديد من اللغات لها مجموعة مختلفة. الترجمة الروسية الجيدة لرسالة items لدينا ستستخدم عادةً one و few و many و other، واليابانية ستستخدم other فقط. لذا فإن عدم التطابق هنا ليس خطأً دائمًا.

تأتي الفئات من قواعد الجمع في Unicode CLDR، و other هي الفئة الموجودة في كل لغة. هذه هي الفئات الأصلية التي أبلغ عنها Intl.PluralRules في Node.js مع CLDR 48:

اللغةفئات الجمع
الإنجليزية، الألمانيةone, other
الفرنسية، الإسبانية، البرتغاليةone, many, other
الروسية، البولنديةone, few, many, other
العربيةzero, one, two, few, many, other
اليابانية، الصينية، الكوريةأخرى

تستخدم الفرنسية والإسبانية والبرتغالية الفئة many فقط للأعداد المستديرة الكبيرة جدًا، مثل المليون، لذا يمكن للرسالة الخاصة بالأعداد الصغيرة عادةً الاستغناء عنها. يُبلغ المدقق عن غياب other كخطأ، وأي تغيير آخر في الفئات كتحذير للمقارنة مع هذا الجدول. يمكنك طباعة القائمة لأي لغة بنفسك:

node -e 'console.log(new Intl.PluralRules("ru").resolvedOptions().pluralCategories)'

يُعطى المترجم الحالات الإنجليزية، لذا تحقق مما عاد. إذا كانت رسالة روسية أو عربية لا تزال تحتوي على one وother فقط، فأضف الحالات المفقودة يدويًا، أو اطلب من مترجم القيام بذلك.

ثم حمّله في التطبيق

يثبت المدقق أن الملف آمن للتحميل، وليس أنه يُقرأ جيدًا. بدّل التطبيق إلى اللغة الجديدة وألقِ نظرة على الشاشات: الكلمات الطويلة في الألمانية أو الفنلندية التي تفيض عن الزر، والنص من اليمين إلى اليسار في العربية أو العبرية، والتواريخ أو الأرقام التي ينسقها كودك بشكل منفصل عن ملف اللغة. يغطي دليلنا حول الترجمة مقابل التوطين ما يتغير أيضًا بين الأسواق إلى جانب الكلمات.

المشكلات الشائعة

الملف المترجم لا يُحمّل في تطبيقي. شغّل المدقق عليه. إذا لم يتمكن المدقق من تحليله، فقد تم تعديل الملف بعد الترجمة أو لم يكن صالحًا من البداية. إذا تم تحليله، فابحث عن أسطر ERROR المتعلقة بالمفاتيح أو الأنواع.

تظهر سلسلة نصية كرسالة ICU خام، مثل {count, plural, ...}. تعطلت صيغة ICU أثناء الترجمة، عادةً بسبب قوس مفقود أو حالة other مفقودة. انسخ البنية من المصدر وترجم الكلمات داخل كل حالة فقط.

ملفي هو YAML أو ملف .po أو Markdown. يقبل BookTranslator هذه أيضًا، لكن لكل تنسيق قواعده الخاصة. للتوثيق، راجع دليل ترجمة Markdown؛ لصفحات الويب، راجع دليل ملف HTML.

هل يمكنني ترجمة المفاتيح الجديدة فقط؟ ليس ضمن تحميل واحد؛ تتم ترجمة الملف بأكمله. ضع المفاتيح الجديدة في ملف منفصل، وترجمه، ثم ادمج النتيجة حسب المفتاح. سيؤكد المدقق أن الملف المدمج يحتوي على كل مفتاح.

هل يجب أن أترجم المفاتيح؟ لا. المفاتيح هي معرّفات يبحث عنها كودك. القيم فقط هي النص الذي يقرأه الأشخاص.

مقالات ذات صلة