ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

vue-i18n 复数化(Pluralization)完全指南:从 `$tc()` 基础用法到自定义复数规则

vue-i18n 复数化(Pluralization)完全指南:从 `$tc()` 基础用法到自定义复数规则 前端国际化【免费下载链接】vue-i18n:globe_with_meridians: Internationalization plugin for Vue.js项目地址https://gitcode.com/gh_mirrors/vu/vue-i18n点击查看免费下载导读本文聚焦 vue-i18n 的复数化Pluralization能力系统讲解基于管道符|分隔的复数消息定义、$tc()与tc()的调用方式、预定义参数{count}/{n}的内置传递机制以及从 v8.0 起提供的pluralizationRules自定义复数规则方案以俄语、乌克兰语等斯拉夫语言为例。读完本文你将能在一套 locale 消息中为同一名词定义单数 / 复数 / 零数量等多种形态并能为任意语言编写符合其语法习惯的复数选择函数同时理解 vue-i18n 底层getChoiceIndex与fetchChoice的完整决策链路。复数化的基本原理管道符分隔的消息复数化允许你根据传入的数量为同一条消息选择不同的翻译形态。前提是在 locale 消息中使用管道符|作为分隔符用管道符把该消息的各个复数形态依次写出来。以 vuepress/guide/pluralization.md 中的英文示例为例const messages { en: { car: car | cars, apple: no apples | one apple | {count} apples } }模板中必须使用$tc()而不是$t()第一个参数是消息的 key第二个参数是数量p{{ $tc(car, 1) }}/p p{{ $tc(car, 2) }}/p p{{ $tc(apple, 0) }}/p p{{ $tc(apple, 1) }}/p p{{ $tc(apple, 10, { count: 10 }) }}/p渲染输出pcar/p pcars/p pno apples/p pone apple/p p10 apples/p这里的关键点有两个消息形态与数量一一对应apple的三种形态no apples | one apple | {count} apples分别对应数量0、1、10。{count}占位符会自动被当前数量替换即使不显式传入{ count: 10 }第二个参数10也会被自动注入详见下文预定义参数一节。注意$tc是挂在 Vue 原型上的实例方法其实现位于 src/extend.js最终委托给VueI18n实例的_tc方法在组件外部或非 Vue 环境中可以使用等价的方法i18n.tc(key, choice, ...values)见 src/index.js。调用链与底层实现$tc是如何选择复数形态的为了深入理解上面的行为有必要看一下_tc与fetchChoice的实现。在 src/index.js 中_tc先对参数做了预处理_tc (key, _locale, messages, host, choice?, ...values) { if (!key) { return } if (choice undefined) { choice 1 } const predefined { count: choice, n: choice } const parsedArgs parseArgs(...values) parsedArgs.params Object.assign(predefined, parsedArgs.params) values parsedArgs.locale null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params] return this.fetchChoice(this._t(key, _locale, messages, host, ...values), choice) }这段源码揭示了三个实现细节若未显式传入choice默认值取1{count}与{n}两个预定义参数在此处被注入且用户显式传入的同名参数会覆盖预定义值Object.assign(predefined, parsedArgs.params)翻译完成后的消息字符串交给fetchChoice完成复数形态的最终选择。fetchChoice的实现位于 src/index.jsfetchChoice (message, choice) { if (!message || !isString(message)) { return null } const choices message.split(|) choice this.getChoiceIndex(choice, choices.length) if (!choices[choice]) { return message } return choices[choice].trim() }可见选择过程分三步按|拆分消息得到候选形态数组 → 调用getChoiceIndex(choice, choices.length)得到最终索引 → 返回对应形态并trim()去除首尾空格。如果索引越界如消息未按预期定义足够多的形态则直接返回整条原始消息。通过预定义参数访问数量{count}与{n}使用复数化时你不必在$tc()的第三个参数中显式传入数量——复数化所需的数字已经通过预定义命名参数{count}和{n}暴露给 locale 消息内部使用且必要时可以被覆盖。以下面的消息为例const messages { en: { apple: no apples | one apple | {count} apples, banana: no bananas | {n} banana | {n} bananas } }模板p{{ $tc(apple, 10, { count: 10 }) }}/p p{{ $tc(apple, 10) }}/p p{{ $tc(banana, 1, { n: 1 }) }}/p p{{ $tc(banana, 1) }}/p p{{ $tc(banana, 100, { n: too many }) }}/p输出p10 apples/p p10 apples/p p1 banana/p p1 banana/p ptoo many bananas/p值得注意的细节{count}与{n}等效两者都会被自动注入为第二个参数choice的值因此$tc(apple, 10)与$tc(apple, 10, { count: 10 })输出完全一致显式传参可以覆盖预定义值$tc(banana, 100, { n: too many })中虽然 choice 是100用于选择复数形态但消息插值用的是n参数的值too many因此输出too many bananas。这一选择用数字、插值用文本的能力常被用于商品库存、限额提示等场景该注入逻辑同样见 src/index.jsconst predefined { count: choice, n: choice }。自定义复数化pluralizationRules为什么需要自定义规则上面展示的0 对应第一个形态、1 对应第二个形态、其余对应第三个形态是英语风格的复数规则它并不适用于所有语言。斯拉夫语言如俄语、乌克兰语等的复数形态划分更为复杂因此需要为这些语言单独定义复数选择逻辑。配置方式从v8.0起可以在new VueI18n()的构造函数选项中传入可选的pluralizationRules对象。该对象以语言名为 key以选择函数为 valuenew VueI18n({ // Key - 使用该规则的语言例如 ru // Value - 用于选择正确复数形态的函数 pluralizationRules: { /** * param choice {number} 由 $tc 传入的选择索引$tc(path.to.rule, choiceIndex) * param choicesLength {number} 可用形态的总数量 * returns 最终用于选取复数词形的索引 */ ru: function(choice, choicesLength) { // 此处的 this VueI18n 实例因此也可以访问 this.locale 等属性 if (choice 0) { return 0; } const teen choice 10 choice 20; const endsWithOne choice % 10 1; if (choicesLength 4) { return (!teen endsWithOne) ? 1 : 2; } if (!teen endsWithOne) { return 1; } if (!teen choice % 10 2 choice % 10 4) { return 2; } return (choicesLength 4) ? 2 : 3; } } })配合下面的俄语消息就能得到符合俄语语法习惯的四种形态const messages { ru: { car: 0 машин | {n} машина | {n} машины | {n} машин, banana: нет бананов | {n} банан | {n} банана | {n} бананов } }其格式含义为0 个事物 | 数量以 1 结尾 | 数量以 2-4 结尾 | 数量以 5-9、0 结尾以及 11-19 的十位数。规则函数的调用契约理解上述函数的关键在于其调用契约对应 src/index.js 中getChoiceIndex的实现函数签名固定为(choice, choicesLength)choice是模板/代码中传给$tc()的第二个参数数量choicesLength是消息用|拆分后的形态数量函数内部this指向VueI18n 实例因此可以访问this.locale等实例属性函数返回值将作为最终索引从拆分后的形态数组中取出对应词形。在 src/index.js 中可以看到规则的命中逻辑当this.locale存在于pluralizationRules中时调用该语言的规则函数否则回退到默认的英语规则if (this.locale in this.pluralizationRules) { return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength]) } else { return defaultImpl(choice, choicesLength) }实际效果验证使用上述规则后模板仍然必须使用$tc()而非$t()p{{ $tc(car, 1) }}/p p{{ $tc(car, 2) }}/p p{{ $tc(car, 4) }}/p p{{ $tc(car, 12) }}/p p{{ $tc(car, 21) }}/p p{{ $tc(banana, 0) }}/p p{{ $tc(banana, 4) }}/p p{{ $tc(banana, 11) }}/p p{{ $tc(banana, 31) }}/p输出结果为p1 машина/p p2 машины/p p4 машины/p p12 машин/p p21 машина/p pнет бананов/p p4 банана/p p11 бананов/p p31 банан/p这套斯拉夫复数规则与预期行为完全一致仓库的单元测试中也给出了完全相同的验证在 test/unit/issues.test.js对应 issue #468中slavicPluralization函数与car: 0 машин | {n} машина | {n} машины | {n} машин消息组合后断言了vm.$tc(car, 0) 0 машин、vm.$tc(car, 1) 1 машина、vm.$tc(car, 2) 2 машины、vm.$tc(car, 12) 12 машин、vm.$tc(car, 21) 21 машина等全部形态的输出。向后兼容的另一种姿势覆写getChoiceIndex对于无法修改VueI18n构造函数选项的场景仓库还提供了另一种兼容方案直接覆写VueI18n.prototype.getChoiceIndex。这在 test/unit/issues.test.js对应 issue #451中演示const defaultImpl VueI18n.prototype.getChoiceIndex VueI18n.prototype.getChoiceIndex function (choice, choicesLength) { if (this.locale ! ru) { return defaultImpl.apply(this, arguments) } // ... 俄语复数规则实现 } // 使用结束后恢复默认实现 VueI18n.prototype.getChoiceIndex defaultImpl注意 src/index.js 中getChoiceIndex的实现逻辑如果实例原型上存在getChoiceIndex会优先调用原型版本保证实例上通过pluralizationRules配置的规则与原型覆写共存时行为一致。这保证了两种自定义方式不会互相冲突。默认复数化规则英语规则如果当前 locale 未出现在pluralizationRules映射中将使用英语语言的默认规则。从源码 src/index.js 可以看到该默认实现const defaultImpl (_choice, _choicesLength) { _choice Math.abs(_choice) if (_choicesLength 2) { return _choice ? _choice 1 ? 1 : 0 : 1 } return _choice ? Math.min(_choice, 2) : 0 }它的行为可以概括为两种形态choicesLength 2数量0选择索引1即第二种形态数量1选择索引0大于1也选择索引1三种及以上形态数量0选择索引0第一种形态其他数量取Math.min(choice, 2)即超出 2 的数量一律选择索引2。这也解释了开篇apple: no apples | one apple | {count} apples的行为0 → no apples、1 → one apple、2 及以上 → {count} apples。注意默认实现先对数量取了绝对值Math.abs因此负数也会按绝对值参与形态选择。与其他 i18n 特性的组合要点复数化并非孤立功能在实际项目中常与其他能力叠加使用这里补充几个由源码确认的组合要点命名参数与列表参数均可插值$tc(Hello {0}, 1, [kazupon])返回Hello kazupon$tc(plurals.format.named, 1, { name: kazupon })同样可用。这类组合行为在 test/unit/basic.test.js 中有完整断言。locale 参数$tc支持传入目标语言例如$tc(plurals.car, 1, ja)会直接按ja语言进行复数化与翻译格式参数与 locale 参数也可同时使用如$tc(plurals.format.list, 1, ja, [kazupon])见 test/unit/basic.test.js。回退行为当 key 在指定 locale 中缺失时$tc会沿 fallback locale 链查找例如$tc(plurals.fallback, 2, ja)会回退并返回英文/默认 locale 的翻译见 test/unit/basic.test.js消息整体走_t的翻译与回退链路复数形态的选择则发生在fetchChoice阶段。i18n组件与v-t指令$tc是实例方法与组件内插值i18n组件 / 指令属于不同入口若组件内使用复数化应通过$tc或在模板表达式中调用等价方法这也是文档中反复强调必须用$tc()而不是$t()的原因。小结与推荐实践用|在消息中定义复数形态模板中使用$tc(key, choice)/ 实例方法tc(key, choice)消息内部可直接使用{count}或{n}引用当前数量需要时也可显式传入覆盖值英语之外的语言通过构造函数选项pluralizationRules为具体语言注册选择函数函数签名固定为(choice, choicesLength)this指向 VueI18n 实例未注册规则的语言自动回退到英语默认规则该规则与 src/index.js 中的defaultImpl一致可阅读 test/unit/issues.test.js 与 test/unit/basic.test.js 了解完整的边界行为包括 0 数量、负数、超出形态数量、locale 参数与回退组合等场景。赞分享前端国际化【免费下载链接】vue-i18n:globe_with_meridians: Internationalization plugin for Vue.js项目地址https://gitcode.com/gh_mirrors/vu/vue-i18n点击查看免费下载相关推荐Markdownlint配置完全指南从基础到自定义规则Markdownlint配置完全指南从基础到自定义规则 什么是Markdownlint配置 Markdownlint是一个强大的Markdown文件校验工具开发工具WiFi Card国际化复数规则自定义复数逻辑的完整实现指南WiFi Card国际化复数规则自定义复数逻辑的完整实现指南 WiFi Card是一个实用的开源工具能够生成包含WiFi登录信息的二维码卡片。在实现多语言支前端终极TFLint配置完全指南从基础设置到高级自定义规则的完整教程终极TFLint配置完全指南从基础设置到高级自定义规则的完整教程 TFLint是一个强大的可插拔Terraform代码检查工具能够帮助开发者发现TerrafLint代码质量静态分析IaC上一篇Chatbot-UI多模型支持机制下一篇Tiptap协作编辑功能探索创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表