دليل دمج next-intl مع Next.js 15 لدعم الترجمة والتدويل خطوة بخطوة
شرح خطوة بخطوة لدمج next-intl من أجل الترجمة في مشروع Next.js 15.

الترجمة (i18n) ضرورية لتطبيقات الويب الحديثة. مع Next.js 15، الطريقة الموصى بها لإضافة الترجمة هي استخدام next-intl
. في هذا الدليل، ستتعلم كيفية إعداد next-intl
في مشروع Next.js 15 بطريقة عملية وفعالة.
1. تثبيت next-intl
أولاً، قم بتثبيت الحزمة:
npm install next-intl
2. إعداد التوجيه (Routing)
أنشئ ملف إعداد التوجيه لتعريف اللغات المدعومة. في Aniq-UI، يتم ذلك في src/i18n/routing.ts
:
1// src/i18n/routing.ts
2import {defineRouting} from 'next-intl/routing';
3
4export const routing = defineRouting({
5 locales: ['en', 'fr', 'ar', 'es'],
6 defaultLocale: 'en'
7});
3. إضافة أدوات التنقل
قم بإعداد أدوات التنقل في src/i18n/navigation.ts
:
// src/i18n/navigation.ts
import { createNavigation } from 'next-intl/navigation';
import { routing } from './routing';
export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
4. تحميل الرسائل على الخادم
قم بإعداد تحميل الرسائل في src/i18n/request.ts
:
1// src/i18n/request.ts
2import { getRequestConfig } from 'next-intl/server';
3import { hasLocale } from 'next-intl';
4import { routing } from './routing';
5
6export default getRequestConfig(async ({ requestLocale }) => {
7 const requested = await requestLocale;
8 const locale = hasLocale(routing.locales, requested)
9 ? requested
10 : routing.defaultLocale;
11
12 const [seoMessages] = await Promise.all([
13 import(`../messages/seo/${locale}.json`).then(module => module.default).catch(() => ({})),
14 ]);
15
16 return {
17 locale,
18 messages: {
19 seo: seoMessages,
20 }
21 };
22});
5. تمرير الرسائل إلى التطبيق
قم بلف صفحاتك بـ NextIntlClientProvider
. على سبيل المثال، في src/app/[locale]/about/layout.tsx
:
1// src/app/[locale]/about/layout.tsx
2import { NextIntlClientProvider } from "next-intl";
3
4export default async function AboutLayout({
5 children,
6 params,
7}: {
8 children: React.ReactNode;
9 params: Promise<{ locale: string }>;
10}) {
11 const { locale } = await params;
12
13 const [about] = await Promise.all([
14 import(`@/messages/about/${locale}.json`).then((mod) => mod.default),
15 ]);
16
17 const messages = { about };
18 return (
19 <NextIntlClientProvider locale={locale} messages={messages}>
20 {children}
21 </NextIntlClientProvider>
22 );
23}
6. تنظيم ملفات الرسائل
احفظ ملفات الترجمة في src/messages/
، مثلاً:
src/messages/about/en.json
src/messages/about/fr.json
src/messages/about/ar.json
src/messages/about/es.json
كل ملف يحتوي على ترجمات لمساحة اسم (namespace)، مثلاً:
{
"header_story": "قصتنا",
"header_title1": "نبني",
// ...
}
7. استخدام الترجمات في المكونات
استخدم هوك useTranslations
من next-intl
:
1import { useTranslations } from "next-intl";
2
3export default function AboutHeader() {
4 const t = useTranslations("about");
5 return <h1>{t("header_title1")}</h1>;
6}
8. إضافة محول اللغة (Language Switcher)
إليك الكود الكامل لمكون LanguageSelector
المستخدم في Aniq-UI:
1// src/components/LanguageSelector/index.tsx
2"use client";
3
4import React, { useState, useRef, useEffect } from "react";
5import { useParams, useRouter } from "next/navigation";
6import { cn } from "@/lib/utils";
7import { IconLanguage, IconChevronDown } from "@/lib/icons";
8
9interface Language {
10 code: string;
11 name: string;
12 rtl: boolean;
13}
14
15const languages: Language[] = [
16 { code: "en", name: "English", rtl: false },
17 { code: "fr", name: "Français", rtl: false },
18 { code: "es", name: "Español", rtl: false },
19 { code: "ar", name: "العربية", rtl: true },
20];
21
22export default function LanguageSelector() {
23 const [isOpen, setIsOpen] = useState(false);
24 const dropdownRef = useRef<HTMLDivElement>(null);
25 const router = useRouter();
26 const params = useParams();
27 const currentLocale = typeof params.locale === "string" ? params.locale : "en";
28
29 const currentLanguage = languages.find((lang) => lang.code === currentLocale) || languages[0];
30
31 useEffect(() => {
32 const handleClickOutside = (event: MouseEvent) => {
33 if (
34 dropdownRef.current &&
35 !dropdownRef.current.contains(event.target as Node)
36 ) {
37 setIsOpen(false);
38 }
39 };
40
41 document.addEventListener("mousedown", handleClickOutside);
42 return () => {
43 document.removeEventListener("mousedown", handleClickOutside);
44 };
45 }, []);
46
47 const changeLanguage = (langCode: string) => {
48 const currentPath = window.location.pathname;
49 // استبدال جزء اللغة في المسار
50 const pathSegments = currentPath.split('/');
51 pathSegments[1] = langCode;
52 const newPath = pathSegments.join('/');
53 router.push(newPath);
54 setIsOpen(false);
55 };
56
57 return (
58 <div className="relative" ref={dropdownRef}>
59 <button
60 onClick={() => setIsOpen(!isOpen)}
61 className="flex items-center cursor-pointer px-2 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 text-sm"
62 aria-expanded={isOpen}
63 aria-haspopup="true"
64 >
65 <IconLanguage className="w-4 h-4 mr-1" />
66 <span className="font-medium text-xs mr-1">{currentLanguage.code.toUpperCase()}</span>
67 <IconChevronDown className={cn("w-3 h-3 transition-transform", isOpen ? "rotate-180" : "")} />
68 </button>
69
70 {isOpen && (
71 <div className="absolute z-50 mt-1 bg-white dark:bg-neutral-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg py-1 w-40 min-w-max right-0">
72 <ul className="py-1">
73 {languages.map((language) => (
74 <li key={language.code}>
75 <button
76 onClick={() => changeLanguage(language.code)}
77 className={cn(
78 "w-full text-left cursor-pointer px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center",
79 language.code === currentLocale && "bg-gray-100 dark:bg-gray-800"
80 )}
81 >
82 <span className="inline-block me-2 w-max font-medium text-xs">{language.code.toUpperCase()}</span>
83 {language.name}
84 </button>
85 </li>
86 ))}
87 </ul>
88 </div>
89 )}
90 </div>
91 );
92}
9. اختبار الإعداد
شغل الخادم المحلي وزر /en/about
أو /fr/about
أو غيرها. يجب أن ترى الترجمات الصحيحة ويمكنك التبديل بين اللغات.
الخلاصة
مع next-intl
، يصبح التدويل في Next.js 15 سهلاً وقويًا. لمثال عملي كامل، راجع مستودع Aniq-UI.
المراجع: