存档

2025年4月 的存档

react-i18next 进阶用法

2025年4月14日 没有评论

在过去的 React 和 React Native 项目里,对于多语言处理,使用的是 react-i18next 来处理的。今天给大家分享下这个库的一些进阶用法和注意事项。

其实一开始的时候,我自己是有点困惑的,react-i18next 和 i18next 是什么关系?为什么项目要安装这2个库,而不是一个?而且为什么在代码里如何从 i18next 里导入某些变量会出现非预期的结果。所以这里也给小伙伴解释下,react-18next 是对 i18next 的 react 版本封装。换句话来说,一般情况下,大家直接使用 react-i18next 库就可以了。

安装很简单:

npm install react-i18next i18next --save

进阶一、兼容性问题

如果你的代码在运行时出现了下面的错误:
i18next::pluralResolver:
Your environment seems not to be Intl API compatible,
use an Intl.PluralRules polyfill.
Will fallback to the compatibilityJSON v3 format handling.
那么按照官方的说明(https://www.i18next.com/how-to/faq#why-are-my-plural-keys-not-working),表示当前环境不兼容 Intl API。

解决方法:

npm install intl-pluralrules --save

然后在多语言的初始化文件里导入:

import 'intl-pluralrules'

这取决于你的 项目结构,可能就在 index.js 里,我们的项目就是在 i18n.ts 文件里。

这个问题,在我们的 React Native 项目里有碰到过。

进阶二,传字段

例如有下面的 json 文件:

{
    "error": {
        "required": "{{field}}必填",
        "invalid": "无效的{{field}}"
    }
}

那么使用时,就是这样使用的:t(‘error.required’, { field: t(‘Email’) })

进阶三,自定义多语言层级分隔符

默认情况下,代码里使用 . 来表示层级,例如 error.required,当然也可以使用下面的配置来更改为使用 “/”:

i18n.use(initReactI18next).init({
  // ...
  keySeparator: '/'
})

那么前面的调用就成了 t(‘error/required’, { field: t(‘Email’) }),我是觉得这个一般情况下是不必无苦硬吃的,可能在特殊场合需要使用。大家看看就可以。

进阶四,自动单复数

i18next 对于自动单复数有预定的约定,只需要在 key 后面添加 “_other” 即可,当然前提是对应的语言描述有复数的用法哈。

{
"hours": "{{ count }} hour",
 "hours_other": "{{ count }} hours",
}

t('hours', { count: 5 }) 
// 5 hours

对于 0 来说,实际来看,会使用复数的翻译,例如 0 hours。如果需要为 0 来单独定义的话,可以添加 “_zero”,例如:

"hours_zero": "Now"

需要提醒的是,如果这里内置的 _zero、_other 和你的多语言冲突了,还是有这个可能的,那么可以修改使用自定义分隔符,例如下面这样:

// i18n.ts
i18n.use(initReactI18next).init({
  ...  // 其它配置   
  pluralSeparator: '__', // 双下划线
})

具体使用:

{
    "days": "{{count}} day",
    "days__other": "{{count}} days",
}

进阶五,Trans 组件

这里直接借用网上的一个示例,假如要显示下面的文字“你好 {userName},你有 {count} 条未读消息。点击查看。”,并且还需要支持点击操作。

在以前的写法中,我们需要将这句话单独的拆开,使用不同的组件包装起来,例如 count 希望是一个
红色的字体之类的,还要放到不同的多语言 key 里。太费劲了。

使用 Trans 组件,可以这么写,大家琢磨琢磨,是不是简单很多。

// en.json
{
    "userMessagesUnread": "Hello <1>{{userName}}</1>, you have {{count}} unread message. <5>Click to view</5>.",
    "userMessagesUnread_other": "Hello <1>{{userName}}</1>, you have {{count}} unread messages.  <5>Click to view</5>."
}

<Trans
    defaults={t('userMessagesUnread')}
    values={{ count, userName }}
    parent={Text}
    components={{
        1: <Text style={{ fontWeight: '500' }} />,
        5: <Text style={{ color: '#4682A9' }} onPress={() => {}} />
    }}
/>

可以看到,通过使用 components 属性,借助索引可以将指定位置的元素给替换包装起来。那么有人就问了,为什么这里是数字1和5,而不是其它的呢?

Trans 组件将上面的文本转换为下面的结构:

[
  'Hello ',
  { children: [{ user_name: 'Admin' }] },
  ', you have ',
  { count: 10 },
  ' unread messages. ',
  { children: ['Click to view'] },
  '.'
]

这么来看,是不是基于 0 的索引位置就出来啦。

更多内容,可以参考官方文档说明。

分类: 日常 标签: ,