konjac

可能是你见过最好用的 React 国际化方案

Usage no npm install needed!

<script type="module">
  import konjac from 'https://cdn.skypack.dev/konjac';
</script>

README

Konjac

可能是你见过最好用的 React 国际化方案

翻译魔芋

Konjac 取自《哆啦A梦》中的"翻译魔芋"

  • 🖇 内联语料,减少上下文跳转
  • 🛠 完善的 jsx 支持
  • 🔪 完全自动的 Code Splitting
  • 🔌 极致的轻量,强大的可拓展性

使用方法

安装:

$ yarn add konjac

在应用外层包裹一个 KonjacProvider 组件,并进行一些基础配置:

import {KonjacProvider} from 'konjac'

function App() {
  const [locale, setLocale] = useState('zh-CN')
  return (
    <KonjacProvider locale={locale} allLocales={['en-US', 'zh-CN']}>
      {/* ... */}
    </KonjacProvider>
  )
}

在组件中,使用 useTranslation 进行翻译映射:

import {useTranslation} from 'konjac'

function Hello() {
  const {t} = useTranslation()
  return (
    <div>
      {t(['Hello!', '你好!'])}
    </div>
  )
}

t 函数的参数 messages 是一个数组,每个元素和 KonjacProvider 配置中的 allLocales 按照顺序一一对应。虽然多数情况下我们会直接使用字符串,但 Konjac 的能力远不止于此,我们还可以使用 ReactNode,这使得 t 函数几乎可以应对任何场景:

const {t} = useTranslation()
const user = {
  name: 'John'
}
const content = t([
  `Hello, ${user.name}!`,
  <span>你好,<i>{user.name}</i>!</span>
])

凭啥要用 Konjac?

因为,内联语料。

国际化已经渐渐成为一个大型工程的标配了,而 React 应用相关的国际化方案也层出不穷,但在传统方案中,语料往往是作为一套独立的 json 对象而存在的。我们需要给每一条语料定义一个 key,然后在组件中使用这个 key 索引到对应版本的语料:

// en-US.json
{
  "dashboard.header.title": "Hello"
}
// zh-CN.json
{
  "dashboard.header.title": "你好"
}
const title = t('dashboard.header.title')

似乎没什么异样?然而,随着项目的发展,我们很可能会遇到非常多的问题:

  1. key 是以纯字符串的方式存在的,对类型检查非常不友好,如果不小心写错,需要在运行时才能发现
  2. key 出现在多个文件(业务组件 + 多个语言版本各自的文件),导致开发中需要频繁的上下文跳转
  3. 难以维护,当业务逻辑发生变化时,key 往往也需要跟着调整,然而,由于 key 只是字符串,批量重命名、批量移动、清理不再被引用的语料等等这些操作都十分繁琐
  4. 所有的语料都塞在一个大 json 中,当应用启动时,需要一次性的加载全部语料,存在性能隐患

所以 Konjac 采用了"内联语料优先"的策略,我们不再需要为每一条语料绞尽脑汁的想一个 key 了,一切都是内联在组件中的,因为我们相信,翻译语料,也是组件逻辑本身的一部分。

内联语料可以自然而完美地解决上述问题,其中最为显著的,就是减少上下文切换和对代码切分的天然支持。

当然,内联语料也并非百利而无一害,例如,内联语料可能会降低复用性,不过,在 Konjac 中,我们其实仍然可以使用集中化的模式:

const locale = {
  dashboard: {
    title: ['Hello', '你好'],
    description: ['...', '...'],
  },
}

const title = t(locale.dashboard.title)

比起使用 json,使用 js 对象来定义的好处显而易见:多层级嵌套、自动补全、类型检查、代码跳转、反向引用查询、灵活的拆分和组装……

事实上,Konjac 是一个非常简单而轻量的库,也并未明确规定使用者要如何组织代码,因为国际化本身就应该十分自然、灵活。