react-i18next 国际化支持
一、今日整体工作内容今天完成的是做了国际化支持实现中英文语言切换。首先看了官方的 API 文档它支持不同的编辑器语言。然后我用 React Next 18 引入了相关包自己维护了一个 locale 键值数组对应中文和英文字段。全文国际化不是简单调用 API它需要配置台注入内容。因为我一开始觉得比较麻烦我用了很多文本的hard code还有一些组件包括主编辑区也是基于 BlockNote 这个开源库开发的其实理一下也还好因为后面用了框架。我用的是 i18n 框架react-i18next能自动读取我维护的中英文 JSON 文件。i18n 框架的核心逻辑本质是 JSON 语言文件的“管理器”和“分发员”。核心解决了三件事语言文件加载、字段映射、语言切换时的状态同步。它提供了一个 t ( )函数。我在实际代码里写了很多 t ( )函数填入对应字段就可以切换中英文我也不太清楚底层原理。我们要做的就是翻译工作和工程化基建。然后又做了一个 UI 组件只是一个按钮。这个东西我没找到现成的用 segmented 写并且补充localstorage 持久化记住之前的选择。在 main 里然后把 i18n 实例放上。import{StrictMode}fromreact;import{createRoot}fromreact-dom/client;import{ThemeProvider}fromnext-themes;importAppfrom./App.tsx;import./locales/i18.ts;createRoot(document.getElementById(root)!).render(StrictModeThemeProviderApp//ThemeProvider/StrictMode,);二、国际化支持一编辑器语言切换异常后面我就是在做一些很没用的工作。编辑区为什么不能跟着切换语言编辑器语言为什么不会改我看 BlockNote 原生是支持的然后我就一直在弄这个…来看官方文档是怎么介绍这块Integration with i18n LibrariesYou can integrate BlockNote with popular i18n libraries like react-i18next or next-intl:import{useCreateBlockNote,BlockNoteView}fromblocknote/react;import{useTranslation}fromreact-i18next;import*aslocalesfromblocknote/core/locales;functionI18nEditor(){const{i18n}useTranslation();consteditoruseCreateBlockNote({dictionary:locales[i18n.languageaskeyoftypeoflocales]||locales.en,});returnBlockNoteView editor{editor}/;}现在代码里一个问题全局创建了实例但编辑器的原则是重新创建实例就会把之前内容丢掉。我要做的一是切换语言二是之前内容不丢失。切换语言这一块一开始偶尔能切换成功偶尔不行应该是渲染时机或者竞态条件的问题。有可能拿到切换数据时组件已经渲染好了或者还没渲染反正很玄学根本不知道有没有被激活。一开始 AI 给我的方案是可能太卡没加载出来。因为实例创建在 App 根组件里切换语言会导致页面重绘。AI 让我把 i18n 实例移到 Content 里再做状态分发。我觉得很麻烦之前用的都是 props 传递改成 Context 很多地方都要改传递实例的地方要重新声明。后来想到原生钩子应该遵循 React Hook 规则要有依赖数组把 language 变量放进依赖数组里。如果当前 language 因切换改变就重新渲染问题就解决了。consteditoruseCreateBlockNote({dictionary:langzh?zh:en,uploadFile,},[lang],);还有默认设置默认打开是中文切换后要记住刷新后状态也保留这些都在 i18n 的 TS 配置里。importi18nfromi18next;import{initReactI18next}fromreact-i18next;importLanguageDetectorfromi18next-browser-languagedetector;importenfrom./en.json;importzhfrom./zh.json;constresources{en:{translation:en,},zh:{translation:zh,},};exportconsti18nPromisei18n.use(LanguageDetector).use(initReactI18next).init({resources,fallbackLng:zh,nonExplicitSupportedLngs:true,supportedLngs:[zh,en],detection:{order:[localStorage],caches:[localStorage],},interpolation:{escapeValue:false,// react already safes from xss},});exportdefaulti18n;二切换语言时编辑区内容不丢失使用 useRef 保存一份“当前的文档内容”。内存缓存Ref使用 useRef 保存一份“当前的文档内容”。无论 editor 怎么变Ref 里的内容都不会丢失。卸载前同步利用 useEffect 的清理函数cleanup function在 editor 卸载前强制从 editor.document 中提取最新内容并更新到 Ref 中。重建后填充当新的 editor 实例挂载时读取这个 Ref 里的内容而不是仅仅依赖 useEditorStorage 里的 data。// 1. 在 Editor 渲染层创建一个 Ref 存储最新内容constlatestContentRefuseRef(null);// 2. 利用 useEffect 监听 editor 变化在旧 editor 销毁前保存内容useEffect((){return(){// 销毁前的清理将 editor 里的内容同步到 ReflatestContentRef.currenteditor.document;};},[editor]);// 3. 在初始化新 editor 后优先使用 latestContentRef 的内容useEffect((){constcontentlatestContentRef.current||data?.content;if(editorcontent){editor.replaceBlocks(editor.document,content);}},[editor]);三白屏问题问题if (!ready) 条件内渲染完整 UIready 为 true 时无返回值导致初始化完成后白屏i18n.init() 是异步操作但组件渲染时直接读取 i18n.language此时 i18n 可能未初始化完成useState(i18n.language) 初始化时机过早LanguageDetector 浏览器语言检测也需要时间。修复方案// 等待 i18n 初始化完成显示 Loading 状态if(!ready){returndivLoading.../div;}// ready 为 true 时渲染完整应用return(ThemeBridgediv classNamefixed-viewportToolbar editor{editor}/Splitter style{{flex:1,height:calc(100% - 48px),overflow:hidden}}Splitter.Panel classNamesidebar-container sidebar-triggerstyle{{overflow:hidden}}collapsible{{start:true,end:true}}div classNamesidebar-hidden sidebar-scrollableSidebar editor{editor}//div/Splitter.PanelSplitter.Panel classNamemain-contentmin20%defaultSize80%style{{display:flex,flexDirection:column,overflow:hidden,}}div classNamemain-content-scrollablestyle{{padding:15px 20px,flex:1,overflow:auto}}Editor key{lang}editor{editor}onSave{save}noteId{docId}//divFootbar editor{editor}//Splitter.Panel/Splitter/div/ThemeBridge);额外优化解决 i18n 异步初始化问题const{i18n,ready}useTranslation();const[lang,setLang]useState(i18n.language);const[isI18nReady,setIsI18nReady]useState(i18n.isInitialized);useEffect((){// 等待 i18n 初始化完成if(i18n.isInitialized){setIsI18nReady(true);setLang(i18n.language);}else{constunsubscribei18n.on(initialized,(){setIsI18nReady(true);setLang(i18n.language);});return()unsubscribe();}},[i18n]);// 等待 ready 后再渲染if(!ready||!isI18nReady){returndivLoading.../div;}三、性能优化与打包除此之外还做了性能优化跟之前策略差不多也是把导入的包做成动态加载。不知道为什么 Web 端性能很快但移动端测试很慢说是 JS 包太大我按提示重新打包在 Vite 配置里重新配置反而更慢了越改越错越改越难用这一块之后我还要补一补性能优化。后面也花了很多时间做 Git 版本回退vercel回退版本再部署。小结这一部分很简单但是我却真的头大终于好了。不知道为什么 AI 给我一些错误答案也可能我自己没描述好乱七八糟的有点浪费时间。瞎忙活重复性大规模重构的要求扔给ai改代码改完也不知道会变成什么样上下文不够的时候拆东墙补西墙各种崩溃感觉很蠢效率很低。我学到的是要去看源码、看配置。之前依赖数组useCreateBlockNote源码里其实已经说过支持dependency 配置项可以填依赖要去看源码不能只看接口文档官方文档不会写那么细的需求。总之读源码很重要要看源码看原始数据流要理清当前代码在做什么比盲目翻文档重要太多
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2420035.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!