
Refine i18nProvider 完整指南为 React 管理后台构建多语言国际化方案【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文聚焦 Refine 框架的国际化i18n核心抽象 ——i18nProvider讲解其接口契约、三个核心方法translate/changeLocale/getLocale的实现细节、如何接入Refine /组件、如何在业务组件中通过useTranslation系列 hooks 调用翻译能力以及如何通过翻译文件覆盖 Refine 内置组件文案。读完本文你将能够在自己的 React 管理后台中接入任意 i18n 库如 react-i18next并实现完整的运行时多语言切换能力。i18nProviderRefine 的国际化抽象层国际化Internationalization简称 i18n允许软件针对不同地区与语言进行本地化适配。Refine 本身不绑定任何 i18n 框架而是定义了一个轻量的 provider 契约 ——i18nProvider你可以在它之上接入 react-i18next、i18next、polyglot 等任意成熟方案。Refine 期望的I18nProvider类型定义如下该类型实际声明于 packages/core/src/contexts/i18n/types.tsimport { I18nProvider } from refinedev/core; const i18nProvider: I18nProvider { translate: (key: string, options?: any, defaultMessage?: string) string, changeLocale: (lang: string, options?: any) Promise, getLocale: () string, };从源码可以看到这三个方法的类型分别为type TranslateFunction ( key: string, options?: any, defaultMessage?: string, ) string; type ChangeLocaleFunction ( locale: string, options?: any, ) Promiseany | any; type GetLocaleFunction () string;也就是说provider 需要暴露一个同步返回字符串的translate、一个负责切换语言并返回 Promise 的changeLocale、以及一个返回当前语言标识的getLocale。translate承担 把 key 翻译成文案changeLocale承担 运行时切换语言getLocale承担 查询当前语言状态 —— 三者组合起来就构成了完整的翻译能力闭环。注册 i18nProvider接入Refine /创建好i18nProvider之后将它作为 prop 传给Refine /组件即可全局启用import { Refine } from refinedev/core; import i18nProvider from ./i18nProvider; const App: React.FC () { return ( Refine i18nProvider{i18nProvider} /* 其他 providers如 dataProvider、authProvider 等 */ {/* 应用内容 */} /Refine ); };在 Refine 源码中I18nProvider类型被IRefineOptions对应的 contexts/refine/types.ts 引用并通过 contexts/i18n/index.tsx 中的I18nContextProvider注入到 React Context 中export const I18nContext React.createContextII18nContext({}); export const I18nContextProvider: React.FCPropsWithChildrenII18nContext ({ i18nProvider, children, }) { return ( I18nContext.Provider value{{ i18nProvider }} {children} /I18nContext.Provider ); };注册完成后你就可以通过useTranslationhook 在任意组件中获得翻译能力。注意I18nContext的默认值是空对象未传入i18nProvider时部分 hook如useGetLocale会直接抛出错误这一点在后面的源码分析中会详细说明。三个核心方法详解translate函数重载与回退逻辑translate将参数透传给i18nProvider.translate并期望返回字符串。它支持两种函数签名函数重载function translate(key: string, options?: any, defaultMessage?: string): string; function translate(key: string, defaultMessage?: string): string;第一种用法传入key、options、defaultMessage三个参数第二种用法传入key和defaultMessage两个参数options为可选参数。签名一keydefaultMessageimport { I18nProvider } from refinedev/core; import { useTranslation } from react-i18next; const { t } useTranslation(); const i18nProvider: I18nProvider { translate: (key: string, defaultMessage?: string) t(key, defaultMessage), // ... };在业务组件中调用import { useTranslation } from refinedev/core; const { translate } useTranslation(); // 若 posts.fields.title 在翻译文件中存在则返回对应文案否则返回默认值 Title translate(posts.fields.title, Title);签名二keyoptionsdefaultMessageimport { I18nProvider } from refinedev/core; import { useTranslation } from react-i18next; const { t } useTranslation(); const i18nProvider: I18nProvider { translate: (key: string, options?: any, defaultMessage?: string) t(key, defaultMessage, options), // ... };import { useTranslation } from refinedev/core; const { translate } useTranslation(); // options 可用于指定命名空间或插值变量 const title translate(posts.fields.title, { ns: resources }, Title);从源码 packages/core/src/hooks/i18n/useTranslate.ts 可以看出hook 内部对三种入参形态做了统一的兜底处理function translate( key: string, options?: string | any, defaultMessage?: string, ) { return ( i18nProvider?.translate(key, options, defaultMessage) ?? defaultMessage ?? (typeof options string typeof defaultMessage undefined ? options : key) ); }这段代码揭示了两个重要的回退规则当i18nProvider未定义时translate不会崩溃而是依次回退到defaultMessage、字符串形式的options最后回退到key本身也就是说即使你暂时没有接入任何 i18n 框架组件中使用translate(posts.fields.title, Title)也会安全地渲染出 Title。这个设计让组件在翻译缺失时依然可用非常适合渐进式接入国际化。changeLocale运行时切换语言changeLocale接收新的 locale 标识透传给i18nProvider.changeLocale并返回一个 Promise。它的类型签名如下changeLocale: (locale: string, options?: any) Promiseany;典型的实现基于 react-i18next会在内部调用i18n.changeLanguage(locale)import { I18nProvider } from refinedev/core; import i18n from ./i18n; const i18nProvider: I18nProvider { // ... changeLocale: (lang: string) i18n.changeLanguage(lang), // ... };getLocale读取当前语言getLocale期望返回一个字符串即从i18nProvider中读取当前 localegetLocale: () string;典型实现const i18nProvider: I18nProvider { // ... getLocale: () i18n.language, // ... };在业务组件中使用 useTranslation 系列 hooksuseTranslation是 Refine 提供的统一入口它内部组合了三个更细粒度的 hook。源码 packages/core/src/hooks/i18n/useTranslation.tsx 清楚地展示了这一点export const useTranslation () { const translate useTranslate(); const changeLocale useSetLocale(); const getLocale useGetLocale(); return { translate, changeLocale, getLocale, }; };语言切换组件示例下面是一个完整可用的多语言切换组件文档示例它同时使用了translate、changeLocale、getLocale三个方法import { useTranslation } from refinedev/core; export const MyComponent () { const { translate, getLocale, changeLocale } useTranslation(); const currentLocale getLocale(); return ( div h1{translate(languages)}/h1 button onClick{() changeLocale(en)} disabled{currentLocale en} English /button button onClick{() changeLocale(de)} disabled{currentLocale de} German /button /div ); };三个细分 hook 的行为差异useTranslateuseTranslate.ts返回translate函数带上述回退逻辑未接入 provider 时不会报错。useSetLocaleuseSetLocale.ts返回changeLocale函数内部用useCallback包裹export const useSetLocale () { const { i18nProvider } useContext(I18nContext); return useCallback((lang: string) i18nProvider?.changeLocale(lang), []); };useGetLocaleuseGetLocale.ts返回getLocale函数但注意 —— 与useTranslate不同当i18nProvider未定义时它会抛出明确错误export const useGetLocale: UseGetLocaleType () { const { i18nProvider } useContext(I18nContext); if (!i18nProvider) { throw new Error( useGetLocale cannot be called without i18n provider being defined., ); } return useCallback(() i18nProvider.getLocale(), []); };因此如果你的应用中存在未接入i18nProvider的页面或组件应避免直接调用useGetLocale或在使用前确认 provider 已经注册。如果你只需要翻译单个文本也可以使用useTranslate的简化形态import { useTranslate } from refinedev/core; export const MyComponent () { const translate useTranslate(); return button{translate(my.translate.text)}/button; };覆盖内置组件文案Translation 文件全量清单Refine 的所有内置组件登录页、按钮、表格、通知、面包屑等都支持 i18n。这意味着你不需要修改任何组件源码只需创建自己的翻译文件即可覆盖 Refine 的默认文本。完整的可覆盖翻译 key 清单维护在 documentation/docs/partials/_partial-translation-file-en.md 中以下为其完整内容可直接作为locales/en/common.json的起点{ pages: { login: { title: Sign in to your account, signin: Sign in, signup: Sign up, divider: or, fields: { email: Email, password: Password }, errors: { validEmail: Invalid email address, requiredEmail: Email is required, requiredPassword: Password is required }, buttons: { submit: Login, forgotPassword: Forgot password?, noAccount: Don’t have an account?, rememberMe: Remember me } }, forgotPassword: { title: Forgot your password?, fields: { email: Email }, errors: { validEmail: Invalid email address, requiredEmail: Email is required }, buttons: { submit: Send reset instructions } }, register: { title: Sign up for your account, fields: { email: Email, password: Password }, errors: { validEmail: Invalid email address, requiredEmail: Email is required, requiredPassword: Password is required }, buttons: { submit: Register, haveAccount: Have an account? } }, updatePassword: { title: Update password, fields: { password: New Password, confirmPassword: Confirm new password }, errors: { confirmPasswordNotMatch: Passwords do not match, requiredPassword: Password required, requiredConfirmPassword: Confirm password is required }, buttons: { submit: Update } }, error: { info: You may have forgotten to add the {{action}} component to {{resource}} resource., 404: Sorry, the page you visited does not exist., resource404: Are you sure you have created the {{resource}} resource., backHome: Back Home } }, actions: { list: List, create: Create, edit: Edit, show: Show }, buttons: { create: Create, save: Save, logout: Logout, delete: Delete, edit: Edit, cancel: Cancel, confirm: Are you sure?, filter: Filter, clear: Clear, refresh: Refresh, show: Show, undo: Undo, import: Import, clone: Clone, notAccessTitle: You dont have permission to access }, warnWhenUnsavedChanges: Are you sure you want to leave? You have unsaved changes., notifications: { success: Successful, error: Error (status code: {{statusCode}}), undoable: You have {{seconds}} seconds to undo, createSuccess: Successfully created {{resource}}, createError: There was an error creating {{resource}} (status code: {{statusCode}}), deleteSuccess: Successfully deleted {{resource}}, deleteError: Error when deleting {{resource}} (status code: {{statusCode}}), editSuccess: Successfully edited {{resource}}, editError: Error when editing {{resource}} (status code: {{statusCode}}), importProgress: Importing: {{processed}}/{{total}} }, loading: Loading, tags: { clone: Clone }, dashboard: { title: Dashboard }, posts: { posts: Posts, fields: { id: Id, title: Title, category: Category, status: { title: Status, published: Published, draft: Draft, rejected: Rejected }, content: Content, createdAt: Created At }, titles: { create: Create Post, edit: Edit Post, list: Posts, show: Show Post } }, table: { actions: Actions }, documentTitle: { default: refine, suffix: | Refine, post: { list: Posts | Refine, show: #{{id}} Show Post | Refine, edit: #{{id}} Edit Post | Refine, create: Create new Post | Refine, clone: #{{id}} Clone Post | Refine } }, autoSave: { success: saved, error: auto save failure, loading: saving..., idle: waiting for changes } }几点使用建议命名空间与默认 key上面的 JSON 以common作为默认命名空间如果你的 i18n 配置把defaultNS设为commonRefine 内置组件的文案就会自动从这里解析。插值变量注意{{action}}、{{resource}}、{{statusCode}}、{{seconds}}、{{processed}}、{{total}}、{{id}}这类占位符它们由 Refine 在调用translate时通过options传入翻译文件必须原样保留。documentTitle特殊项它用于控制浏览器标签页标题例如post.list会在列表页显示 Posts | Refine这要求你的路由 provider 支持动态文档标题。autoSave特殊项当表单开启自动保存autoSave时会用到这组文案分别对应已保存、保存失败、保存中和等待修改四种状态。集成真实 i18n 框架以 react-i18next 为例仓库中的 i18n-react 示例 演示了如何用 i18next react-i18next 构建完整的i18nProvider。其 src/i18n.ts 是初始化 i18next 的典型配置import i18n from i18next; import { initReactI18next } from react-i18next; import Backend from i18next-xhr-backend; import detector from i18next-browser-languagedetector; i18n .use(Backend) .use(detector) .use(initReactI18next) .init({ supportedLngs: [en, de], backend: { loadPath: /locales/{{lng}}/{{ns}}.json, }, ns: [common], defaultNS: common, fallbackLng: [en, de], }); export default i18n;该配置的关键点supportedLngs: [en, de]声明支持的语言示例项目支持英语和德语backend.loadPath按/locales/{{lng}}/{{ns}}.json的路径模式按需加载翻译文件{{lng}}是语言代码、{{ns}}是命名空间ns/defaultNS声明并使用common命名空间与上一节翻译文件中的命名空间保持一致fallbackLng当某个语言缺少翻译时回退到可用语言避免界面出现空白。基于此i18nProvider可以这样实现import i18n from ./i18n; import { I18nProvider } from refinedev/core; const i18nProvider: I18nProvider { translate: (key, options, defaultMessage) i18n.t(key, defaultMessage, options), changeLocale: (lang) i18n.changeLanguage(lang), getLocale: () i18n.language, };如果你想参考 Next.js 场景下的集成方式仓库中还提供了 i18n-nextjs 示例展示了在服务端渲染框架中如何组织 i18n。FAQ如何自动化生成多语言翻译文件为每种语言手工维护翻译 JSON 很繁琐。社区中有一种成熟的思路在 CI 流水线中加入基于 DeepLAI 翻译服务的自动化步骤由机器人自动把locales/en下的 JSON 翻译成其他语言并提交回仓库。具体做法是引入一个 GitHub Action监听翻译文件的变更事件触发 DeepL 翻译任务将产物写回locales目录。这样开发者只需维护单一语言如英语的翻译源文件其余语言由流水线自动同步既保证翻译质量的一致性也减少了人工重复劳动。参考示例i18n-react 示例基于 react-i18next 的完整 i18nProvider 实现支持 en/de 双语切换i18n-nextjs 示例Next.js 场景下的 i18n 集成useTranslation hook 文档useTranslation/useTranslate/useSetLocale/useGetLocale的详细用法核心类型定义I18nProvider接口的权威源码翻译文件全量清单Refine 内置组件可覆盖的全部翻译 key。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考