404 Not Found

404 Not Found


nginx

التدويل (i18n)

يريد MegaShop دخول السوق الياباني والعالمي، لكن جميع صفحاته بالصينية فقط. لا تستطيع Alice رؤية الأسعار بالين في اليابان، والمستخدمون اليابانيون لا يستطيعون العثور على المنتجات بأسمائها اليابانية. يحتاج Charlie إلى i18n لتمكين MegaShop من دعم التبديل بين الصينية والإنجليزية واليابانية، مع تنسيق الأسعار تلقائياً حسب العملة.

1. ما ستتعلمه


2. قصة حقيقية لمهندس معماري

(1) نقطة الألم: قيود اللغة الواحدة على العولمة

موقع MegaShop بالصينية فقط، لذلك لا يستطيع المستخدمون اليابانيون فهمه، والمستخدمون الأمريكيون لا يجدون المنتجات بأسمائها الإنجليزية. جميع الأسعار معروضة باليوان، وعندما ترى Alice في أمريكا "¥2,999" لا تعرف إن كان بالين أم باليوان.

(2) حل @nuxtjs/i18n

وحدة Nuxt i18n تفعّل تلقائياً دعم تعدد اللغات لكل صفحة، واستراتيجية URL الخاصة بها صديقة لـ SEO:

TEXT
/en/products/123  → صفحة إنجليزية، سعر بالدولار
/ja/products/123  → صفحة يابانية، سعر بالين
/zh/products/123  → صفحة صينية، سعر باليوان

(3) الفوائد: تغطية السوق العالمي

بعد ثلاثة أشهر، زادت الزيارات من اليابان بنسبة 200٪ وزيارات أمريكا بنسبة 150٪؛ وسوم hreflang مكّنت Google من فهرسة كل نسخة لغية بشكل صحيح.


3. سير عمل معالجة اللغات في i18n

(1) سير عمل اكتشاف اللغة → التحميل → العرض

100%
flowchart TB
    A[طلب المستخدم] --> B{اكتشاف اللغة}
    B -->|Cookie| C[استخدام اللغة المحفوظة]
    B -->|عنوان المتصفح| D[مطابقة Accept-Language]
    B -->|الافتراضي| E[استخدام defaultLocale: en]
    C --> F[تحميل ملف الترجمة]
    D --> F
    E --> F
    F --> G[تعيين بادئة URL]
    G --> H[عرض الصفحة باللغة المحددة]
    H --> I[إنشاء وسوم hreflang]
    ```

---

## 4. تكوين @nuxtjs/i18n

### (1) التثبيت والتكوين الأساسي

```bash
npm install @nuxtjs/i18n

(1) ▶ مثال: تكوين وحدة i18n

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n'],

  i18n: {
    locales: [
      { code: 'en', name: 'English', file: 'en.json', currency: 'USD' },
      { code: 'zh', name: 'Chinese', file: 'zh.json', currency: 'CNY' },
      { code: 'ja', name: 'Japanese', file: 'ja.json', currency: 'JPY' }
    ],
    defaultLocale: 'en',
    lazy: true,
    langDir: 'locales/',
    strategy: 'prefix_except_default',  // / للإنجليزية، /zh/...، /ja/...
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n-locale',
      redirectOn: 'root'
    }
  }
})

الناتج:

TEXT
// التنفيذ ناجح

(2) مقارنة استراتيجيات URL

الاستراتيجية URL الإنجليزية URL الصينية فعالية SEO
prefix_except_default /products /zh/products ✅ مُوصى بها
prefix /en/products /zh/products ✅ متطابقة
prefix_and_default /en/products + /products /zh/products ⚠️ مكررة
no_prefix /products /products ❌ لا يمكن التمييز

4. إدارة ملفات الترجمة

(1) ▶ مثال: هيكل ملفات الترجمة

TEXT
locales/
├── en.json    # الإنجليزية
├── zh.json    # الصينية
└── ja.json    # اليابانية

الناتج:

TEXT
التنفيذ ناجح

(2) ▶ مثال: ملف الترجمة الإنجليزية

JSON
{
  "common": {
    "home": "Home",
    "products": "Products",
    "cart": "Cart",
    "search": "Search",
    "signIn": "Sign In",
    "signOut": "Sign Out"
  },
  "product": {
    "addToCart": "Add to Cart",
    "outOfStock": "Out of Stock",
    "reviews": "{count} reviews",
    "price": "Price: {amount} {currency}",
    "freeShipping": "Free shipping on orders over {amount} USD"
  },
  "cart": {
    "empty": "Your cart is empty",
    "total": "Total: {amount}",
    "checkout": "Proceed to Checkout",
    "items": "{count} items"
  }
}

الناتج:

JSON
{
  "common": {
    "home": "Home",
    "products": "Products",
    "cart": "Cart",
    "search": "Search",
    "signIn": "Sign In",
    "signOut": "Sign Out"
  },
  "product": {
    "addToCart": "Add to Cart",
    "outOfStock": "Out of Stock",
    "reviews": "{count} reviews",
    "price": "Price: {amount} {currency}",
    "freeShipping": "Free shipping on orders over {amount} USD"
  },
  "cart": {
    "empty": "Your cart is empty",
    "total": "Total: {amount}",
    "checkout": "Proceed to Ch

(3) ▶ مثال: ملف الترجمة الصينية

JSON
{
  "common": {
    "home": "Home Page",
    "products": "Products",
    "cart": "Shopping Cart",
    "search": "Search",
    "signIn": "Sign In",
    "signOut": "Sign Out"
  },
  "product": {
    "addToCart": "Add to Cart",
    "outOfStock": "Out of stock",
    "reviews": "{count} reviews",
    "price": "Price: {amount} {currency}",
    "freeShipping": "Free shipping on orders of {amount} yuan or more"
  },
  "cart": {
    "empty": "Your cart is empty",
    "total": "Total: {amount}",
    "checkout": "Proceed to Checkout",
    "items": "{count} items"
  }
}

الناتج:

JSON
{
  "common": {
    "home": "Home Page",
    "products": "Products",
    "cart": "Shopping Cart",
    "search": "Search",
    "signIn": "Sign In",
    "signOut": "Sign Out"
  },
  "product": {
    "addToCart": "Add to Cart",
    "outOfStock": "Out of stock",
    "reviews": "{count} reviews",
    "price": "Price: {amount} {currency}",
    "freeShipping": "Free shipping on orders of {amount} yuan or more"
  },
  "cart": {
    "empty": "Your cart is empty",
    "total": "Total: {amount}",
    "checkout": "Proceed to Checkout",
    "items": "{count} items"
  }
}

5. واجهة برمجة التشغيل

(1) ▶ مثال: استخدام useI18n في مكوّن

VUE
<template>
  <div>
    <h1>{{ t('common.products') }}</h1>
    <button>{{ t('product.addToCart') }}</button>
    <p>{{ t('product.reviews', { count: 1200 }) }}</p>
    <p>{{ t('product.price', { amount: formattedPrice, currency: currentCurrency }) }}</p>
  </div>
</template>

<script setup lang="ts">
const { t, locale, locales } = useI18n()

// الحصول على العملة الحالية بناءً على اللغة
const currentCurrency = computed(() => {
  const current = locales.value.find(l => l.code === locale.value)
  return current?.currency || 'USD'
})

const formattedPrice = computed(() => {
  return new Intl.NumberFormat(locale.value, {
    style: 'currency',
    currency: currentCurrency.value
  }).format(299.99)
})
</script>

الناتج:

TEXT
// التنفيذ ناجح

(2) ▶ مثال: تنقل تبديل اللغة

VUE
<!-- components/LanguageSwitcher.vue -->
<template>
  <div class="language-switcher">
    <NuxtLink
      v-for="loc in availableLocales"
      :key="loc.code"
      :to="switchLocalePath(loc.code)"
      :class="{ active: loc.code === locale }"
    >
      {{ loc.name }}
    </NuxtLink>
  </div>
</template>

<script setup lang="ts">
const { locale, locales, t } = useI18n()
const switchLocalePath = useSwitchLocalePath()

const availableLocales = computed(() =>
  (locales.value as any[]).filter(l => l.code !== locale.value)
)
</script>

الناتج:

TEXT
// التنفيذ ناجح

(3) ▶ مثال: ترجمة المسار localePath

VUE
<template>
  <nav>
    <NuxtLink :to="localePath('/')">{{ t('common.home') }}</NuxtLink>
    <NuxtLink :to="localePath('/products')">{{ t('common.products') }}</NuxtLink>
    <NuxtLink :to="localePath('/cart') }}">{{ t('common.cart') }}</NuxtLink>
  </nav>
</template>

<script setup lang="ts">
const localePath = useLocalePath()
</script>

الناتج:

TEXT
// التنفيذ ناجح

6. SEO وi18n

(1) الإنشاء التلقائي لوسوم hreflang

يُنشئ @nuxtjs/i18n تلقائياً وسم hreflang لكل صفحة:

HTML
<!-- مُنشأ تلقائياً بواسطة وحدة i18n -->
<link rel="alternate" hreflang="en" href="https://megashop.com/products/123" />
<link rel="alternate" hreflang="zh" href="https://megashop.com/zh/products/123" />
<link rel="alternate" hreflang="ja" href="https://megashop.com/ja/products/123" />
<link rel="alternate" hreflang="x-default" href="https://megashop.com/products/123" />

(1) ▶ مثال: خريطة موقع متعددة اللغات

TYPESCRIPT
// nuxt.config.ts - تكامل i18n + sitemap
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n', '@nuxtjs/sitemap'],

  i18n: {
    locales: [
      { code: 'en', name: 'English', file: 'en.json' },
      { code: 'zh', name: 'Chinese', file: 'zh.json' },
      { code: 'ja', name: 'Japanese', file: 'ja.json' }
    ],
    defaultLocale: 'en'
  },

  sitemap: {
    hostname: 'https://megashop.com',
    i18n: {
      locales: ['en', 'zh', 'ja'],
      defaultLocale: 'en'
    }
  }
})

الناتج:

TEXT
// التنفيذ ناجح

