i18n
یک موتور بینالمللیسازی مستقل از فریمورک: هستهای کوچک و واکنشی (I18nCore) با یک singleton سراسری اختیاری (createI18n / useI18n). هیچچیز اینجا به DOM دست نمیزند: هرطور میخواهید به رابط کاربری گرهاش بزنید.
import { createI18n, useI18n } from 'ranuts/i18n';از ranuts/utils هم دوباره صادر میشود. اگر تنها به i18n نیاز دارید از ranuts/i18n وارد کنید: این نقطه ورودی فقط موتور و دو کمککنندهاش را دارد، نه هرچه که بسته فراخِ utils ممکن است با خود بیاورد.
کاربرد
import { createI18n, useI18n } from 'ranuts/i18n';
createI18n({
messages: {
en: { 'hero.title': 'Hello, {name}', 'nav.docs': 'Docs' },
zh: { 'hero.title': '你好,{name}', 'nav.docs': '文档' },
},
fallbackLocale: 'en',
persist: true,
detectNavigator: true,
});
const i18n = useI18n()!;
i18n.t('hero.title', { name: 'Ada' }); // "Hello, Ada"
i18n.setLocale('zh');
i18n.t('hero.title', { name: 'Ada' }); // "你好,Ada"واژهنامهها تختاند: t() مستقیم messages[locale][key] را میجوید، پس کلیدها رشتههای عینیاند مثل 'hero.title'، نه شیءهای تودرتو.
زبان آغازین
یک بار در سازنده و به این ترتیب حل میشود:
- انتخاب ذخیرهشده در
localStorage(تنها وقتیpersistروشن باشد و فقط اگر آن زبان واژهنامه داشته باشد) config.locale- زبانهای مرورگر (تنها وقتی
detectNavigatorروشن باشد) fallbackLocale
گام ۳ از resolveLocale میگذرد که بهجای تنها navigator.language، کل فهرست مرتب navigator.languages را میخواند: خوانندهای که انتخاب اولش میان واژهنامههای شما نیست، بهجای سقوط مستقیم به زبان جایگزین، انتخاب دومش را میگیرد.
درج مقدار
t(key, params) جاینگهدارهای {param} را در یک گذر از چپ به راست جایگزین میکند و از قرارداد رشتههای قالب در format! زبان Rust، str.format پایتون و String.Format داتنت پیروی میکند:
| ورودی | خروجی |
|---|---|
{{ | یک { عینی |
}} | یک } عینی |
{name} | params.name که به متن تبدیل شده |
{name} بدون چنین پارامتری | دستنخورده میماند، پس جاینگهدارِ سرگردان دیده میشود نه اینکه خالی بماند |
یک { / } تنها، یا گروهی با فاصله مثل { x }، جاینگهدار نیست و عیناً بیرون داده میشود؛ پس CSS و JSON و تکههای کد درون یک پیام سالم رد میشوند. برای پیچیدن یک مقدار در آکولاد عینی، جفت بیرونی را دو برابر کنید: {{{name}}}.
واژهنامههای تایپدار
شکل واژهنامهتان را بهعنوان آرگومان نوع بدهید تا هر فراخوانی t() در زمان کامپایل بررسی شود. بدون آن، کلیدی که نامش عوض شده یا غلط تایپ شده بیصدا به «خودِ کلید را رسم کن» تنزل میکند: کاربر جایی که باید جملهای باشد agentModelFirstDownlaod میبیند، و تا آن لحظه هیچچیز شکست نمیخورد.
interface Messages {
save: string;
cancel: string;
}
const i18n = createI18n<Messages>({
messages: {
en: { save: 'Save', cancel: 'Cancel' },
'zh-CN': { save: '保存' }, // هنوز در حال ترجمه — اشکالی ندارد
},
fallbackLocale: 'en',
});
i18n.t('save'); // ok
i18n.t('saev'); // خطای کامپایل
useI18n<Messages>()?.t('cancel'); // همان نوع را دوباره بدهید تا بررسی برقرار بماندسه نکته این را از «صرفاً موجود» به «واقعاً بهدردبخور» تبدیل میکند:
- هر زبان
Partialاست. ترجمهای که در جریان است حالت عادی ماجراست؛ زبان جایگزین آنچه را یک زبان هنوز پر نکرده پوشش میدهد. - نوع از آرگومان نوع میآید، هرگز از داده.
messagesدرNoInferپیچیده شده، پس زبانهایی با مجموعهکلیدهای متفاوت نمیتوانند TypeScript را وادار کنند اشتراک آنها را استنتاج کند. وگرنه کلیدی که تنها زبان جایگزین تعریفش کرده در هر محل فراخوانی رد میشد و ترجمهای ناتمام بهجای بازگشت در زمان اجرا، بیلد را میشکست. interfaceهم کار میکند، نه فقطtype. قیدStringValues<T>است ({ [K in keyof T]: string }) نهRecord<string, string>، چون TypeScript امضای اندیس ضمنی را تنها به نامهای مستعار نوع میدهد: قید گذاشتن به شیوه بدیهی، همه مصرفکنندهها را وادار میکرد واژهنامهشان را بهtypeبازنویسی کنند.
نگذاشتن آرگومان نوع، رفتار بیتایپ را دقیقاً همانطور نگه میدارد: MessageDict پیشفرض همان Record<string, string> است که keyof آن string میشود.
پیکربندی
| میدان | توضیح | نوع | پیشفرض |
|---|---|---|---|
locale | زبان آغازین. وقتی persist روشن باشد، انتخاب ذخیرهشده بر آن میچربد | string | - |
fallbackLocale | زبانی که وقتی کلیدی در زبان فعال نباشد به کار میرود | string | 'en' |
messages | زبان ← کلید ← رشته | LocaleMessages | {} |
persist | زبان فعال را در localStorage نگه میدارد | boolean | false |
storageKey | کلید localStorage وقتی persist روشن است | string | 'ran-locale' |
detectNavigator | زبان آغازین را از ترجیحهای زبانی مرورگر برمیدارد | boolean | false |
API
createI18n
singleton سراسری را میسازد و ثبت میکند.
پارامترها
| پارامتر | توضیح | نوع | پیشفرض |
|---|---|---|---|
config | پیکربندی را ببینید | I18nConfig | {} |
بازگشت
| آرگومان | توضیح | نوع |
|---|---|---|
i18n | نمونه تازه | I18nCore |
useI18n
نمونه سراسری فعال را برمیگرداند، یا null وقتی هیچ نمونهای ساخته نشده باشد.
بازگشت
| آرگومان | توضیح | نوع |
|---|---|---|
i18n | نمونه فعال یا null | I18nCore | null |
I18nCore
| عضو | توضیح |
|---|---|
t(key, params?) | ترجمه میکند؛ به زبان جایگزین و سپس به خود کلید برمیگردد |
locale / getLocale() | زبان فعال |
setLocale(locale) | زبان را عوض میکند، (در صورت روشن بودن) ذخیره و خبر میدهد. اگر تغییری نباشد کاری نمیکند |
addMessages(locale, dict) | واژهنامهای را در یک زبان ادغام میکند و در صورت نبود میسازدش |
getMessages(locale?) | واژهنامه یک زبان، یا {} |
availableLocales | زبانهایی که واژهنامه ثبتشده دارند |
onChange(fn) | تغییر زبان را مشترک میشود؛ تابعی برای لغو اشتراک برمیگرداند |
destroy() | همه مشترکان را برمیدارد |
SSR
امن است. همه دسترسیها به localStorage و navigator محافظت شدهاند، پس ساختن یک نمونه هنگام رندر سمت سرور به config.locale یا fallbackLocale میرسد.