أسهل طريقة لترجمة تطبيقات Next.js (مع إعداد الصفحات) .
إذا كنت تستخدم next-i18next (دليل الصفحات) في الإنتاج وترغب في إطلاق العنان لبعض القوى الخارقة، فيمكنك إلقاء نظرة على منشور المدونة هذا.
إذا كنت تستخدم Next.js 13/14 مع دليل التطبيق، فليست هناك حاجة إلى next-i18next، ويمكنك استخدام i18next وreact-i18next مباشرة، كما هو موضح في منشور المدونة هذا.
على الرغم من أن Next.js يوفر توجيهًا دوليًا مباشرةً، إلا أنه لا يتعامل مع أي إدارة لمحتوى الترجمة، أو وظيفة الترجمة الفعلية نفسها. كل ما يفعله Next.js هو الحفاظ على مزامنة اللغات المحلية وعناوين URL.
لاستكمال ذلك، يوفر next-i18next
الوظائف المتبقية - إدارة محتوى الترجمة، والمكونات/الخطافات لترجمة مكونات React الخاصة بك - مع الدعم الكامل لـ SSG/SSR، ومساحات الأسماء المتعددة، وتقسيم الأكواد، وما إلى ذلك.
بينما يستخدم next-i18next
i18next وreact-i18next ضمن الغطاء، يحتاج مستخدمو next-i18next
ببساطة إلى تضمين محتوى الترجمة الخاص بهم كملفات JSON ولا داعي للقلق بشأن أي شيء آخر.
العرض المباشر متاح هنا. هذا التطبيق التجريبي هو مثال بسيط - لا أكثر ولا أقل.
سهل الإعداد وسهل الاستخدام: يستغرق الإعداد بضع خطوات فقط، والتكوين بسيط.
لا توجد متطلبات أخرى: يعمل next-i18next
على تبسيط عملية التدويل لتطبيق Next.js الخاص بك دون أي تبعيات إضافية.
جاهز للإنتاج: يدعم next-i18next
تمرير الترجمات وخيارات التكوين إلى الصفحات كدعائم مع دعم SSG/SSR.
سيوفر ملف next-i18next.config.js
الخاص بك التكوين لـ next-i18next
. بعد الضبط، يتيح لنا appWithTranslation
استخدام وظيفة t
(الترجمة) في مكوناتنا عبر الخطافات.
ثم نضيف serverSideTranslation
إلى getStaticProps أو getServerSideProps (حسب حالتك) في مكوناتنا على مستوى الصفحة.
الآن أصبح تطبيق Next.js الخاص بنا قابلاً للترجمة بالكامل!
yarn add next-i18next react-i18next i18next
تحتاج أيضًا إلى react
والتثبيت next
.
افتراضيًا، يتوقع next-i18next
أن يتم تنظيم ترجماتك على هذا النحو:
.
└── public
└── locales
├── en
| └── common.json
└── de
└── common.json
ويمكن أيضًا رؤية هذا الهيكل في المثال البسيط.
إذا كنت تريد تنظيم ترجماتك/مساحات الأسماء الخاصة بك بطريقة مخصصة، فستحتاج إلى تمرير قيم localePath
و localeStructure
المعدلة في تكوين التهيئة.
أولاً، قم بإنشاء ملف next-i18next.config.js
في جذر مشروعك. يأتي بناء جملة كائن i18n
المتداخل من Next.js مباشرةً.
يُخبر هذا next-i18next
ما هي defaultLocale
الخاصة بك واللغات المحلية الأخرى، حتى يتمكن من تحميل الترجمات مسبقًا على الخادم:
next-i18next.config.js
/** @type {import('next-i18next').UserConfig} */
module . exports = {
i18n : {
defaultLocale : 'en' ,
locales : [ 'en' , 'de' ] ,
} ,
}
الآن، قم بإنشاء أو تعديل ملف next.config.js
، عن طريق تمرير كائن i18n
إلى ملف next.config.js
، لتمكين توجيه عنوان URL المترجم:
next.config.js
const { i18n } = require ( './next-i18next.config' )
module . exports = {
i18n ,
}
هناك ثلاث وظائف يصدرها next-i18next
، والتي ستحتاج إلى استخدامها لترجمة مشروعك:
هذا هو HOC الذي يغلف _app
الخاص بك :
import { appWithTranslation } from 'next-i18next'
const MyApp = ( { Component , pageProps } ) => (
< Component { ... pageProps } / >
)
export default appWithTranslation ( MyApp )
يعد appWithTranslation
HOC مسؤولاً بشكل أساسي عن إضافة I18nextProvider
.
هذه وظيفة غير متزامنة تحتاج إلى تضمينها في المكونات على مستوى الصفحة، عبر getStaticProps
أو getServerSideProps
(اعتمادًا على حالة الاستخدام الخاصة بك):
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
export async function getStaticProps ( { locale } ) {
return {
props : {
... ( await serverSideTranslations ( locale , [
'common' ,
'footer' ,
] ) ) ,
// Will be passed to the page component as props
} ,
}
}
لاحظ أنه يجب استيراد serverSideTranslations
من next-i18next/serverSideTranslations
- وهذه وحدة منفصلة تحتوي على كود خاص بـ NodeJs.
لاحظ أيضًا أن serverSideTranslations
غير متوافق مع getInitialProps
، لأنه يمكن تنفيذه فقط في بيئة الخادم، بينما يتم استدعاء getInitialProps
على جانب العميل عند التنقل بين الصفحات.
يعد serverSideTranslations
HOC مسؤولاً بشكل أساسي عن تمرير الترجمات وخيارات التكوين إلى الصفحات، كدعائم - تحتاج إلى إضافتها إلى أي صفحة تحتوي على ترجمات.
هذا هو الخطاف الذي ستستخدمه بالفعل للقيام بالترجمة نفسها. يأتي خطاف useTranslation
من react-i18next
، لكن يجب استيراده من next-i18next
مباشرة.
لا تستخدم تصدير useTranslation
لـ react-i18next
، ولكن استخدم فقط المصدر من next-i18next
!
import { useTranslation } from 'next-i18next'
export const Footer = ( ) => {
const { t } = useTranslation ( 'footer' )
return (
< footer >
< p > { t ( 'description' ) } < / p >
< / footer >
)
}
افتراضيًا، يرسل next-i18next
جميع مساحات الأسماء الخاصة بك إلى العميل عند كل طلب أولي. يمكن أن يكون هذا أسلوبًا مناسبًا للتطبيقات الصغيرة ذات المحتوى الأقل، ولكن الكثير من التطبيقات ستستفيد من تقسيم مساحات الأسماء بناءً على المسار.
للقيام بذلك، يمكنك تمرير مصفوفة من مساحات الأسماء المطلوبة لكل صفحة إلى serverSideTranslations
. يمكنك رؤية هذا الأسلوب في الأمثلة/simple/pages/index.tsx. لن يؤدي تمرير مصفوفة فارغة من مساحات الأسماء المطلوبة إلى إرسال أي مساحات أسماء.
ملاحظة: يوفر useTranslation
مساحات أسماء للمكون الذي تستخدمه فيه. ومع ذلك، يوفر serverSideTranslations
إجمالي مساحات الأسماء المتاحة لشجرة React بأكملها وينتمي إلى مستوى الصفحة. كلاهما مطلوب.
افتراضيًا، يرسل next-i18next
اللغة النشطة فقط إلى العميل عند كل طلب. يساعد هذا في تقليل حجم الحمولة الأولية المرسلة إلى العميل. ولكن في بعض الحالات قد يحتاج المرء إلى ترجمات للغات أخرى في وقت التشغيل أيضًا. على سبيل المثال، عند استخدام الخطاف getFixedT of useTranslation
.
لتغيير السلوك وتحميل لغات إضافية، ما عليك سوى تمرير مصفوفة من اللغات كوسيطة أخيرة إلى serverSideTranslations
.
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export async function getStaticProps({ locale }) {
return {
props: {
- ...(await serverSideTranslations(locale, ['common', 'footer'])),
+ ...(await serverSideTranslations(locale, ['common', 'footer'], null, ['en', 'no'])),
},
};
}
ونتيجة لذلك، سيتم دائمًا تحميل الترجمات لكل من اللغات no
و en
المحلية بغض النظر عن اللغة الحالية.
ملاحظة: يجب إضافة الوسيطة الإضافية إلى كافة الصفحات التي تستخدم وظيفة
getFixedT
.
افتراضيًا، سيضيف next-i18next
defaultLocale
كبديل. لتغيير هذا، يمكنك تعيين fallbackLng
. جميع القيم التي يدعمها i18next
( string
، array
، object
، function
) مدعومة بواسطة next-i18next
أيضًا.
بالإضافة إلى ذلك، يمكن ضبط nonExplicitSupportedLngs
على true
لدعم جميع متغيرات اللغة، دون الحاجة إلى توفير ملفات JSON لكل منها. لاحظ أنه لا يزال يتعين تضمين كافة المتغيرات في locales
لتمكين التوجيه داخل next.js
.
ملاحظة: يمكن استخدام
fallbackLng
وnonExplicitSupportedLngs
في الوقت نفسه. يوجد استثناء واحد فقط: لا يمكنك استخدام دالة لـfallbackLng
عندما تكونnonExplicitSupportedLngs
true
،
module . exports = {
i18n : {
defaultLocale : 'en' ,
locales : [ 'en' , 'fr' , 'de-AT' , 'de-DE' , 'de-CH' ] ,
} ,
fallbackLng : {
default : [ 'en' ] ,
'de-CH' : [ 'fr' ] ,
} ,
nonExplicitSupportedLngs : true ,
// de, fr and en will be loaded as fallback languages for de-CH
}
انتبه إلى أن استخدام fallbackLng
و nonExplicitSupportedLngs
يمكن أن يؤدي بسهولة إلى زيادة الحجم الأولي للصفحة.
لمعلوماتك: لن يؤدي تعيين fallbackLng
إلى false
إلى إجراء تسلسل للغتك الاحتياطية (عادةً defaultLocale
). سيؤدي هذا إلى تقليل حجم تحميل الصفحة الأولي.
إذا كنت بحاجة إلى تعديل خيارات التكوين الأكثر تقدمًا، فيمكنك تمريرها عبر next-i18next.config.js
. على سبيل المثال:
module . exports = {
i18n : {
defaultLocale : 'en' ,
locales : [ 'en' , 'de' ] ,
} ,
localePath :
typeof window === 'undefined'
? require ( 'path' ) . resolve ( './my-custom/path' )
: '/public/my-custom/path' ,
ns : [ 'common' ] ,
}
بعض ملحقات i18next
(التي يمكنك تمريرها إلى config.use
) غير قابلة للتسلسل، لأنها تحتوي على وظائف وعناصر JavaScript أولية أخرى.
قد تواجه هذا إذا كانت حالة الاستخدام الخاصة بك أكثر تقدمًا. سترى Next.js يلقي خطأ مثل:
Error: Error serializing `._nextI18Next.userConfig.use[0].process` returned from `getStaticProps` in "/my-page".
Reason: `function` cannot be serialized as JSON. Please only return JSON serializable data types.
لإصلاح ذلك، ستحتاج إلى ضبط config.serializeConfig
على false
، وتمرير التكوين يدويًا إلى appWithTranslation
:
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../next-i18next.config.js'
const MyApp = ( { Component , pageProps } ) => (
< Component { ... pageProps } / >
)
export default appWithTranslation ( MyApp , nextI18NextConfig )
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import nextI18NextConfig from '../next-i18next.config.js'
export const getStaticProps = async ( { locale } ) => ( {
props : {
... ( await serverSideTranslations (
locale ,
[ 'common' , 'footer' ] ,
nextI18NextConfig
) ) ,
} ,
} )
عند الاستخدام على الصفحات التي تم إنشاؤها من جانب الخادم باستخدام getStaticPaths
fallback: true
أو fallback: 'blocking'
، فإن الإعداد الافتراضي المشار إليه أعلاه سيؤدي إلى إلغاء تحميل التطبيق وإعادة تحميله عند كل تحميل، مما يتسبب في عواقب سلبية مختلفة مثل استدعاء كل useEffect(() => {...}, [])
ربط مرتين وتدهور طفيف في الأداء.
ويرجع ذلك إلى حقيقة أنه بالنسبة لتلك الصفحات، يقوم Next.js بإجراء أول عرض باستخدام serverSideProps
فارغ ثم عرض ثانٍ باستخدام serverSideProps
الذي يتضمن ترجمات next-i18next
. باستخدام الإعداد الافتراضي، يكون مثيل i18n
undefined
في البداية عندما يكون serverSideProps
empty
، مما يتسبب في إلغاء التحميل وإعادة التحميل.
للتخفيف من هذه المشكلة، يمكنك القيام بما يلي:
import { UserConfig } from 'next-i18next' ;
import nextI18NextConfig from '../next-i18next.config.js'
const emptyInitialI18NextConfig : UserConfig = {
i18n : {
defaultLocale : nextI18NextConfig . i18n . defaultLocale ,
locales : nextI18NextConfig . i18n . locales ,
} ,
} ;
const MyApp = ( { Component , pageProps } ) => (
< Component { ... pageProps } / >
)
export default appWithTranslation ( MyApp , emptyInitialI18NextConfig ) // Makes sure the initial i18n instance is not undefined
سيعمل هذا طالما أنك تتأكد من أن التعليمات البرمجية من جانب العميل، في حالة الصفحة الاحتياطية، لا تحاول عرض أي ترجمة وإلا فسوف تحصل على خطأ "عدم تطابق الخادم والعميل" من Next.js (بسبب حقيقة أن الخادم لديه ترجمة فعلية في html الخاص به بينما لدى العميل html مفتاح الترجمة في نفس المكان).
هذا أمر طبيعي وجيد: لا يجب أن تعرض مفتاح الترجمة للمستخدم الخاص بك على أي حال!
منذ الإصدار 11.0.0، يوفر next-i18next أيضًا دعمًا لتحميل الترجمات من جانب العميل.
في بعض حالات الاستخدام، قد ترغب في تحميل ملف ترجمة ديناميكيًا دون الحاجة إلى استخدام serverSideTranslations
. يمكن أن يكون هذا مفيدًا بشكل خاص للمكونات التي يتم تحميلها ببطء والتي لا تريد أن تؤدي إلى إبطاء الصفحات.
مزيد من المعلومات حول ذلك يمكن العثور عليها هنا.
نظرًا لأنه يتم تحميل الموارد مرة واحدة عند بدء تشغيل الخادم، فلن يتم تحميل أي تغييرات يتم إجراؤها على ملفات JSON للترجمة قيد التطوير حتى يتم إعادة تشغيل الخادم.
في الإنتاج، لا يمثل هذا مشكلة، ولكن في التطوير قد ترغب في رؤية تحديثات لملفات الترجمة JSON الخاصة بك دون الحاجة إلى إعادة تشغيل خادم التطوير الخاص بك في كل مرة. للقيام بذلك، قم بتعيين خيار التكوين reloadOnPrerender
على true
.
سيؤدي هذا الخيار إلى إعادة تحميل ترجماتك عندما يتم استدعاء serverSideTranslations
(في getStaticProps
أو getServerSideProps
). إذا كنت تستخدم serverSideTranslations
في getServerSideProps
، فمن المستحسن تعطيل reloadOnPrerender
في بيئات الإنتاج لتجنب إعادة تحميل الموارد في كل استدعاء للخادم.
مفتاح | القيمة الافتراضية | ملحوظة |
---|---|---|
defaultNS | 'common' | |
localePath | './public/locales' | يمكن أن تكون وظيفة، انظر الملاحظة أدناه. (يمكن أيضًا أن يكون فارغًا، في حالة تمرير خيار الموارد مباشرةً عبر التكوين، كما هو الحال هنا) |
localeExtension | 'json' | يتم تجاهله إذا كان localePath عبارة عن وظيفة. |
localeStructure | '{{lng}}/{{ns}}' | يتم تجاهله إذا كان localePath عبارة عن وظيفة. |
reloadOnPrerender | false | |
serializeConfig | true | |
use (للمكونات الإضافية) | [] | |
onPreInitI18next | undefined | أي (i18n) => i18n.on('failedLoading', handleFailedLoading) |
localePath
كدالة هو من النموذج (locale: string, namespace: string, missing: boolean) => string
تعيد المسار بأكمله بما في ذلك اسم الملف والامتداد. عندما يكون missing
صحيحًا، قم بإرجاع المسار لخيار addPath
الخاص بـ i18next-fs-backend
، وعندما يكون خطأ، قم بإرجاع المسار لخيار loadPath
. مزيد من المعلومات في الريبو i18next-fs-backend
.
إذا كان localePath عبارة عن وظيفة، فتأكد أيضًا من تحديد خيار ns، لأنه من جانب الخادم لا يمكننا التحميل المسبق لمساحات الأسماء بعد ذلك.
يمكن أيضًا تمرير جميع خيارات i18next وخيارات React-i18next الأخرى.
يمكنك أيضًا تمرير resources
مباشرةً مع ضبط localePath
على null
.
بشكل افتراضي، يستخدم i18next {{
كبادئة و }}
كلاحقة للاستيفاء. إذا كنت تريد/تحتاج إلى تجاوز إعدادات الاستيفاء هذه، فيجب عليك أيضًا تحديد إعداد localeStructure
بديل يطابق البادئة واللاحقة المخصصة لديك.
على سبيل المثال، إذا كنت تريد استخدام {
و }
فسيبدو التكوين كما يلي:
{
i18n : {
defaultLocale : 'en' ,
locales : [ 'en' , 'nl' ] ,
} ,
interpolation : {
prefix : '{' ,
suffix : '}' ,
} ,
localeStructure : '{lng}/{ns}' ,
}
next-i18next.config.js
المخصص إذا كنت تريد تغيير مسار التكوين الافتراضي، فيمكنك تعيين متغير البيئة I18NEXT_DEFAULT_CONFIG_PATH
.
على سبيل المثال، داخل ملف .env
، يمكنك تعيين مسار ثابت:
I18NEXT_DEFAULT_CONFIG_PATH=/path/to/project/apps/my-app/next-i18next.config.js
أو يمكنك استخدام خدعة للمسار الديناميكي وتعيين ما يلي داخل next.config.js
:
process . env . I18NEXT_DEFAULT_CONFIG_PATH = ` ${ __dirname } /next-i18next.config.js` ;
// ... Some other imports
const { i18n } = require ( './next-i18next.config' ) ;
// ... Some other code
module . exports = {
i18n ,
...
} ;
هذا يعني أن ملف التكوين i18n سيكون في نفس الدليل مثل next.config.js
ولا يهم مكان وجود دليل العمل الحالي الخاص بك. يساعد هذا على سبيل المثال nx
عندما يكون لديك monorepo وتبدأ تطبيقك من جذر المشروع ولكن التطبيق موجود في apps/{appName}
.
ملاحظة: إذا لم يكن التكوين next-i18next.config.js
الخاص بك موجودًا في نفس الدليل مثل next.config.js
، فيجب عليك نسخه يدويًا (أو بواسطة برنامج نصي مخصص).
إذا كنت تخطط لإضافة next-i18next بشكل تدريجي إلى مشروعك، فنوصيك بتمرير next-i18next.config
إلى appWithTranslation
لتجنب أي مشكلات.
أي
import nextI18nextConfig from '../../next-i18next.config' ;
//...
export default appWithTranslation ( MyApp , nextI18nextConfig ) ;
راجع العدد رقم 2259 للمزيد من المعلومات.
قد لا تتمكن بعض PaaS بدون خادم من تحديد مسار ترجماتك وتتطلب تكوينًا إضافيًا. إذا كانت لديك مشكلات في نظام الملفات باستخدام serverSideTranslations
، فاضبط config.localePath
لاستخدام path.resolve
. يمكن العثور على مثال هنا.
بالنسبة لنشر Docker، لاحظ أنه إذا كنت تستخدم ملف Dockerfile
من مستندات Next.js، فلا تنس نسخ next.config.js
و next-i18next.config.js
في صورة Docker.
COPY --from=builder /app/next.config.js ./next.config.js
COPY --from=builder /app/next-i18next.config.js ./next-i18next.config.js
إذا اخترت استخدام واجهة i18next الخلفية مختلفة عن الواجهة الخلفية i18next-fs-backend المضمنة، فستحتاج إلى التأكد من تحميل موارد الترجمة قبل استدعاء الدالة t
. نظرًا لأن React suspense غير مدعوم حتى الآن لـ SSR، فيمكن حل هذه المشكلة بطريقتين مختلفتين:
1) تحميل مساحات الأسماء:
اضبط خيار ns
، كما في هذا المثال. سيؤدي القيام بذلك إلى ضمان تحميل جميع موارد الترجمة عند التهيئة.
2) التحقق من العلم الجاهز:
إذا لم تتمكن أو لا تريد توفير المصفوفة ns
، فإن استدعاء الدالة t
سيؤدي إلى تحميل مساحات الأسماء بسرعة. هذا يعني أنك ستحتاج إلى التعامل مع الحالة "غير جاهز" عن طريق تحديد ready === true
أو props.tReady === true
. سيؤدي عدم القيام بذلك إلى عرض ترجماتك قبل تحميلها، مما سيؤدي إلى استدعاء "حفظ مفقود" على الرغم من وجود الترجمات بالفعل (لم يتم تحميلها بعد). يمكن القيام بذلك باستخدام خطاف useTranslation أو withTranslation HOC.
هل تحاول إنشاء تصدير HTML ثابت عن طريق تنفيذ next export
ويتلقى هذا الخطأ؟
خطأ: دعم i18n غير متوافق مع عملية التصدير التالية. انظر هنا لمزيد من المعلومات حول النشر: https://nextjs.org/docs/deployment
ولكن هناك طريقة للتغلب على ذلك بمساعدة كاشف اللغة التالية. تحقق من منشور المدونة هذا وهذا المشروع النموذجي.
لديك طرق متعددة لاستخدام الدالة t في المكون الفرعي الخاص بك:
t
عبر الدعائم إلى الأطفالuseTranslation
، كما في هذا المثال:next-i18next/examples/simple/components/Footer.tsx
السطر 6 في e6b5085
withTranslation
وبشكل عام، تحتاج دائمًا إلى التأكد من أن serverSideTranslations يحتوي على جميع مساحات الأسماء التي تحتاجها في الشجرة.
الشكر موصول لهؤلاء الأشخاص الرائعين (مفتاح الرموز التعبيرية):
روب كابيليني | الكسندر كاتشكاييف ؟ ؟ | ماتياس ووبي ؟ | لوكاس فيليسيانو ؟ ؟ | ريان ليونج | ناثان فريمل ؟ | إسحاق هينمان ️️️️♿️ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ?? ؟ ؟ ؟ ؟ ؟ ?️ ? |
أدريانو رايانو ️️️️♿️ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ؟ ?? ؟ ؟ ؟ ؟ ؟ ?️ ? | فيليكس موشيف ؟ | سيباستيان فانفيلتيم ؟ ؟ |
يتبع هذا المشروع مواصفات جميع المساهمين. المساهمات من أي نوع موضع ترحيب!
التوطين كخدمة - locize.com
هل تحتاج إلى إدارة الترجمة؟ هل تريد تعديل ترجماتك باستخدام محرر InContext؟ استخدم النسخة الأصلية التي قدمها لك القائمون على i18next!
باستخدام locize، فإنك تدعم بشكل مباشر مستقبل i18next وnext-i18next.