(2) قائمة فحص SEO لـ i18n

عنصر الفحص المتطلب طريقة التحقق
وسم hreflang ربط متبادل بين النسخ اللغوية عرض الكود المصدري
URL الأساسي ربط بنسخة اللغة الحالية عرض الكود المصدري
x-default ربط باللغة الافتراضية عرض الكود المصدري
عناوين URL تحتوي بادئات لغوية /zh/ /ja/ شريط عنوان المتصفح
معدل اكتمال الترجمة > 95٪ فحص آلي

7. مثال شامل: MegaShop—تعدد اللغات والعملات

TYPESCRIPT
// composables/useLocalizedPrice.ts
export function useLocalizedPrice() {
  const { locale, locales } = useI18n()

  const currentCurrency = computed(() => {
    const current = (locales.value as any[]).find(l => l.code === locale.value)
    return current?.currency || 'USD'
  })

  function formatPrice(amount: number): string {
    return new Intl.NumberFormat(locale.value, {
      style: 'currency',
      currency: currentCurrency.value
    }).format(amount)
  }

  // تحويل السعر الأساسي بالدولار إلى العملة المحلية (مبسط)
  const rates: Record<string, number> = { USD: 1, CNY: 7.25, JPY: 149.5 }

  function convertFromUSD(usdAmount: number): number {
    const rate = rates[currentCurrency.value] || 1
    return usdAmount * rate
  }

  function formatFromUSD(usdAmount: number): string {
    return formatPrice(convertFromUSD(usdAmount))
  }

  return { currentCurrency, formatPrice, convertFromUSD, formatFromUSD }
}
VUE
<!-- components/ProductCard.vue -->
<template>
  <div class="product-card">
    <img :src="product.image" :alt="product.name" />
    <h3>{{ product.name }}</h3>
    <p>{{ formatFromUSD(product.price) }}</p>
    <button>{{ t('product.addToCart') }}</button>
    <p>{{ t('product.reviews', { count: product.reviewCount }) }}</p>
  </div>
</template>

<script setup lang="ts">
const { t } = useI18n()
const { formatFromUSD } = useLocalizedPrice()

defineProps<{ product: any }>()
</script>

❓ أسئلة شائعة

س ما الفرق بين lazy: true وlazy: false؟
ج lazy: true يحمّل ملفات الترجمة عند الطلب (فقط عند تبديل اللغة)، مما يقلل حجم المحتوى فوق الطي. lazy: false يحمّل جميع اللغات دفعة واحدة؛ التبديل فوري لكن المحتوى فوق الطي يتحمل ببطء أكبر. نوصي بـ lazy: true.
س هل يجب استخدام JSON أم YAML لملفات الترجمة؟
ج JSON هو التنسيق الافتراضي ويوفر أفضل توافق. YAML أكثر قابلية للقراءة لكنه يتطلب تثبيت محلل. يُوصى بـ JSON للعمل الجماعي لأنه يدعم الهياكل المتداخلة.
س هل ستؤدي استراتيجية i18n "prefix_except_default" إلى عدم وجود بادئة URL للغة الافتراضية؟
ج نعم، "/" للإنجليزية، و"/zh/" للصينية. هذه هي استراتيجية SEO المُوصى بها—عناوين URL أقصر للغة الافتراضية. إذا كنت تحتاج تناسقاً، استخدم استراتيجية "prefix".
س كيف يتم الحفاظ على تبديل اللغة أثناء التشغيل؟
ج وحدة i18n تتضمن حفظاً تلقائياً عبر ملفات تعريف الارتباط (detectBrowserLanguage.useCookie: true). يمكنك أيضاً تعيينها يدوياً باستخدام setLocaleCookie().
س كيف يجب التعامل مع عملات متعددة—هل يتولى i18n الترجمة فقط؟
ج i18n يتولى ترجمة اللغة؛ العملة يجب معالجتها بشكل منفصل. استخدم Intl.NumberFormat للتنسيق، ونفّذ تحويل العملات في Composable. useLocalizedPrice في MegaShop يغلف كليهما.
س كيف تُدار الترجمات لمليون صفحة منتج؟
ج البيانات الديناميكية مثل أسماء المنتجات والأوصاف تُخزن بعدة لغات في قاعدة البيانات (في جدول ProductTranslation)؛ ملفات الترجمة تتولى نصوص واجهة المستخدم فقط. API يرجع الترجمة المناسبة بناءً على اللغة الحالية.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): ثبّت @nuxtjs/i18n، كوّن دعم الصينية والإنجليزية، ونفّذ ترجمة أساسية لواجهة المستخدم
  2. تمرين متقدم (الصعوبة: ⭐⭐): أضف دعم اللغة اليابانية + مبدل اللغة + ترجمة مسار localePath، وتحقق من وسم hreflang
  3. تحدٍ (الصعوبة: ⭐⭐⭐): نفّذ Composable باسم useLocalizedPrice لدعم التحويل والتنسيق التلقائي لثلاث عملات—USD وCNY وJPY—واعرض الأسعار بالعملة المناسبة على صفحة المنتج بناءً على اللغة.

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%