تحميل الترجمات

استخدام موفر لإدارة الرسائل

المشكلة

كتابة النصوص بشكل ثابت، مثل 'Hello World'، مباشرة في مكونات التطبيق يربط المحتوى بالكود. لعرض لغة مختلفة، يجب على المطورين تكرار المكون أو إضافة منطق if/else، مما يجعل الترجمة غير قابلة للتوسع ويتطلب تغييراً كاملاً في الكود لكل نص جديد.

الحل

استخدم IntlProvider من react-intl لتوفير الترجمات. قم بتحميل رسائل الترجمة على الخادم في التخطيط الجذري، ومررها إلى مكون موفر من جانب العميل، ثم استهلكها في مكونات العميل الأخرى باستخدام خطاف useIntl.

الخطوات

1. تثبيت react-intl

أولاً، أضف react-intl كتبعية لمشروعك.

npm install react-intl

2. إنشاء ملفات ترجمة مسطحة

أنشئ مجلد dictionaries. بداخله، أضف ملف JSON لكل لغة. يعمل react-intl بشكل أفضل مع بنية مفتاح-قيمة مسطحة.

// dictionaries/en.json
{
  "home.title": "Home Page",
  "home.welcome": "Hello, welcome to our site!",
  "about.title": "About Us"
}
// dictionaries/es.json
{
  "home.title": "Página de Inicio",
  "home.welcome": "¡Hola, bienvenido a nuestro sitio!",
  "about.title": "Sobre Nosotros"
}

3. إنشاء دالة لتحميل القواميس

أنشئ دالة مساعدة لتحميل ملف القاموس الصحيح على الخادم بناءً على معامل lang.

// app/get-dictionary.ts
import 'server-only';

// Define the type for our flat message object
type Messages = Record<string, string>;

const dictionaries: { [key: string]: () => Promise<Messages> } = {
  en: () => import('@/dictionaries/en.json').then((module) => module.default),
  es: () => import('@/dictionaries/es.json').then((module) => module.default),
  // fr: () => import('@/dictionaries/fr.json').then((module) => module.default),
};

export const getDictionary = async (lang: string) => {
  const load = dictionaries[lang];
  if (load) {
    return load();
  }
  // Fallback to English
  return dictionaries.en();
};

4. إنشاء موفر من جانب العميل

IntlProvider هو مكون عميل يستخدم Context من React. يجب علينا إنشاء غلاف له يمكنه قبول الرسائل المحملة من الخادم.

// app/components/IntlClientProvider.tsx
'use client';

import { IntlProvider } from 'react-intl';

type Props = {
  children: React.ReactNode;
  locale: string;
  messages: Record<string, string>; // Flat messages object
};

export default function IntlClientProvider({
  children,
  locale,
  messages,
}: Props) {
  return (
    <IntlProvider messages={messages} locale={locale} defaultLocale="en">
      {children}
    </IntlProvider>
  );
}

5. تحديث التخطيط الجذري

عدّل app/[lang]/layout.tsx ليكون مكون async. سيقوم بتحميل الرسائل وتمريرها إلى IntlClientProvider.

// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import IntlClientProvider from '@/app/components/IntlClientProvider';

export async function generateStaticParams() {
  return [{ lang: 'en' }, { lang: 'es' }];
}

export default async function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Load messages on the server
  const messages = await getDictionary(params.lang);

  return (
    <html lang={params.lang}>
      <body>
        {/* Pass messages to the client provider */}
        <IntlClientProvider locale={params.lang} messages={messages}>
          {children}
        </IntlClientProvider>
      </body>
    </html>
  );
}

6. استخدام الترجمات في مكون العميل

يمكنك الآن استخدام خطاف useIntl في أي مكون عميل. لا يمكن لمكونات الخادم استخدام هذا الخطاف.

أنشئ مكون عميل جديد لعرض النص المترجم:

// app/components/HomePageContent.tsx
'use client';

import { useIntl } from 'react-intl';

export default function HomePageContent() {
  const intl = useIntl();

  return (
    <div>
      <h1>{intl.formatMessage({ id: 'home.title' })}</h1>
      <p>{intl.formatMessage({ id: 'home.welcome' })}</p>
    </div>
  );
}

7. إضافة المكون إلى صفحتك

أخيراً، أضف مكون العميل الجديد إلى صفحتك.

// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';

export default function Home() {
  // This page is a Server Component
  return (
    <div>
      {/* It renders the Client Component */}
      <HomePageContent />
    </div>
  );
}