尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CKEditor 5 本地化(Localization)完全指南:翻译 UI、多语言配置与插件国际化

CKEditor 5 本地化(Localization)完全指南:翻译 UI、多语言配置与插件国际化 CKEditor 5 本地化Localization完全指南翻译 UI、多语言配置与插件国际化【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5本文以 docs/framework/deep-dive/localization.md 为骨架结合 ckeditor5 仓库中Locale、translation-service等核心实现系统讲解 CKEditor 5 的消息本地化机制如何在插件中编写可翻译的 UI、如何通过三种方式为编辑器补充或覆盖翻译、如何处理复数形式以及如何复用其他包的既有翻译。读完本文你将能够为自己的自定义插件接入完整的国际化能力并为编辑器装配任意语言的界面。引言CKEditor 5 的本地化体系CKEditor 5 的所有 WYSIWYG 编辑器功能都支持消息本地化message localization即任何功能feature的用户界面都可以根据用户偏好翻译成各种语言与地区变体。这套翻译系统对第三方插件完全开放支持第三方插件的本地化允许传入自定义翻译以修复缺失或错误的本地化能够生成确定性deterministic构建产物提供易用的 API 用于提供翻译与编写可本地化内容在本地化流程的每一步都支持复数形式plural forms。注意请务必使用较新的 CKEditor 5 开发工具包版本。低于 v60.0.0 的旧版本工具不支持本文档描述的功能。在仓库中本地化的核心代码位于packages/ckeditor5-utils包packages/ckeditor5-utils/src/locale.ts —Locale类与Translations类型定义packages/ckeditor5-utils/src/translation-service.ts —add()翻译注册函数与_translate()底层翻译逻辑、Message接口packages/ckeditor5-utils/src/language.ts — 语言方向LTR/RTL判定。术语表在开始之前先明确翻译流程中的关键术语Message消息需要被翻译的字符串或对象。字符串形式是{ id: message, string: message }对象形式的快捷写法。Message ID消息 ID用于区分消息的属性。对于可能发生冲突的短消息如%0 images尤为有用。Message string消息字符串消息的默认英文形式。当消息支持复数时它是默认的单数形式。Message plural消息复数形式消息可选的复数英文形式。该属性的存在表示消息应当同时支持单数和复数形式。Translation source.ts翻译源为某一种语言生成、包含词典dictionary的 TypeScript 模块。所有可本地化的 CKEditor 5 包都在lang/translations/目录中包含此类文件。Translation asset翻译资源包含某一种语言生成翻译的 JavaScript 文件或文件的一部分。仓库中的真实例子packages/ckeditor5-alignment/lang/translations/pl.ts 是ckeditor5-alignment包的波兰语翻译源其导出结构完全符合Translations类型而 packages/ckeditor5-alignment/lang/contexts.json 则记录了每条消息的语义上下文供翻译人员理解消息的用途。编写可本地化的 UIt()函数所有需要本地化的message都应传给 CKEditor 5 专门的t()函数Locale#t见 packages/ckeditor5-utils/src/locale.ts。在 JavaScript 文件中把它作为独立函数取出使用例如从编辑器的Locale实例获取const { t } editor.locale;或从任意视图方法中获取const t this.t;。在 TypeScript 文件中翻译工具还能基于类型信息识别直接的Locale#t()调用因此editor.t()、editor.locale.t()、locale.t()、this.t()等写法均被支持。t()函数接受两个参数第一个参数字符串字面量或包含id、string与可选plural属性的对象字面量。字符串字面量会同时充当message ID与message string第二个参数单个值或值数组用于填充更复杂翻译场景中的占位符。如果指定了plural属性数组中的第一个值将作为决定复数形式的关键量quantity。重要限制由于翻译流程依赖静态代码分析器static code analyzer支持的调用模式取决于源文件类型。在 JavaScript 文件中分析器只查找名为t()的函数因此它不能挂在Locale实例上调用也不能改名在 TypeScript 文件中分析器还会根据类型信息识别直接的Locale#t()调用。同理第一个参数只能是字符串字面量或对象字面量不能传入变量。简单场景字符串形式const emojiName cat; // 假设选择了英语 t( insert emoji ); // insert emoji t( insert %0 emoji, emojiName ); // insert cat emoji t( insert %0 emoji, [ emojiName ] ); // insert cat emoji从源码可以看到Locale#_t()内部会把字符串消息归一化为{ string: message }对象然后交给_translate()处理最后通过interpolateString()将%0、%1等占位符替换为传入值packages/ckeditor5-utils/src/locale.ts。占位符使用%index形式单个值只填充%0数组则按下标一一对应。高级场景对象形式与复数const quantity 3; // 假设选择了英语 t( { string: %0 emoji, id: ACTION_EMOJI }, insert ); // insert emoji t( { string: %0 emoji, plural: %0 emojis, id: N_EMOJIS }, quantity ); // 3 emojis t( { string: %1 %0 emoji, plural: %1 %0 emojis, id: ACTION_N_EMOJIS }, [ quantity, Insert ] ); // Insert 3 emojisid属性用于区分字符串相同但翻译应不同的消息例如英文同为editor的 in the editor 与 my editor。Message接口的完整定义见 packages/ckeditor5-utils/src/translation-service.ts。示例本地化插件 UI下面的例子展示如何为插件创建可本地化的用户界面——一个插入笑脸表情的按钮其悬浮提示tooltip可被翻译// 自定义插件配置包括必要的导入。 // 以下代码应放入继承自 Plugin 类的自定义插件类中。 // ... editor.ui.componentFactory.add( smilingFaceEmoji, locale { const buttonView new ButtonView( locale ); // 本地化的标签。 const label editor.locale.t( Insert smiling face emoji ); buttonView.set( { label, icon: emojiIcon, tooltip: true } ); buttonView.on( execute, () { editor.execute( insertSmilingFaceEmoji ); editor.editing.view.focus(); } ); } ); // 其余自定义插件配置。 // ...关于如何完整创建一个 CKEditor 5 插件可参考文档 docs/tutorials/crash-course/ 下的入门教程。示例本地化 pending actionsPending actions待处理操作用于告知用户某个操作正在进行中此时退出编辑器会丢失数据。其实现位于 packages/ckeditor5-core/src/pendingactions.ts。下面展示如何本地化这些提示消息class FileRepository { // 更多方法。 // ... updatePendingAction() { const pendingActions this.editor.plugins.get( PendingActions ); const t this.editor.t; const getMessage value t( Upload in progress (%0%)., value ); // Upload in progress (12%). this._pendingAction pendingActions.add( getMessage( this.uploadedPercent ) ); this._pendingAction.bind( message ).to( this, uploadedPercent, getMessage ); } }这里使用了bind( message ).to( ... )将待处理操作的消息与上传进度动态绑定每次进度变化都会用getMessage重新生成带百分比的本地化消息。为编辑器添加翻译三种方式首先如果你在某个 CKEditor 5 功能中发现缺失或错误的翻译可以参见 docs/framework/contributing/ 中的翻译贡献指南——CKEditor 5 是开源项目来自世界各地用户的翻译贡献都会被其他使用者感激。向编辑器添加翻译有三种方式可满足不同场景的需求通过translation-service的add()函数添加翻译——需要在创建编辑器实例之前完成且要求导入 CKEditor 5 的 utility 函数通过扩展全局window.CKEDITOR_TRANSLATIONS对象——同样需要在创建编辑器实例之前完成像其他 CKEditor 5 包那样在发布包的lang/translations/目录中创建 TypeScript 翻译源——适合第三方插件作者可在构建阶段只打包所需语言的翻译。方式一使用add()函数translation-service的add()辅助函数通过扩展全局window.CKEDITOR_TRANSLATIONS对象来添加翻译packages/ckeditor5-utils/src/translation-service.ts。由于需要导入它只能在构建编辑器之前使用。自 CKEditor 5 v19.0.0 起add()方法接受一个可选的第三个参数getPluralForm()函数。该函数仅在没有为某种语言加载语言文件时才需要用来定义复数形式。如果某个message需要支持单复数则其翻译应传一个翻译数组。add( pl, { Add space: [ Dodaj spację, Dodaj %0 spacje, Dodaj %0 spacji ] } ); // 假设选择了波兰语 t( { string: Add space, plural: Add %0 spaces }, 1 ) // Dodaj spację t( { string: Add space, plural: Add %0 spaces }, 2 ) // Dodaj 2 spacje t( { string: Add space, plural: Add %0 spaces }, 5 ) // Dodaj 5 spacji在add()的源码实现中每条翻译都会通过Object.assign()合并进对应语言的dictionary同时仅在首次注册该语言时写入getPluralForm已存在的语言不会被覆盖。从源码注释还可以看到getPluralForm()既支持返回布尔值如英语n n ! 1也支持返回数值下标如波兰语的复合规则。方式二扩展window.CKEDITOR_TRANSLATIONS对象第二种方式是通过全局window.CKEDITOR_TRANSLATIONS对象添加翻译。对于每种要支持的语言需要扩展该对象的dictionary属性并在缺失时提供getPluralForm()函数。dictionary属性一个message ID ⇒ translations映射。translations可以是字符串如果消息需要支持复数则是该语言下包含单数与各复数形式的翻译数组。getPluralForm()属性一个根据给定数量返回复数形式下标的函数。注意使用 CKEditor 5 翻译时该属性会由CKEditor 5 翻译资源translation assets自动定义。下面是一个window.CKEDITOR_TRANSLATIONS对象的部分示例包含波兰语的Cancel与Add space两个消息 ID{ // 每个键应是有效的语言代码。 pl: { // pl 语言的翻译映射。 dictionary: { Cancel: Anuluj, Add space: [ Dodaj spację, Dodaj %0 spacje, Dodaj %0 spacji ] }, // 返回给定语言复数形式下标的函数。 // 注意只有为一种新语言添加翻译时才需要传入该函数。 getPluralForm: n n 1 ? 0 : n % 10 2 n % 10 4 ( n % 100 10 || n % 100 20 ) ? 1 : 2 } // 其他语言。 // ... }必须扩展window.CKEDITOR_TRANSLATIONS对象中已存在的属性以免丢失其他翻译。这可以借助Object.assign()与||运算符轻松实现// 确保全局对象已定义若未定义则创建。 window.CKEDITOR_TRANSLATIONS window.CKEDITOR_TRANSLATIONS || {}; // 确保波兰语词典存在。 window.CKEDITOR_TRANSLATIONS[ pl ] window.CKEDITOR_TRANSLATIONS[ pl ] || {}; window.CKEDITOR_TRANSLATIONS[ pl ].dictionary window.CKEDITOR_TRANSLATIONS[ pl ].dictionary || {}; // 用你的翻译扩展波兰语词典 Object.assign( window.CKEDITOR_TRANSLATIONS[ pl ].dictionary, { Save: Zapisz } );如果你添加了一种全新语言请记得设置getPluralForm()函数——它应返回一个数字对于英语这类复数规则简单的语言也可以返回布尔值用于指示给定值应使用哪种形式。方式三创建翻译源文件推荐给插件作者第三种方式主要面向包含大量可本地化消息的插件在lang/translations/目录中为每种语言代码创建一个 TypeScript 文件。默认导出必须符合Translations类型定义见 packages/ckeditor5-utils/src/locale.ts// lang/translations/es.ts import type { Translations } from ckeditor/ckeditor5-utils; const translations: Translations { es: { dictionary: { // 文本对齐工具栏按钮的标签。 Align left: Alinear a la izquierda } } }; export default translations;如果你在 CKEditor 5 生态之外开发自己的插件可以使用 package generator构建工具会处理lang/目录包括翻译同步来创建翻译源与翻译资源。翻译源只包含词典。需要从ckeditor5包加载匹配语言的翻译含getPluralForm编辑器才能获得该语言的复数形式函数——因为包的翻译源中只有dictionary不携带复数规则。要构建并配置一个本地化的编辑器请遵循 docs/getting-started/setup/ui-language.md 中的步骤。该文档还给出了完整的实践示例通过 npm 导入ckeditor5/translations/pl.js并放入编辑器配置的translations数组或通过 CDN 加载translations/es.umd.js脚本CDN 方式无需手动传入配置并支持通过config.language.ui/config.language.content分别设置 UI 语言与内容语言例如英文 UI 阿拉伯文内容。仓库中各包实际生成的语言文件可参考 packages/ckeditor5-alignment/lang/translations/ 下的 74 个语言目录。复用其他包中的翻译如果你想复用另一个包中已存在的message应当通过不同名称的别名调用翻译函数而不是直接使用t()。这样可防止静态代码分析器把它当作一条新的源消息来处理。协作功能collaboration features与斜杠命令功能slash commands已经在使用这种方案。下面取自斜杠命令默认命令列表的例子展示了t()与translateVariableKey()的区别translateVariableKey( Block quote )会复用其他包中的翻译而t( Create a block quote )会被静态代码分析器处理为一条新消息。这样既保证了title的翻译来自块引用block quote功能中已有的 Block quote 消息又为description创建了一条新翻译。public getDefaultCommands() { const t this.editor.t; const translateVariableKey this.editor.locale.t; return [ { id: blockQuote, commandName: blockQuote, icon: IconQuote, title: translateVariableKey( Block quote ), description: t( Create a block quote ) }, // 更多命令定义 // ... ] }注意这里两种调用方式都来自同一个Locale实例this.editor.t与this.editor.locale.t指向同一底层方法区别只在于名字是否会让静态分析器误判为新消息。底层原理Locale与_translate()的协作从源码层面看一次完整的翻译流程是这样的编辑器初始化时创建Locale实例通过构造参数接收uiLanguage、contentLanguage与translations默认uiLanguage en并计算出uiLanguageDirection/contentLanguageDirectionpackages/ckeditor5-utils/src/locale.tsLocale#t是一个静态绑定到实例上下文的函数因此应始终以函数形式调用const t locale.t; t( Label )这也是文档强调从editor.locale取出后直接调用的原因packages/ckeditor5-utils/src/locale.ts_t()把values统一转为数组字符串消息规范化为对象若有plural则以第一个值为数量调用_translate()并最终完成占位符插值packages/ckeditor5-utils/src/locale.ts_translate()的查找优先级为配置传入的translations优先于全局window.CKEDITOR_TRANSLATIONS若全局对象中只有一种语言还会自动将该语言作为目标语言当某语言缺少翻译或词典不存在时会回退到消息的默认string数量为 1或默认plural数量不为 1packages/ckeditor5-utils/src/translation-service.ts。RTL 支持方面packages/ckeditor5-utils/src/language.ts 内置了一份 RTL 语言代码清单阿拉伯语、波斯语、希伯来语、库尔德语、维吾尔语、乌尔都语等Locale依据它自动确定 UI 与内容方向因此无需手动处理镜像布局。此外translation-service模块在加载时就会确保window.CKEDITOR_TRANSLATIONS全局对象存在packages/ckeditor5-utils/src/translation-service.ts翻译资产的 UMD 构建正是依赖这一点在运行期注册各语言翻译。仓库配套的单元测试 packages/ckeditor5-utils/tests/translation-service.js 覆盖了add()、_translate()、_clear()等函数的典型行为可作为理解与验证翻译行为的参考。已知限制目前无法在不销毁编辑器的情况下于运行时更改已选定的编辑器语言如需切换语言必须重新创建编辑器实例这通常意味着重新create()并传入新的translations配置。小结CKEditor 5 的本地化体系贯穿从消息编写t()函数、翻译收集静态分析器扫描字面量、翻译注册add()/window.CKEDITOR_TRANSLATIONS/lang/translations/*.ts到运行时查找_translate()字典 复数形式函数的完整链路。对插件作者而言推荐的实践是编写 UI 时坚持使用字面量形式的t()调用、需要复用时改用别名、为多语言消息提供plural属性并通过lang/translations/目录发布各语言翻译源——这样既能生成确定性构建也能让用户按需加载语言资源。【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表