المقالات والمعرفة
بناء موقع Astro متعدد اللغات بعقود قابلة للاختبار
منهج عملي يربط المسارات واللغة واتجاه النص ونموذج المحتوى وhreflang باختبارات صريحة، بدل التعامل مع الترجمة كنسخ صفحات فقط.
المشكلة ليست ترجمة النص وحدها
قد يعرض الموقع ثلاث لغات ومع ذلك يظل عقده غير واضح: صفحة لا تملك نظيرًا مترجمًا، رابط اللغة يذهب إلى الرئيسية، lang صحيح لكن dir خاطئ، أو hreflang يشير إلى عنوان نسبي أو صفحة غير موجودة. الحل ليس زيادة الشروط في المكونات، بل تعريف عقود صغيرة يمكن فحصها.
هذا المثال يفترض:
- العربية هي اللغة الافتراضية في الجذر؛
- الإنجليزية تحت
/en/والتركية تحت/tr/؛ - ليست كل مادة طويلة مترجمة إلى كل لغة؛
- المحتوى غير المعتمد لا يظهر في البناء العام.
يدعم Astro تحديد locales وdefaultLocale، ويجعل prefixDefaultLocale: false صفحات اللغة الافتراضية في الجذر. لكن إعداد المسارات لا يحل وحده اتجاه النص أو تكافؤ المحتوى أو بيانات البحث.
العقد الأول: المسار يحدد لغة واحدة
اجعل استخراج اللغة وتحويل المسار مسؤولية دوال مركزية. يجب ألا تبني المكونات عناوين اللغات بتقطيع النصوص بطرق مختلفة.
type Locale = 'ar' | 'en' | 'tr';
const prefixes: Record<Locale, string> = {
ar: '',
en: '/en',
tr: '/tr',
};
export function localeFromPath(pathname: string): Locale {
const first = pathname.split('/').filter(Boolean)[0];
return first === 'en' || first === 'tr' ? first : 'ar';
}
export function localizedPath(locale: Locale, basePath: string): string {
const normalized = basePath === '/' ? '/' : `/${basePath.replace(/^\/+|\/+$/g, '')}/`;
return `${prefixes[locale]}${normalized}` || '/';
}اختبر على الأقل:
| الإدخال | اللغة | المسار الإنجليزي المناظر |
|---|---|---|
/ | ar | /en/ |
/about/ | ar | /en/about/ |
/tr/projects/ | tr | /en/projects/ |
إذا لم توجد الصفحة المناظرة، لا تفترض وجودها. إما تخفي خيار اللغة لذلك المحتوى، أو توجه إلى فهرس اللغة مع رسالة صريحة. الرابط الصامت إلى مادة مختلفة يضلل المستخدم ومحرك البحث.
العقد الثاني: اللغة لا تعني اتجاه النص
يجب أن يحمل عنصر html كلًا من lang وdir:
---
const locale = 'ar';
const direction = locale === 'ar' ? 'rtl' : 'ltr';
---
<html lang={locale} dir={direction}>يوصي W3C بوضع dir="rtl" على html عندما يكون اتجاه الوثيقة العام من اليمين إلى اليسار، ويحذر من استخدام CSS لتحديد الاتجاه الأساسي. استخدم خصائص CSS المنطقية كي يتكيف التخطيط:
.card {
padding-inline: 1rem;
margin-block-end: 1.5rem;
border-inline-start: 0.25rem solid currentColor;
}أما النص الذي يدخله المستخدم ولا تعرف لغته، فقد يحتاج dir="auto". لا تطبقه على كل الصفحة؛ الاتجاه الصريح للوثيقة أكثر ثباتًا.
العقد الثالث: هوية المحتوى منفصلة عن عنوانه
العنوان قابل للتغيير والترجمة، لذلك لا يصلح مفتاحًا للربط. استخدم translationKey ثابتًا، مع لغة وحالة نشر ودرجة إتاحة:
locale: 'ar'
translationKey: 'multilingual-astro-contracts'
status: 'draft'
visibility: 'private'
title: 'بناء موقع Astro متعدد اللغات بعقود قابلة للاختبار'تسمح Content Collections في Astro بتحميل المحتوى والتحقق من بنيته. أضف قاعدة تفشل إذا كانت المادة public من دون حالة published أو وصف وتواريخ مراجعة. هذا يجعل «خاص افتراضيًا» قرارًا تنفيذيًا، لا تعليمات تحريرية فقط.
فلتر النشر يجب أن يكون إيجابيًا:
const visible = entries.filter(
({ data }) =>
data.locale === locale &&
data.status === 'published' &&
data.visibility === 'public' &&
(!data.publishedAt || data.publishedAt <= now),
);تجنب منطق «انشر كل شيء إلا draft»؛ أي حالة جديدة قد تصبح عامة بالخطأ.
العقد الرابع: canonical وhreflang يعكسان الواقع
لكل صفحة عنوان canonical مطلق يمثل النسخة الحالية. وعندما تتوفر نسخ لغوية، يجب أن تشير كل نسخة إلى نفسها وإلى النسخ الأخرى بعناوين كاملة. تشترط إرشادات Google أن تكون عناوين hreflang كاملة، وتوصي بالربط المتبادل بين النسخ.
<link rel="canonical" href={new URL(currentPath, siteOrigin)} />
<link rel="alternate" hreflang="ar" href={new URL(arPath, siteOrigin)} />
<link rel="alternate" hreflang="en" href={new URL(enPath, siteOrigin)} />
<link rel="alternate" hreflang="x-default" href={new URL(arPath, siteOrigin)} />لا تولّد وسمًا للتركية إذا لم توجد نسخة تركية فعلية للمقال. يجب أن تأتي قائمة اللغات المتاحة من مجموعة الترجمات الموجودة، لا من قائمة لغات الموقع العامة.
في بيئة المعاينة، ابنِ canonical وsitemap على أصل المعاينة وأضف noindex. لا تنشر روابط مطلقة إلى نطاق لا تملكه؛ فهذا يجعل الدليل التقني يصف أصلًا مختلفًا عن الصفحة المستلمة.
العقد الخامس: اختبر المصفوفة لا مثالًا واحدًا
اختبار الرئيسية العربية لا يثبت صلاحية ثلاث لغات. أنشئ مصفوفة للمسارات العامة، ثم طبّق عليها الفحوص نفسها:
1. حالة HTTP ناجحة؛
2. html[lang][dir] صحيحان؛
3. canonical يطابق المسار؛
4. بدائل اللغة الموجودة متبادلة ولا تقود إلى 404؛
5. لا يوجد تجاوز أفقي عند 320 CSS px؛
6. لا مخالفات وصول آلية من مستوى A/AA المختار؛
7. الروابط المحلية والأجزاء #fragment موجودة؛
8. صفحة المسودة لا تدخل sitemap أو البحث.
أضف اختبارًا سلبيًا: مادة draft/private يجب ألا تملك مسار تفاصيل عامًا. اختبار النجاح وحده لا يثبت أن بوابة النشر مغلقة.
ما الذي لا يحله هذا التصميم؟
- لا يثبت جودة الترجمة؛ يلزم مراجع بشري مستقل.
- لا يضمن تكافؤ كل اللغات؛ الترجمة الانتقائية قرار تحريري يجب إظهاره بصدق.
- لا يحدد وحده سياسة fallback. إعادة كتابة محتوى عربي تحت عنوان إنجليزي قد تضلل
langوSEO. - لا يستبدل اختبار قارئ الشاشة أو Safari الفعلي.
- لا يبرر التحويل التلقائي بحسب لغة المتصفح إذا كان سيمنع المستخدم من اختيار نسخة ثابتة ومشاركتها.
قائمة قبول مختصرة
- لغة افتراضية ومسارات موثقة.
langوdirعلى مستوى الوثيقة.- CSS منطقي لا يعتمد على
leftوrightللبنية الأساسية. translationKeyثابت وقائمة لغات متاحة من المحتوى الفعلي.- فلتر نشر إيجابي وحالة افتراضية خاصة.
- canonical وhreflang مطلقان ومتبادلان.
- اختبارات لكل المسارات والاتجاهات والحالات السلبية.
- مراجعة لغوية بشرية قبل الإطلاق.
المراجع
- Astro: Internationalization routing
- Astro: Content collections
- Google Search Central: Localized versions of pages
- W3C Internationalization: Structural markup and right-to-left text
سجل التحديث
- 2026-10-09: اعتُمد النشر من صاحب الموقع، وأُعيد فحص وثائق Astro وW3C الأصلية. الموافقة على النشر لا تعني إنجاز مراجعة لغوية بشرية مستقلة، وتبقى قائمة القبول دليلًا للقارئ لا شهادة بجاهزية أي موقع.
- 2026-10-04: المسودة الأولى، مبنية على تنفيذ واختبارات المنصة الحالية ووثائق Astro وGoogle وW3C الأصلية. تحتاج مراجعة تقنية ولغوية مستقلة قبل النشر.