استخدم مساعدك المفضل للملخص واستخدم هذه الصفحة والموفر AI الذي تريده
تاريخ الإصدارات
- "السجل الأولي"v9.1.3٦/٨/٢٠٢٥
تمت ترجمة محتوى هذه الصفحة باستخدام الذكاء الاصطناعي.
اعرض آخر نسخة المحتوى الأصلي باللغة الإنكليزيةIf you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the documentationCopy doc Markdown to clipboard
ترجم موقع SolidStart الخاص بك باستخدام Intlayer | التدويل (i18n)
جدول المحتويات
يغطي هذا الدليل تطبيق SolidStart المقدم عبر الخادم (server-rendered): يتم اكتشاف اللغة عند الطلب، وتتم ترجمة الصفحات وعرضها على الخادم باللغة الصحيحة، وتصريحات <html lang> و hreflang وإشارات sitemap التي تحتاجها محركات البحث يتم إصدارها من جانب الخادم.
لماذا Intlayer بدلاً من الحلول الأخرى؟
بالمقارنة مع الحلول الرئيسية مثل @solid-primitives/i18n أو i18next، فإن Intlayer هو حل يأتي مجهّزًا بتحسينات مدمجة مثل:
تم تحسين Intlayer ليعمل بشكل مثالي مع Solid من خلال تقديم تقسيم محتوى على مستوى المكونات، وترجمات تفاعلية، وجميع الميزات المطلوبة لتوسيع التدويل (i18n).
بدلاً من تحميل ملفات JSON ضخمة في صفحاتك، قم بتشغيل المحتوى الضروري فقط. يساعد Intlayer على تقليل حجم الحزمة والصفحة بنسبة تصل إلى 50%.
يساعد تقسيم محتوى تطبيقك في نطاقات محددة على تسهيل الصيانة للتطبيقات واسعة النطاق. يمكنك نسخ أو حذف مجلد ميزة واحدة دون العبء الذهني لمراجعة قاعدة كود المحتوى بالكامل. بالإضافة إلى ذلك، يتميز Intlayer بـ أنواع كاملة (fully typed) لضمان دقة محتواك.
يؤدي تجميع المحتوى في نفس المكان إلى تقليل السياق المطلوب بواسطة نماذج اللغات الكبيرة (LLMs). يأتي Intlayer أيضًا مع مجموعة من الأدوات، مثل CLI للاختبار عن الترجمات المفقودة، و LSP، و MCP، و agent skills، لجعل تجربة المطور (DX) أكثر سلاسة لوكلاء الذكاء الاصطناعي.
استخدم الأتمتة للترجمة في خط أنابيب CI/CD الخاص بك باستخدام نموذج اللغة الكبير الذي تختاره بتكلفة مزود الذكاء الاصطناعي الخاص بك. يقدم Intlayer أيضًا مترجمًا (compiler) لأتمتة استخراج المحتوى، بالإضافة إلى منصة ويب للمساعدة في الترجمة في الخلفية.
قد يؤدي ربط ملفات JSON الضخمة بالمكونات إلى مشاكل في الأداء والتفاعلية. يقدم Intlayer تحسينًا لتحميل المحتوى في وقت البناء (build time).
أكثر من مجرد حل تدويل (i18n)، يوفر Intlayer محررًا مرئيًا مُستضافًا ذاتيًا و نظام إدارة محتوى كامل (CMS) لمساعدتك في إدارة محتواك متعدد اللغات في الوقت الفعلي، مما يجعل التعاون مع المترجمين وكتّاب المحتوى وأعضاء الفريق الآخرين سلسًا. يمكن تخزين المحتوى محليًا و/أو عن بُعد.
دليل خطوة بخطوة لإعداد Intlayer في تطبيق SolidStart
تثبيت التبعيات
قم بتثبيت الحزم اللازمة باستخدام npm:
bashنسخ الكودنسخ الكود إلى الحافظة
npx intlayer init --interactiveخيار
--interactiveاختياري. استخدمintlayer-cli initإذا كنت وكيل ذكاء اصطناعي.سيقوم هذا الأمر باكتشاف بيئتك وتثبيت الحزم المطلوبة. على سبيل المثال:
bashنسخ الكودنسخ الكود إلى الحافظة
npm install intlayer solid-intlayer vite-intlayerintlayer
الحزمة الأساسية التي توفر أدوات التدويل إدارة التكوين، والترجمة، وإعلان المحتوى، والتحويل البرمجي، وأوامر CLI.
solid-intlayer
الحزمة التي تدمج Intlayer مع تطبيق Solid. توفر مزودي السياق (context providers) والخطاطيف (hooks) لتدويل Solid.
vite-intlayer
تتضمن ملحق Vite لدمج Intlayer مع مجمّع Vite، بالإضافة إلى معالج توجيه اللغة الذي يكتشف اللغة المفضلة للمستخدم ويدير ملفات الكوكيز ويتعامل مع إعادة توجيه عناوين URL.
هنا يُعتبر
vite-intlayerاهتمامًا من جانب الخادم وليس فقط في وقت البناء: فهو يقدم معالج الطلبات الذي يشغله خادم Nitro الخاص بـ SolidStart. إن إبقائه فيdependenciesهو الخيار الآمن والافتراضي — يمكنك نقله إلىdevDependenciesفقط إذا كنت تقوم بنشر مجلد.outputالمجمّع الذي يقحم فيه Nitro المعالج.تكوين مشروعك
أنشئ ملف تكوين لتكوين لغات تطبيقك:
intlayer.config.tsنسخ الكودنسخ الكود إلى الحافظة
import { type IntlayerConfig, Locales } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // لغاتك الأخرى ], defaultLocale: Locales.ENGLISH, }, routing: { mode: "prefix-no-default", }, }; export default config;باستخدام
prefix-no-default، يتم تقديم اللغة الافتراضية من عناوين URL بدون بادئة:plaintextنسخ الكودنسخ الكود إلى الحافظة
/ /about → الإنجليزية (اللغة الافتراضية)/fr /fr/about → الفرنسية/es /es/about → الإسبانيةمن خلال ملف التكوين هذا، يمكنك إعداد عناوين URL المترجمة، وإعادة توجيه الوسائط (middleware)، وأسماء ملفات تعريف الارتباط (cookies)، وموقع وامتداد إعلانات المحتوى الخاصة بك، وتعطيل سجلات Intlayer في وحدة التحكم، والمزيد. للحصول على قائمة كاملة بالمعلمات المتاحة، يرجى الرجوع إلى توثيق التكوين.
دمج Intlayer في تكوين Vite الخاص بك
أضف ملحق Intlayer إلى التكوين الخاص بك:
vite.config.tsنسخ الكودنسخ الكود إلى الحافظة
import { solidStart } from "@solidjs/start/config"; import { nitro } from "nitro/vite"; import { defineConfig } from "vite"; import { intlayer } from "vite-intlayer"; export default defineConfig({ plugins: [solidStart(), nitro(), intlayer()], });يقوم ملحق Vite
intlayer()ببناء ملفات إعلان المحتوى الخاصة بك، ومراقبتها في وضع التطوير، وتحديد متغيرة بيئة Intlayer داخل التطبيق. كما يوفر أسماء مستعارة (aliases) تعمل على تحسين الأداء.توجيه اللغة يأتي مع الملحق
يعمل SolidStart على Nitro، ويسجل
intlayer()معالج توجيه اللغة الخاص به مباشرةً في خط أنبوب خادم Nitro (عبر خيارrouting.enableProxy، وهوtrueافتراضيًا). لا داعي لتوصيل أي شيء آخر: في الخادم المجمّع، يتم فحص كل طلب قبل وصوله إلى الموجّه (router)، و- تُقرأ اللغة من بادئة URL، ثم من ملف تعريف الارتباط
INTLAYER_LOCALE، ثم تترويسةAccept-Language؛ - يتم إعادة توجيه URL غير المزود ببادئة إلى نظيره المترجم عندما لا تكون اللغة المحللة هي اللغة الافتراضية (
/→/fr)؛ - يتم إعادة توجيه URL ذي البادئة المكررة إلى شكله المعياري (
/en/about→/about)؛ - يتم إعادة كتابة ملف تعريف الارتباط الخاص باللغة في الاستجابة.
- تُقرأ اللغة من بادئة URL، ثم من ملف تعريف الارتباط
إعلان المحتوى الخاص بك
أنشئ وادِر إعلانات المحتوى الخاصة بك لتخزين الترجمات:
src/contents/home.content.tsنسخ الكودنسخ الكود إلى الحافظة
import { type Dictionary, t } from "intlayer"; const homeContent = { key: "home-page", content: { title: t({ en: "Hello world!", fr: "Bonjour le monde !", es: "¡Hola mundo!", }), metaTitle: "SolidStart + Intlayer", metaDescription: t({ en: "A SolidStart application internationalized with Intlayer.", fr: "Une application SolidStart internationalisée avec Intlayer.", es: "Una aplicación SolidStart internacionalizada con Intlayer.", }), documentation: t({ en: "Visit start.solidjs.com to learn how to build SolidStart apps.", fr: "Visitez start.solidjs.com pour apprendre à créer des applications SolidStart.", es: "Visita start.solidjs.com para aprender a crear aplicaciones SolidStart.", }), }, } satisfies Dictionary; export default homeContent;⚠️ تنبيه خاص بـ SolidStart: كل ملف
.ts/.tsxتحتsrc/routesيصبح مسارًا، وملف.content.tsيحتوي على تصدير افتراضي، لذا سيتم اعتباره صفحة. احتفظ بإعلانات المحتوى لـ الصفحات خارج مجلد المسارات (src/contents/يعمل بشكل جيد). يمكن أن يظل محتوى المكونات في نفس المكان، حيث لا يتم فحصsrc/componentsبواسطة موجّه نظام الملفات.يمكن تعريف إعلانات المحتوى الخاصة بك في أي مكان في تطبيقك طالما أنها مضمنة في دليل
contentDir(افتراضيًا،./src)، وتطابق امتداد ملف إعلان المحتوى (افتراضيًا،.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}).لمزيد من التفاصيل، يُرجى الرجوع إلى توثيق إعلان المحتوى.
إضافة التوجيه المترجم
الهدف من هذه الخطوة هو إعطاء كل لغة عنوان URL الخاص بها، وهو ما تقوم محركات البحث بفهرسته.
قم بنقل صفحاتك تحت شريحة ديناميكية اختيارية. في موجّه نظام الملفات لـ SolidStart، يترجم
[[locale]]إلى نمط المسار:locale?:plaintextنسخ الكودنسخ الكود إلى الحافظة
src/routes/ [[locale]].tsx ← تخطيط للتحقق من الشريحة [[locale]]/ index.tsx → / و /fr و /es about.tsx → /about و /fr/about و /es/about [...404].tsx → التقاط شامل لأي شيء آخرالمهمة الوحيدة لملف التخطيط هي تقييد الشريحة باللغة المكونة:
src/routes/[[locale]].tsxنسخ الكودنسخ الكود إلى الحافظة
import type { RouteSectionProps } from "@solidjs/router";import { locales } from "intlayer";export const route = { matchFilters: { locale: locales, },};export default function LocaleLayout(props: RouteSectionProps) { return <>{props.children}</>;}يقوم
@solidjs/routerبتوسيع:locale?إلى نمطين — أحدهما بالشريحة والآخر بدونها — ويجربها بالترتيب التنازلي للخصائص.matchFiltersهو ما يصنع الفرق بين الإعداد الناجح والإعداد المربك:اظهار جميع محتويات الجدولافتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
URL بدون matchFiltersمع matchFilters/fr/aboutصفحة حول بالفرنسية صفحة حول بالفرنسية /aboutصفحة حول (تغلب الشريحة الثابتة) صفحة حول /unknownالصفحة الرئيسية، بهدوء، مع locale=unknownلا يوجد تطابق → ينتقل إلى 404 الشامل يفضل استخدام
[locale](مطلوب) بدلاً من[[locale]]إذا كنت تستخدم وضع التوجيه'prefix-all'، واستغنِ عن الشريحة تمامًا بالنسبة لـ'no-prefix'أو'search-params'.توفير اللغة لتطبيقك
عنوان URL هو المصدر الوحيد للحقيقة بالنسبة للغة: لقد قام الوسيط بالفعل بإعادة توجيه الطلب إلى مساره المترجم، لذا فإن قراءة المسار في التخطيط الرئيسي تحافظ على توافق العرض من الخادم والتنشيط في العميل (hydration)، وتجعل كل تنقل من جانب العميل يحدّث اللغة تلقائيًا.
src/app.tsxنسخ الكودنسخ الكود إلى الحافظة
import { MetaProvider } from "@solidjs/meta";import { Router, useLocation } from "@solidjs/router";import { FileRoutes } from "@solidjs/start/router";import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";import { IntlayerProvider } from "solid-intlayer";import { createEffect, type ParentProps, Suspense } from "solid-js";import { isServer } from "solid-js/web";import { Nav } from "~/components/Nav";import "./app.css";const RootLayout = (props: ParentProps) => { const location = useLocation(); const locale = () => getLocaleFromPath(location.pathname) ?? defaultLocale; // يعرض الخادم <html> في entry-server.tsx؛ // يجب على التنقلات بين اللغات من جانب العميل تحديث السمتين بنفسها. createEffect(() => { if (isServer) return; document.documentElement.lang = locale(); document.documentElement.dir = getHTMLTextDir(locale()); }); return ( <MetaProvider> <IntlayerProvider locale={locale()}> <Nav /> <Suspense>{props.children}</Suspense> </IntlayerProvider> </MetaProvider> );};export default function App() { return ( <Router root={RootLayout}> <FileRoutes /> </Router> );}يتفاعل
IntlayerProviderمع خاصيةlocaleالخاصة به، لذا فإن إمرار استدعاء الوصولlocale()داخل JSX كافٍ — يقوم Solid بتجميعها إلى دالة الحصول (getter)، ويعاد عرض الشجرة بأكملها باللغة الجديدة عند تغيير URL.تعيين تعبيرات lang و dir لـ HTML على الخادم
يتم عرض العنصر
<html>بواسطةentry-server.tsx، خارجRouter. اقرأ اللغة من URL الطلب بدلاً من ذلك:src/entry-server.tsxنسخ الكودنسخ الكود إلى الحافظة
// @refresh reloadimport { createHandler, StartServer } from "@solidjs/start/server";import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";import { getRequestEvent } from "solid-js/web";export default createHandler(() => ( <StartServer document={({ assets, children, scripts }) => { const url = getRequestEvent()?.request.url ?? "/"; const locale = getLocaleFromPath(url) ?? defaultLocale; return ( <html dir={getHTMLTextDir(locale)} lang={locale}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> {assets} </head> <body> <div id="app">{children}</div> {scripts} </body> </html> ); }} />));تتلقى الزواحف (crawlers) الآن اللغة الصحيحة في البايت الأول:
htmlنسخ الكودنسخ الكود إلى الحافظة
<html dir="ltr" lang="fr"></html>استخدام Intlayer في صفحاتك
الوصول إلى قواميس المحتوى الخاصة بك في جميع أنحاء تطبيقك:
src/routes/[[locale]]/index.tsxنسخ الكودنسخ الكود إلى الحافظة
import { Meta, Title } from "@solidjs/meta";import { useIntlayer } from "solid-intlayer";import Counter from "~/components/Counter";export default function Home() { const content = useIntlayer("home-page"); return ( <main> <Title>{content.metaTitle.value}</Title> <Meta content={content.metaDescription.value} name="description" /> <h1>{content.title}</h1> <Counter /> <p>{content.documentation}</p> </main> );}في Solid، يعيد
useIntlayerمحتوى تفاعليًا (مثلcontent). يمكنك الوصول إلى خصائصه مباشرةً.إذا كنت تريد استخدام محتواك في خاصية من نوع
string، مثلalt،title،href،aria-label، إلخ، يمكنك استخدام قيمة الدالة، مثل:htmlنسخ الكودنسخ الكود إلى الحافظة
<img src="{content.image.src.value}" alt="{content.image.value}" /><img src="{content.image.src.toString()}" alt="{content.image.toString()}" /><img src="{String(content.image.src)}" alt="{String(content.image)}" />لمعرفة المزيد حول خطاف
useIntlayer، يُرجى الرجوع إلى التوثيق.عقد المحتوى ليست مقتصرة على الترجمات العادية. على سبيل المثال عداد بصيغة الجمع:
src/components/Counter.content.tsنسخ الكودنسخ الكود إلى الحافظة
import { type Dictionary, plural, t } from "intlayer";const counterContent = { key: "counter", content: { clicks: plural({ one: t({ en: "{{count}} click", fr: "{{count}} clic", es: "{{count}} clic", }), other: t({ en: "{{count}} clicks", fr: "{{count}} clics", es: "{{count}} clics", }), }), },} satisfies Dictionary;export default counterContent;src/components/Counter.tsxنسخ الكودنسخ الكود إلى الحافظة
import { useIntlayer } from "solid-intlayer";import { createSignal } from "solid-js";export default function Counter() { const [count, setCount] = createSignal(0); const content = useIntlayer("counter"); return ( <button onClick={() => setCount(count() + 1)} type="button"> {content.clicks(count())} </button> );}يختار
plural()الفئة من خلالIntl.PluralRulesللغة النشطة، لذا فإن اللغات التي تحتوي على أكثر من صيغتي جمع تعمل دون الحاجة إلى أي كود إضافي.إنشاء مكون رابط مترجم
أنشئ مكون
Linkمخصصًا يضيف تلقائيًا بادئة اللغة الحالية إلى عناوين URL الداخلية:src/components/LocalizedLink.tsxنسخ الكودنسخ الكود إلى الحافظة
import { A, type AnchorProps } from "@solidjs/router";import { getLocalizedUrl } from "intlayer";import { useLocale } from "solid-intlayer";import type { ParentComponent } from "solid-js";export const LocalizedLink: ParentComponent<AnchorProps> = (props) => { const { locale } = useLocale(); const isExternal = () => /^[a-z][a-z0-9+.-]*:/i.test(props.href); const localizedHref = () => isExternal() ? props.href : getLocalizedUrl(props.href, locale()); return <A {...props} href={localizedHref()} />;};src/components/Nav.tsxنسخ الكودنسخ الكود إلى الحافظة
import { useIntlayer } from "solid-intlayer";import type { Component } from "solid-js";import { LocaleSwitcher } from "./LocaleSwitcher";import { LocalizedLink } from "./LocalizedLink";export const Nav: Component = () => { const content = useIntlayer("nav"); return ( <nav> <LocalizedLink href="/">{content.home}</LocalizedLink> <LocalizedLink href="/about">{content.about}</LocalizedLink> <LocaleSwitcher /> </nav> );};كتابة
href="/about"مرة واحدة ينتج الآن/aboutأو/fr/aboutأو/es/aboutاعتمادًا على اللغة النشطة — دون الحاجة لإضافة بادئة يدويًا في أي مكان في صفحاتك.إنشاء مكون مبدل اللغة
عرض المبدل كـ روابط حقيقية (anchors) بدلاً من
<select>: تصبح كل لغة في الصفحة الحالية رابطًا قابلاً للزحف يمكن فتحه في تبويب جديد، وهو ما لا يستطيع التحكم المقتصر على JavaScript تقديمه.يقوم
getPathWithoutLocaleبإزالة شريحة اللغة من المسار الحالي، ويقومgetLocalizedUrlبإعادة بنائها للغة المستهدفة، بحيث تتبع الروابط وضع التوجيه الخاص بك دون إدخال كود ثابت. التنقل هو ما يغير اللغة المعروضة — يشتق مسار[[locale]]اللغة من عنوان URL — بينما يحفظsetLocaleالخيار في ملف تعريف الارتباطINTLAYER_LOCALEبحيث تؤدي الزيارة اللاحقة لعنوان URL الخالي من اللغة إلى نفس اللغة.src/components/LocaleSwitcher.tsxنسخ الكودنسخ الكود إلى الحافظة
import { A, useLocation } from "@solidjs/router"; import { getHTMLTextDir, getLocaleName, getLocalizedUrl, getPathWithoutLocale, } from "intlayer"; import { useIntlayer, useLocale } from "solid-intlayer"; import { type Component, For } from "solid-js"; export const LocaleSwitcher: Component = () => { const content = useIntlayer("locale-switcher"); const location = useLocation(); const { locale, setLocale, availableLocales } = useLocale(); // المسار المعياري (الخالي من اللغة) للصفحة المعروضة حاليًا const pathWithoutLocale = () => getPathWithoutLocale(location.pathname); return ( <div> <button aria-label={content.label.value} popoverTarget="localePopover" type="button" > {getLocaleName(locale())} </button> <div id="localePopover" popover="auto"> <For each={availableLocales}> {(localeItem) => ( <A dir={getHTMLTextDir(localeItem)} // التطابق الدقيق فقط، حتى لا يتم تمييز رابط اللغة الافتراضية // كنشط في كل صفحة end href={getLocalizedUrl(pathWithoutLocale(), localeItem)} hreflang={localeItem} lang={localeItem} onClick={() => setLocale(localeItem)} // يضمن أن زر "الرجوع" في المتصفح يعود إلى الصفحة السابقة replace > {/* اللغة بلغتها الخاصة - على سبيل المثال Français */} {getLocaleName(localeItem)} </A> )} </For> </div> </div> ); };في Solid، يعتبر
localeمنuseLocaleهو وصول إشارة (signal accessor). استخدمlocale()(مع الأقواس) لقراءة قيمته الحالية بشكل تفاعلي.يعرض
getLocaleName(localeItem)كل لغة بلغتها الخاصة —English / Français / Español. مرر وسيطة ثانية لترجمة الأسماء إلى اللغة المعروضة حاليًا بدلاً من ذلك:getLocaleName(localeItem, locale())يعطيEnglish / French / Spanishبالإنجليزية، وanglais / français / espagnolبالفرنسية.يقوم
<A>بالفعل بتعيينaria-current="page"على الرابط المطبق على URL الحالي، لذا لا يوجد شيء لإضافته في هذا الشأن. تٌقرأreplaceمن التعبير المعروض بواسطة الموجّه: فهي تستبدل مدخل التارخ بدلاً من إضافته، بحيث يعود زر "الرجوع" في المتصفح إلى الصفحة التي زرتها قبل التبديل بدلاً من الذهاب إلى نفس الصفحة باللغة السابقة.تضمن
dirوhreflangفي كل رابط التوجيه الصحيح لأسماء اللغات من اليمين إلى اليسار وتخبر التقنيات المساعدة والزواحف باللغة التي يشير إليها كل رابط.لمعرفة المزيد حول خطاف
useLocale، يُرجى الرجوع إلى التوثيق.إصدار روابط canonical و hreflang
اختياريتخبر التوضيحات
hreflangمحركات البحث أن/aboutو/fr/aboutو/es/aboutهي نفس الصفحة بلغات مختلفة. يقومgetMultilingualUrlsبإنشائها من المسار المعياري (الخالي من اللغة) متبعًا وضع التوجيه الخاص بك، بحيث لا يلزم إدخال أي كود ثابت:src/components/AlternateLinks.tsxنسخ الكودنسخ الكود إلى الحافظة
import { defaultLocale, getMultilingualUrls, getPathWithoutLocale,} from "intlayer";import { type Component, For } from "solid-js";export type AlternateLinksProps = { /** عنوان URL المطلق للصفحة التي يتم عرضها. */ url: string;};export const AlternateLinks: Component<AlternateLinksProps> = (props) => { const multilingualUrls = () => { const { origin, pathname } = new URL(props.url); return Object.entries( getMultilingualUrls(`${origin}${getPathWithoutLocale(pathname)}`) ); }; const canonicalUrl = () => new URL(props.url).origin + new URL(props.url).pathname; return ( <> <link href={canonicalUrl()} rel="canonical" /> <For each={multilingualUrls()}> {([locale, localizedUrl]) => ( <link href={localizedUrl} hreflang={locale} rel="alternate" /> )} </For> <link href={ multilingualUrls().find(([locale]) => locale === defaultLocale)?.[1] } hreflang="x-default" rel="alternate" /> </> );};قم بعرضه في رأس المستند (head)، حيث يكون عنوان URL للطلب متاحًا:
src/entry-server.tsxنسخ الكودنسخ الكود إلى الحافظة
import { AlternateLinks } from "~/components/AlternateLinks";// … داخل <head>، بجوار وسوم meta الأخرى:<AlternateLinks url={url} />;يقوم
GET /fr/aboutبعد ذلك بتقديم:htmlنسخ الكودنسخ الكود إلى الحافظة
<link href="https://example.com/fr/about" rel="canonical" /><link href="https://example.com/about" hreflang="en" rel="alternate" /><link href="https://example.com/fr/about" hreflang="fr" rel="alternate" /><link href="https://example.com/es/about" hreflang="es" rel="alternate" /><link href="https://example.com/about" hreflang="x-default" rel="alternate" />ملاحظة حول
@solidjs/meta: في وقت كتابة هذا الدليل، يتم تطبيق<Title>و<Meta>من@solidjs/metaعلى العميل بعد التنشيط ولكن لا يتم إرسالها إلى<head>المعروض من الخادم في SolidStart v2. حتى يتم إصلاح ذلك في المستقبل، قم بعرض الوسوم التي يجب أن تراها الزواحف بدون JavaScript —canonicalوhreflangوإذا لزم الأمرtitle/description— مباشرة فيentry-server.tsx، كما هو موضح أعلاه.إدارة صفحات غير الموجودة (404)
اختيارييلتقط مسار splat في جذر
src/routesكل مسار لم تطابقه شريحة اللغة — بما في ذلك بادئات اللغة غير الصالحة التي يرفضهاmatchFilters. نظرًا لأن اللغة ما زالت تأتي من URL عبر التخطيط الرئيسي، يتم عرض صفحة 404 بلغة الزائر:src/routes/[...404].tsxنسخ الكودنسخ الكود إلى الحافظة
import { Title } from "@solidjs/meta";import { HttpStatusCode } from "@solidjs/start";import { useIntlayer } from "solid-intlayer";import { LocalizedLink } from "~/components/LocalizedLink";export default function NotFound() { const content = useIntlayer("not-found-page"); return ( <main> <Title>{content.metaTitle.value}</Title> <HttpStatusCode code={404} /> <h1>{content.title}</h1> <LocalizedLink href="/">{content.backHome}</LocalizedLink> </main> );}اظهار جميع محتويات الجدولافتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
الطلب النتيجة /xx404—xxليست لغة مكونة/nonexistent404باللغة الافتراضية/fr/nonexistent404بالفرنسية (Page introuvable)إنشاء خريطة موقع (sitemap) متعددة اللغات
اختياريمنشئ sitemap الخاص بـ Intlayer يوسع كل مسار إلى إدخال واحد لكل لغة ويربط بدائل
xhtml:linkبينها، لذلك يتعين على المسار فقط إدراج المسارات المعيارية الخالية من اللغة.على عكس المنشئات الأساسية التي تصدر فقط عناوين URL مسطحة، يربط Intlayer روابط ثنائية الاتجاه بين كل متغير مترجم لكل صفحة، مما يساعد محركات البحث على ربط عناوين URL المترجمة وتقديم العنوان الصحيح للجمهور المناسب.
يحول SolidStart الملف الذي يصدر طريقة HTTP إلى مسار API، ويزيل امتداد
.tsمن المسار — لذلك يتم تقديمsrc/routes/sitemap.xml.tsعند/sitemap.xml:src/routes/sitemap.xml.tsنسخ الكودنسخ الكود إلى الحافظة
import type { APIEvent } from "@solidjs/start/server"; import { generateSitemap } from "intlayer"; const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000"; export const GET = (_event: APIEvent) => { const sitemap = generateSitemap( [ { path: "/", changefreq: "daily", priority: 1.0 }, { path: "/about", changefreq: "monthly", priority: 0.8 }, ], { siteUrl: SITE_URL } ); return new Response(sitemap, { headers: { "Content-Type": "application/xml" }, }); };output of GET /sitemap.xmlنسخ الكودنسخ الكود إلى الحافظة
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"> <url> <loc>https://example.com/about</loc> <changefreq>monthly</changefreq> <priority>0.8</priority> <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/> <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/about"/> <xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/about"/> <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about"/> </url></urlset>لا تدعم مسارات API المعلمات الاختيارية، لذا احتفظ بهذا الملف في جذر
src/routes، خارج شريحة[[locale]]. تحتوي خريطة الموقع بالفعل على كل لغة.يمكنك بناء
robots.txtبنفس الطريقة باستخدامgetMultilingualUrls، بحيث تغطي إدخالاتDisallowكل إملاء مترجم لمسار حساس:src/routes/robots.txt.tsنسخ الكودنسخ الكود إلى الحافظة
import { getMultilingualUrls } from "intlayer"; const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000"; const disallowedPaths = ["/admin", "/private"].flatMap((path) => Object.values(getMultilingualUrls(path)) ); export const GET = () => new Response( [ "User-agent: *", "Allow: /", ...disallowedPaths.map((path) => `Disallow: ${path}`), "", `Sitemap: ${SITE_URL}/sitemap.xml`, ].join("\n"), { headers: { "Content-Type": "text/plain" } } );استرداد اللغة في دوال الخادم الخاص بك
اختياريقد ترغب في الوصول إلى اللغة الحالية من داخل دالة خادم أو مسار API.
في إعداد قائم على البادئة مثل هذا الإعداد، عنوان URL هو السلطة الإلزامية: يقرا
getLocaleFromPathالبادئة من URL الطلب.getLocaleهو الحل الاحتياطي للطلبات التي لا تحمل بادئة لغة — فهو يفحص ملف تعريف الارتباطINTLAYER_LOCALE، ثم ترويسةx-intlayer-locale، ثم يتفاوض علىAccept-Language.src/routes/[[locale]]/index.tsxنسخ الكودنسخ الكود إلى الحافظة
import { createAsync } from "@solidjs/router";import { getCookie, getIntlayer, getLocale, getLocaleFromPath } from "intlayer";import { getRequestEvent } from "solid-js/web";const loadLocalizedData = async () => { "use server"; const request = getRequestEvent()?.request; const locale = getLocaleFromPath(request?.url) ?? (await getLocale({ // الحصول على ملف تعريف الارتباط من الطلب (افتراضيًا: 'INTLAYER_LOCALE') getCookie: (name) => getCookie(name, request?.headers.get("cookie") ?? ""), // الحصول على الترويسة من الطلب (افتراضيًا: 'x-intlayer-locale')، // والرجوع إلى تفاوض Accept-Language getHeader: (name) => request?.headers.get(name) ?? undefined, })); // استرداد بعض المحتوى خارج المكون باستخدام getIntlayer() const content = getIntlayer("home-page", locale); return { locale, title: String(content.title) };};export default function Page() { const data = createAsync(() => loadLocalizedData()); return <p>{data()?.title}</p>;}لا تعتمد على
getLocaleبمفرده هنا: يتم كتابة ملف تعريف الارتباط الخاص باللغة فقط بمجرد أن يغير الزائر اللغة بنشاط، لذا فإن الزيارة الأولى لـ/fr/...ستقوم بالتحليل إلى اللغة الافتراضية.استخراج محتوى مكوناتك
اختياريإذا كان لديك قاعدة كود حالية، فقد يكون تحويل آلاف الملفات مستهلكًا للوقت.
لتسهيل هذه العملية، يقدم Intlayer مترجمًا (compiler) / أداة استخراج (extractor) لتحويل مكوناتك واستخراج المحتوى.
لإعداده، يمكنك إضافة قسم
compilerفي ملفintlayer.config.tsالخاص بك:intlayer.config.tsنسخ الكودنسخ الكود إلى الحافظة
import { type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { // ... باقي التكوين الخاص بك compiler: { /** * يحدد ما إذا كان ينبغي تفعيل المترجم. */ enabled: true, /** * يحدد مسار ملفات المخرجات */ output: ({ fileName, extension }) => `./${fileName}${extension}`, /** * يحدد ما إذا كان ينبغي حفظ المكونات بعد تحويلها. * * - إذا كان `true`، سيقوم المترجم بإعادة كتابة ملف المكون على القرص. وبالتالي سيكون التحويل دائمًا، وسيتجاوز المترجم التحويل في العملية التالية. بهذه الطريقة، يمكن للمترجم تحويل التطبيق، ثم إزالته. * * - إذا كان `false`، سيقوم المترجم بإقحام استدعاء الدالة `useIntlayer()` في الكود في مخرجات البناء فقط، ويحافظ على قاعدة الكود الأساسية كما هي. سيتم إجراء التحويل في الذاكرة فقط. */ saveComponents: false, /** * بادئة مفتاح القاموس */ dictionaryKeyPrefix: "", }, }; export default config;قم بتشغيل أداة الاستخراج لتحويل مكوناتك واستخراج المحتوى
bashنسخ الكودنسخ الكود إلى الحافظة
npx intlayer extractقم بنقل ملفات المحتوى المنشأة لصفحاتك خارج
src/routesبعد ذلك، للسبب الموضح في الخطوة 5.تكوين TypeScript
يستخدم Intlayer زيادة النماذج (module augmentation) للاستفادة من مزايا TypeScript وجعل قاعدة الكود الخاصة بك أكثر قوة.
تأكد من أن تكوين TypeScript الخاص بك يتضمن الأنواع التلقائية المنشأة:
tsconfig.jsonنسخ الكودنسخ الكود إلى الحافظة
{ compilerOptions: { // ... التكوينات الحالية الخاصة بك }, include: [ "src", "*.ts", ".intlayer/**/*.ts", // تضمين الأنواع المنشأة تلقائيًا ],}مفاتيح القاموس ومسارات المحتوى يتم فحصها الآن في وقت التجميع (compile time):
tsxنسخ الكودنسخ الكود إلى الحافظة
useIntlayer("home-page"); // ✅useIntlayer("hom-page"); // ❌ Argument of type '"hom-page"' is not assignable to parameter of type 'keyof __DictionaryRegistry'
التحقق من الإعداد الخاص بك
قم ببناء الخادم وتشغيله، ثم تحقق من أن هذه الطلبات تعمل كما هو متوقع:
نسخ الكود إلى الحافظة
npm run buildnode .output/server/index.mjsافتح الجدول في نافذة منبثقة لعرض جميع محتويات البيانات بوضوح
| الطلب | الاستجابة المتوقعة |
|---|---|
GET / | 200 — الإنجليزية |
GET / مع Accept-Language: fr | 302 → /fr |
GET / مع ملف تعريف الارتباط INTLAYER_LOCALE=es | 302 → /es |
GET /fr | 200 — الفرنسية, <html lang="fr"> |
GET /fr/about | 200 — صفحة حول بالفرنسية |
GET /en/about | 302 → /about (إعادة توجيه معيارية) |
GET /xx | 404 |
GET /fr/nonexistent | 404 بالفرنسية |
GET /sitemap.xml | 200 — خريطة موقع XML متعددة اللغات |
تعمل الصفوف التي تعرض صفحة بشكل مماثل تحت vite dev. تنطبق صفوف إعادة التوجيه الثلاثة فقط على خادم مجمّع ما لم تقم بتسجيل المعالج كوسيط بنفسك — انظر الخطوة 3.
قم بتشغيل خادم التطوير على Node (vite dev) بدلاً من Bun (bun --bun vite dev): يفشل SSR الخاص بـ SolidStart حاليًا تحت بيئة تشغيل Bun مع الخطأExpected a Response object, but received 'NodeResponse'. هذا غير مرتبط بـ Intlayer — يتم تكراره في القالب العادي — ويؤثر فقط على خادم التطوير، وليس علىvite build.
تكوين Git
يُوصى بتجاهل الملفات التي تم إنشاؤها بواسطة Intlayer. يتيح لك ذلك تجنب إرسالها إلى مستودع Git الخاص بك.
لقيام بذلك، يمكنك إضافة التعليمات التالية إلى ملف .gitignore الخاص بك:
نسخ الكود إلى الحافظة
# تجاهل الملفات المنشأة بواسطة Intlayer.intlayerإضافة VS Code
لتحسين تجربة التطوير الخاصة بك مع Intlayer، يمكنك تثبيت إضافة Intlayer الرسمية لـ VS Code.
تقدم هذه الإضافة:
- الإكمال التلقائي لمفاتيح الترجمة.
- اكتشاف الأخطاء في الوقت الفعلي للترجمات المفقودة.
- معاينات مضمنة (inline) للمحتوى المترجم.
- إجراءات سريعة لإنشاء الترجمات وتحديثها بسهولة.
التعمق أكثر
للتعمق أكثر، يمكنك تطبيق المحرر المرئي أو إخراج محتواك إلى الخارج باستخدام CMS.