كيف تعمل المزامنة
يُعد الأمر sync العملية الأساسية في rosetta. إليك ما يحدث عند تشغيل npx i18n-rosetta sync.
نظرة عامة على مسار العمل
خطوة بخطوة
1. تحليل الإعدادات (Config Resolution)
يقوم rosetta بتحميل i18n-rosetta.config.json (أو يكتشف الإعدادات تلقائيًا). ويقوم بتحديد:
- اللغة المصدر واللغات المستهدفة
- مخطط الأزواج (أي مجموعات المصدر→الهدف التي ستتم معالجتها)
- إعدادات الطريقة، والنموذج، والجودة لكل زوج
قبل فحص الملفات، يطبع rosetta ترويسة بدء التشغيل:
i18n-rosetta v3.3.2
[INFO] Detected format: json (auto)
[INFO] Detected framework: Hugo
- شعار الإصدار (Version banner): يعرض الإصدار المثبت لأغراض تصحيح الأخطاء وتقارير المشكلات.
- اكتشاف التنسيق (Format detection): يُبلغ عن تنسيق الملف وما إذا تم اكتشافه تلقائيًا
(auto)أو تم تكوينه صراحةً(config). يدعمjson، وtoml، وyaml. - اكتشاف إطار العمل (Framework detection): عند تعيين
contentDir، يحدد إطار العمل (Hugo) لتأكيد تنشيط مزامنة المحتوى.
2. فحص المصدر
يتم تحميل ملف اللغة المصدر وتسطيحه إلى خريطة مفتاح→قيمة (key→value map):
// Input (nested)
{ "hero": { "title": "Welcome", "subtitle": "Build" } }
// Flattened
{ "hero.title": "Welcome", "hero.subtitle": "Build" }
3. اكتشاف التغييرات
يقرأ rosetta ملف .i18n-rosetta.lock، والذي يخزن تجزئات SHA-256 لقيم المصدر المترجمة مسبقًا. لكل مفتاح، يتحقق مما يلي:
| الحالة | الإجراء |
|---|---|
| المفتاح مفقود من الهدف | ترجمة (Translate) |
| تغيرت تجزئة المصدر منذ آخر مزامنة | إعادة ترجمة (Re-translate) (قديم) |
القيمة المستهدفة تبدأ بـ [EN] | إعادة ترجمة (علامة احتياطية قديمة) |
| تجزئة المصدر لم تتغير، والمفتاح موجود | تخطي (Skip) |
لهذا السبب يترجم rosetta ما تغير فقط — فهو لا يعيد ترجمة ملفك بالكامل في كل عملية مزامنة.
4. التجميع في دفعات (Batching)
يتم تجميع المفاتيح في دفعات (الافتراضي: 80 مفتاحًا/دفعة لـ LLM، و 128 لـ Google Translate). يقلل التجميع من رحلات الذهاب والإياب لـ API مع إبقاء المطالبات (prompts) قابلة للإدارة.
أثناء الترجمة، يعرض rosetta شريط تقدم مضمن يتم تحديثه بعد اكتمال كل دفعة:
[INFO] fr.json — 2,847 missing
████████████████░░░░░░░░░░░░░░░░ 1,440/2,847 keys
يتم عرض الشريط باستخدام إرجاع السطر \r للتحديثات في نفس المكان — بدون تمرير. يتم إخفاؤه في وضعي --quiet و --json.
4ب. ذاكرة الترجمة (Translation Memory)
قبل التجميع، يتحقق rosetta من ذاكرة التخزين المؤقت لذاكرة الترجمة (.rosetta/tm.json). المفاتيح التي يتطابق نصها المصدر + اللغة + الطريقة مع ترجمة سابقة يتم تقديمها فورًا من ذاكرة التخزين المؤقت — لا حاجة لاستدعاء API.
[TM] 142 key(s) served from cache
Translating 3 key(s) to French (llm)... [OK]
تُعد ذاكرة الترجمة (TM) الآلية الأساسية لتوفير التكاليف. إعادة تشغيل المزامنة بعد تغيير مفتاح واحد يؤدي إلى ترجمة ذلك المفتاح فقط، وليس الملف بأكمله. راجع ذاكرة الترجمة للحصول على التفاصيل.
لتجاوز ذاكرة التخزين المؤقت لتشغيل واحد: i18n-rosetta sync --no-tm
5. الترجمة
يتم إرسال كل دفعة إلى طريقة الترجمة المكونة:
llm: مطالبة مهيكلة إلى OpenRouter مع تعليمات توجيهية للسياق (register) والجنس.llm-coached: نفس الشيء، ولكن مع إدراج القواعد النحوية، والقاموس، وملاحظات الأسلوب.google-translate: طلب دفعة عبر Google Cloud Translation API v2.api: طلب HTTP POST إلى نقطة نهاية (endpoint) بعيدة.
رسالة النظام (السياق، توجيهات الجنس، القواعد) متطابقة عبر الدفعات للغة معينة، مما يتيح التخزين المؤقت للمطالبات (prompt caching) — يقوم مزودون مثل Anthropic و Google بتخزين رسائل النظام المتكررة مؤقتًا، مما يقلل من تكاليف الرموز (tokens).
6. بوابة الجودة (Quality Gate)
يتم التحقق من صحة كل ترجمة قبل كتابتها على القرص. يتم تشغيل خمسة فحوصات:
| الفحص | ما يكتشفه | مثال |
|---|---|---|
| فارغ/خالٍ (Empty/blank) | لم يُرجع النموذج شيئًا | "" |
| صدى المصدر (Source echo) | أرجع النموذج الإدخال الإنجليزي | "Welcome" للغة اليابانية |
| حلقة الهلوسة (Hallucination loop) | تكرار الثلاثيات (trigrams) | "Qo' Qo' Qo' Qo'" |
| تضخم الطول (Length inflation) | المخرجات أطول بـ 4 أضعاف أو أكثر من المصدر | مصدر بـ 10 أحرف ← مخرجات بـ 50 حرفًا |
| التوافق مع نظام الكتابة (Script compliance) | نظام كتابة خاطئ للغة | نص لاتيني للغة العربية |
يتم تسجيل حالات الفشل ببادئة [GATE]. لا توجد إجراءات احتياطية صامتة (silent fallbacks).
راجع بوابة الجودة للحصول على التفاصيل.
6ب. التحقق من المصطلحات
بالنسبة للأزواج الموجهة (coached pairs) التي تحتوي على قاموس، يتحقق rosetta مما إذا كان LLM قد استخدم بالفعل المصطلحات المطلوبة بعد الترجمة. يتم تسجيل الانتهاكات كتحذيرات [TERM]:
[TERM] en→fr: 2 term violation(s)
• "dashboard" → expected "tableau de bord" but got "panneau"
هذه تحذيرات، وليست أخطاء حظر — لا يزال يتم كتابة الترجمة.
7. سلسلة إعادة المحاولة (Retry Cascade)
عند فشل تحليل JSON أو حدوث أخطاء على مستوى الدفعة، يعيد rosetta المحاولة بدفعات أصغر تدريجيًا:
Full batch (80 keys) → Failed
└→ Half batch (40 keys) → 1 failure
└→ Individual keys (1 each) → Isolates the problem key
يتم تقييد ميزانية إعادة المحاولة بواسطة maxRetries (الافتراضي: 3) لمنع الإنفاق المفرط للرموز.
8. الكتابة والقفل (Write & Lock)
تتم كتابة الترجمات الناجحة في ملف اللغة المستهدفة، مع الحفاظ على بنية التداخل الأصلية. يتم تحديث ملف القفل بتجزئات SHA-256 الجديدة.
9. التحقق (Verification)
بعد معالجة جميع الأزواج، يعيد rosetta قراءة ملفات اللغة المكتوبة من القرص ويقوم بتشغيل تمريرة تحقق (ما لم يتم تعيين --no-verify). يكتشف هذا الفجوة بين إبلاغ المزامنة بالنجاح وكون المفاتيح خاطئة في الواقع:
- تكافؤ المفاتيح (Key parity) — جميع مفاتيح المصدر موجودة في كل هدف.
- علامات
[EN]الاحتياطية — علامات قديمة من عمليات تشغيل سابقة. - ترجمات فارغة — قيم فارغة تسربت.
- التوافق مع نظام الكتابة — لغات غير لاتينية بترجمات تحتوي على ASCII فقط.
- الحفاظ على العناصر النائبة (Placeholder preservation) — العناصر النائبة لـ ICU تتطابق مع المصدر.
- مشكلات الترميز (Encoding issues) — علامات BOM، والأحرف غير المرئية.
يتوفر هذا أيضًا كأمر i18n-rosetta verify مستقل لبوابات CI.
ترجمة المحتوى (المرحلة 2)
بالنسبة لمشاريع Docusaurus و Hugo، يُشغل sync مرحلة ثانية بعد ترجمة مفاتيح JSON. تترجم هذه المرحلة ملفات Markdown و MDX (المستندات، منشورات المدونة، البرامج التعليمية) باستخدام نفس الطرق وبوابة الجودة.
كيف تعمل
- يكتشف rosetta جميع ملفات المحتوى المصدر (
.md،.mdx) عن طريق التنقل في دليل content/docs. - لكل زوج من الملفات × اللغة، يتحقق من ملف قفل محتوى منفصل (
.i18n-rosetta-content.lock) بحثًا عن تغييرات في تجزئة SHA-256. - يتم تجميع الملفات المتغيرة أو المفقودة في مجموعة عناصر عمل مسطحة (flat work-item pool).
- تتم معالجة المجموعة بـ تزامن متوازٍ (parallel concurrency) (الافتراضي: 12 استدعاء API متزامنًا).
Phase 2: content (79 translations to process, 341 skipped, concurrency: 48)
[1/79] (1%) docs/concepts/security.md → ja [RE-TRANSLATE] (~3328s left)
[2/79] (3%) docs/concepts/security.md → th [RE-TRANSLATE] (~1821s left)
...
[79/79] (100%) blog/v3-2-quality.md → de [OK]
[OK] Created 79 content file(s), 341 unchanged
التوازي (Parallelism)
تعمل الآن كل من المرحلة 1 (مفاتيح JSON) والمرحلة 2 (المحتوى) بالتوازي:
- المرحلة 1: تنطلق جميع ترجمات اللغات بشكل متزامن (الافتراضي: 50 لغة متزامنة). داخل كل لغة، تعمل دفعات API أيضًا بالتوازي (4 دفعات متزامنة). تكتمل مزامنة 12 لغة مع 120 مفتاحًا في حوالي دقيقة واحدة بدلاً من 15 دقيقة.
- المرحلة 2: تتم ترجمة جميع مجموعات الملفات × اللغات كمجموعة مسطحة (الافتراضي: 12 استدعاء API متزامنًا). تتم ترجمة الملفات المختلفة واللغات المختلفة في وقت واحد.
تحكم في التوازي باستخدام --json-concurrency، أو --content-concurrency، أو --concurrency (يعين كليهما):
# Faster JSON sync (more parallel locale translations)
npx i18n-rosetta sync --json-concurrency 30
# Faster content sync (more parallel API calls)
npx i18n-rosetta sync --content-concurrency 20
# Slower (gentler on rate limits)
npx i18n-rosetta sync --concurrency 4
حماية المحتوى
أثناء الترجمة، يحمي rosetta المحتوى غير القابل للترجمة:
- كتل التعليمات البرمجية (Code blocks) (المحاطة والمسافة البادئة) يتم استبدالها بعناصر نائبة.
- حقول Frontmatter غير الموجودة في قائمة
translatableFieldsيتم الحفاظ عليها كما هي. - الروابط (Links)، ومسارات الصور، وعلامات HTML محمية.
- الرموز القصيرة (Shortcodes) ومتغيرات الاستيفاء (مثل
{count}،{{.Params.title}}) محمية.
بعد الترجمة، تتم استعادة جميع العناصر النائبة والتحقق من صحتها. إذا كان أي منها مفقودًا أو تالفًا، يتم رفض الترجمة وإعادة المحاولة.
النجاح الجزئي
فشل دفعة واحدة لا يعيق الباقي. إذا نجحت 9 دفعات من أصل 10، تتم كتابة هذه الـ 9. يتم تسجيل الدفعة الفاشلة، ويمكنك إعادة تشغيل sync لإعادة المحاولة.
التشغيل التجريبي (Dry Run)
معاينة ما سيتغير دون كتابة أي ملفات:
npx i18n-rosetta sync --dry-run
فرض إعادة الترجمة (Force Re-translate)
فرض إعادة ترجمة مفاتيح معينة حتى لو لم تتغير:
npx i18n-rosetta sync --force-keys "hero.title,nav.about"
تقدير التكلفة
قبل الترجمة، يُنشئ rosetta تقرير تكلفة ما قبل المزامنة يوضح التكاليف المقدرة لكل زوج. يتم تشغيل هذا تلقائيًا أثناء كل sync — تراه قبل إجراء أي استدعاءات لـ API.
╔══════════════════════════════════════════════════════════╗
║ Cost Estimate ║
╠════════════╦═══════╦════════════╦════════════════════════╣
║ Pair ║ Keys ║ Est. Cost ║ Method ║
╠════════════╬═══════╬════════════╬════════════════════════╣
║ en → fr ║ 142 ║ $0.07 ║ google-translate ║
║ en → ja ║ 38 ║ — ║ llm (model-dependent) ║
║ en → crk ║ 38 ║ — ║ llm-coached ║
╚════════════╩═══════╩════════════╩════════════════════════╝
ما يتم تقديره
توفر كل طريقة ترجمة تقدير التكلفة الخاص بها:
| الطريقة | أساس التكلفة | الدقة |
|---|---|---|
google-translate | السعر المنشور من Google (20 دولارًا/مليون حرف) | دقيق |
llm | يختلف حسب نموذج OpenRouter | يعتمد على النموذج — راجع أسعار OpenRouter |
llm-coached | نفس llm بالإضافة إلى رموز سياق التوجيه (coaching context tokens) | يعتمد على النموذج |
api | يحدده الخادم | غير معروف — لا يمكن التقدير دون الاستعلام من نقطة النهاية |
عندما لا تتمكن طريقة ما من تحديد التكلفة (طرق LLM، واجهات برمجة التطبيقات البعيدة)، يُبلغ rosetta بـ — بدلاً من التخمين. استخدم --dry لرؤية تقديرات التكلفة دون الترجمة فعليًا.
انظر أيضًا
- مرجع واجهة سطر الأوامر (CLI) — المزامنة — علامات وخيارات الأوامر
- ذاكرة الترجمة — التخزين المؤقت وتوفير التكاليف
- بوابة الجودة — كيف يتم التحقق من صحة الترجمات
- طرق الترجمة — كيف تعمل كل طريقة
- العمل مع المترجمين المحترفين — سير عمل XLIFF
- التكوين (Configuration) — مرجع الإعدادات
- دليل CI/CD — أتمتة عمليات المزامنة في مسار عملك