切换文档网站到dumi

This commit is contained in:
wxzhang 2022-08-07 11:16:10 +08:00
parent 0f7158f5de
commit a3b42aa7c3
42 changed files with 2508 additions and 0 deletions

100
.umirc.ts Normal file
View File

@ -0,0 +1,100 @@
import { defineConfig } from 'dumi';
// more config: https://d.umijs.org/config
export default defineConfig({
title: 'VoerkaI18n',
base:"voerkao18n",
mode: 'site',
logo: "/images/i18n.png",
outputPath:"docs/dist",
resolve:{
includes:["docs/src"]
},
locales: [['zh-CN', '中文']],
navs:[
{
title:"指南",
path: "/guide"
},
{
title:"参考",
path: "/reference"
},
{
title:"贡献源码",
path: "/contribute"
},
{
title:"源代码",
path: "https://gitee.com/zhangfisher/voerka-i18n"
}
],
menus: {
'/guide': [
{
title: '开始',
children: [
"/guide/intro/readme.md",
"/guide/intro/install.md",
"/guide/intro/get-started.md",
"/guide/intro/versions.md",
"/guide/intro/support.md",
"/guide/intro/question.md"
],
},
{
title: '指南',
children: [
"/guide/use/t.md",
"/guide/use/interpolation.md",
"/guide/use/datetime.md",
"/guide/use/plural.md",
"/guide/use/currency.md",
"/guide/use/namespace.md",
"/guide/use/change-langeuage.md",
"/guide/use/vue.md",
"/guide/use/react.md"
],
},
{
title: '高级特性',
children: [
"/guide/advanced/runtime.md",
"/guide/advanced/textMap.md",
"/guide/advanced/multi-libs.md",
"/guide/advanced/autoimport.md",
"/guide/advanced/customformatter.md",
"/guide/advanced/langpack.md",
"/guide/advanced/autotranslate.md",
"/guide/advanced/framework.md",
"/guide/advanced/remoteLoad.md",
"/guide/advanced/lngpatch.md",
"/guide/advanced/langedit.md"
],
},
{
title: '工具',
children: [
"/guide/tools/cli.md",
"/guide/tools/babel.md",
"/guide/tools/vite.md",
"/guide/tools/vue.md"
],
},
],
'/reference': [
{
title: '参考',
children:[
"/reference/voerkaI18n.md",
"/reference/i18nscope.md",
"/reference/formatters.md",
"/reference/lang-code.md",
]
}
]
}
});

View File

@ -0,0 +1,73 @@
---
sidebar: heading
---
# 源码贡献
`voerkai18n`是开源项目,欢迎大家贡献源码。
## 获取源码
`voerkai18n`在Github和[Gitee](https://gitee.com/zhangfisher/voerka-i18n)上面开源。
### 拉取源码
```shell
git clone https://gitee.com/zhangfisher/voerka-i18n
```
### 安装依赖
`voerkai18n`是一个`monorepo`多包工程,使用`pnpm`作为包管理器。所以首先需要安装`pnpm`
```javascript | pure
> npm install -g pnpm
```
然后再使用`pnpm install`
## 源码结构
```javascript | pure
voerkai18n
packages
autopublish // 自动发布工具,仅用于开发阶段
babel // @voerkai18n/babel插件
cli // @voerkai18n/cli命令行工具
formatters // @voerkai18n/formatters通用的格式化器
react // @voerkai18n/react
runtime // @voerkai18n/runtime
utils // @voerkai18n/utils工具库
vite // @voerkai18n/vite插件
vue // @voerkai18n/vue插件
apps // 测试应用
test // 单元测试用例
docs // 文档网站Vuepress
readme.md
```
## 工作原理
![](/images/arch.png)
## 开发格式化器
## 单元测试
## 文档
## 发布

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,25 @@
# 自动导入翻译函数
使用`voerkai18 compile`后,要进行翻译时需要从`./languages`导入`t`翻译函数。
```javascript | pure
import { t } from "./languages"
```
由于默认情况下,`voerkai18 compile`命令会在当前工程的`/languages`文件夹下,这样我们为了导入`t`翻译函数不得不使用各种相对引用,这即容易出错,又不美观,如下:
```javascript | pure
import { t } from "./languages"
import { t } from "../languages"
import { t } from "../../languages"
import { t } from "../../../languages"
```
作为国际化解决方案,一般工程的大部份源码中均会使用到翻译函数,这种使用体验比较差。
为此,我们提供了一个几个插件可以来自动完成翻译函数的自动引入,包括:
- `babel`插件
- `vite`插件
关于插件如何使用请参阅文档。

View File

@ -0,0 +1,9 @@
# 自动翻译
传统的国际化解决方案均是需要手工进行翻译的,`voerkai18n`解决方案支持调用在线翻译服务进行自动翻译。
- 内置的`voerkai18n translate`命令能调用在线翻译服务完成对提取的文本的自动翻译。
- 目前支持访问百度在线API进行自动翻译。百度提供了免费的在线API虽然只支持`QPS=1`,即每秒调用一次。但是`voerkai18n translate`命令会对要翻译的文本进行合并后再调用,因此大部分情况下,均足够使用了。
`voerkai18n translate`命令的使用请参阅扩展文档。

View File

@ -0,0 +1,197 @@
# 自定义格式化器
当我们使用`voerkai18n compile`编译后,会生成`languages/formatters.js`文件,可以在该文件中自定义您自己的格式化器。
`formatters.js`文件内容如下:
```javascript | pure
module.exports = {
// 在所有语言下生效的格式化器
"*":{
//[格式化名称]:(value)=>{...},
//[格式化名称]:(value,arg)=>{...},
},
// 在所有语言下只作用于特定数据类型的格式化器
$types:{
// [数据类型名称]:(value)=>{...},
// [数据类型名称]:(value)=>{...},
},
zh:{
$types:{
// 所有类型的默认格式化器
"*":{
},
Date:{},
Number:{},
Boolean:{ },
String:{},
Array:{
},
Object:{
}
},
[格式化名称]:(value)=>{.....},
//.....
},
en:{
$types:{
// [数据类型名称]:(value)=>{...},
},
[格式化名称]:(value)=>{.....},
//.....更多的格式化器.....
}
}
```
## 格式化器函数
**每一个格式化器就是一个普通的同步函数**,不支持异步函数,格式化器函数可以支持无参数或有参数。
- 无参数的格式化器:`(value)=>{....返回格式化的结果...}`
- 带参数的格式化器:`(value,arg1,...)=>{....返回格式化的结果...}`,其中`value`是上一个格式化器的输出结果。
## 类型格式化器
可以为每一种数据类型指定一个默认的格式化器,支持对`String``Date``Error``Object``Array``Boolean``Number`等数据类型的格式化。
当插值变量传入时,如果有定义了对应的的类型格式化器,会默认调用该格式化器对数据进行转换。
比如我们定义对`Boolean`类型格式化器,
```javascript | pure
//formatters.js
module.exports = {
// 在所有语言下只作用于特定数据类型的格式化器
$types:{
Boolean:(value)=> value ? "ON" : "OFF"
}
}
t("灯状态:{status}",true) // === 灯状态ON
t("灯状态:{status}",false) // === 灯状态OFF
```
在上例中,如果我们想在不同的语言环境下,翻译为不同的显示文本,则可以为不同的语言指定类型格式化器
```javascript | pure
//formatters.js
module.exports = {
zh:{
$types:{
Boolean:(value)=> value ? "开" : "关"
}
},
en:{
$types:{
Boolean:(value)=> value ? "ON" : "OFF"
}
}
}
// 当切换到中文时
t("灯状态:{status}",true) // === 灯状态:开
t("灯状态:{status}",false) // === 灯状态:关
// 当切换到英文时
t("灯状态:{status}",true) // === 灯状态ON
t("灯状态:{status}",false) // === 灯状态OFF
```
**说明:**
- 完整的类型格式化器定义形式
```javascript | pure
module.exports = {
"*":{
$types:{...}
},
zh:{
$types:{...}
},
en:{
$types:{....}
}
}
```
在匹配应用格式化时会先在当前语言的`$types`中查找匹配的格式化器,如果找不到再上`*.$types`中查找。
- `*.$types`代表当所有语言中均没有定义时才匹配的类型格式化。
- 类型格式化器是**默认执行的,不需要指定名称**。
- 当前作用域的格式化器优先于全局的格式化器。
## 通用的格式化器
类型格式化器只针对特定数据类型,并且会默认调用。而通用的格式化器需要使用`|`管道符进行显式调用。
同样的,通用的格式化器定义在`languages/formatters.js`中。
```javascript | pure
module.exports = {
"*":{
$types:{...},
[格式化名称]:(value)=>{.....},
},
zh:{
$types:{...},
[格式化名称]:(value)=>{.....},
},
en:{
$types:{....},
[格式化名称]:(value)=>{.....},
[格式化名称]:(value,arg)=>{.....},
}
}
```
每一个格式化器均需要指定一个名称,在进行插值替换时会优先依据当前语言来匹配查找格式化器,如果找不到,再到键名为`*`中查找。
```javascript | pure
module.exports = {
"*":{
uppercase:(value)=>value
},
zh:{
uppercase:(value)=>["一","二","三","四","五","六","七","八","九","十"][value-1]
},
en:{
uppercase:(value)=>["One","Two","Three","Four","Five","Six","seven","eight","nine","ten"][value-1]
},
jp:{
}
}
// 当切换到中文时
t("{value | uppercase}",1) // == 一
t("{value | uppercase}",2) // == 二
t("{value | uppercase}",3) // == 三
// 当切换到英文时
t("{value | uppercase}",1) // == One
t("{value | uppercase}",2) // == Two
t("{value | uppercase}",3) // == Three
// 当切换到日文时由于在该语言下没有定义uppercase格式式因此到*中查找
t("{value | uppercase}",1) // == 1
t("{value | uppercase}",2) // == 2
t("{value | uppercase}",3) // == 3
```
## 作用域格式化器
定义在`languages/formatters.js`里面的格式化器仅在当前工程生效,也就是仅在当前作用域生效。一般由应用开发者自行扩展。
## 全局格式化器
定义在`@voerkai18n/runtime`里面的格式化器则全局有效,在所有场合均可以使用,但是其优先级低于作用域内的同名格式化器。
目前内置的全局格式化器请参阅API参考
## 扩展格式化器
除了可以在当前项目`languages/formatters.js`自定义格式化器和`@voerkai18n/runtime`里面的全局格式化器外,单列了`@voerkai18n/formatters`项目用来包含了更多的格式化器。
作为开源项目,欢迎大家提交贡献更多的格式化器。

View File

@ -0,0 +1,68 @@
# 框架集成
`voerkai18n`可用应用于绝大多数框架,包括但不限于`Vue``React``Angular``Svelte`等等。要为某个框架或库集成VoerkaI18n的功能一般需要完成以下几件事
## 执行`t`翻译函数
翻译本质上是非常简单的查表功能,原则上只需要将所有需要翻译的字符串使用`t`函数包装起来即可。因此,只需要具备执行`t`函数的条件即可。而这是比较容易做到的,没有任何难度。
一般只需要`import { t } from "./languages"`即可直接使用`t`函数,不需要任何配置。`./languages`里面的文件本身就是您项目源码的一部分。
至此,您的应用就具备翻译功能了。
## 自动导入`t`翻译函数
由于需要在所以需要使用`t`翻译函数的文件均需要导入,比较麻烦。为了简化导入工作,`voerkai18n`提供了`babel``vite``webpack`插件,配置好以后,可以扫描发现在源码中使用了`t`函数就自动进行导入。这几个插件基本上涵盖了大部份Javascrip工程场景应用这些插件后`t`函数就相当于是一个全局函数,可以在项目中直接使用而不必进行导入。
自动导入`t`翻译函数仅是改善编程体验,并不是必须的。
## 动态切换语言
动态切换语言指的是用户在界面上选择其他语言,整个界面自动更新为新的语言,即重新渲染。基本步骤如下:
- **切换语言**
```javascript | pure
import { i18nScope } from "./languages"
await i18nScope.change("<新语言>")
// 或者调用全局
VoerkaI18n.change("<新语言>")
```
- **响应语言切换事件**
```javascript | pure
import { i18nScope } from "./languages"
i18nScope.on((newLanguage)=>{...})
// 或者调用全局
VoerkaI18n.on((newLanguage)=>{...})
```
- **重新渲染界面**
应用程序可以在侦听到语言切换事件后对整个应用程序进行重新渲染。而这种全局重新渲染各个框架的实现就有所差别但是总体上并不难。比如在Vue中可以这样
```javascript | pure
import { i18nScope } from "./languages"
i18nScope.on((newLanguage)=>{
app._instance.update() // 强制重新渲染
activeLanguage.value = newLanguage
})
```
## 小结
了解了上述基本原理,为`Vue``React``Angular``Svelte``uniapp``ReactNative`等应用程序集成`voerkai18n`就非常容易。

View File

@ -0,0 +1,16 @@
# 在线编辑语言包
利用**动态加载语言包**的机制,开发者可以非常容易就开发出`让用户自行编辑界面语言`的功能。
请详细阅读[`远程加载语言包`](./remote-load)的实现过程,其基本思路如下:
- 后端采用数据库来保存语言包,每一个语言包可以用一个表或多个表存储。
- 编写对应的编辑语言包的Web API,实现通过API修改语言包功能
- 前端代码重写语言包加载器函数将原来的读取静态语言包的方式修改为采用API读取
- 前端增加编辑语言包的界面

View File

@ -0,0 +1,22 @@
# 语言包
当使用`webpack``rollup``esbuild`进行项目打包时,默认语言包采用静态加载,会被打包进行源码中,而其他语言则采用异步打包方式。在`languages/index.js`中。
```javascript | pure
const defaultMessages = require("./zh.js")
const activeMessages = defaultMessages
// 语言作用域
const scope = new i18nScope({
default: defaultMessages, // 默认语言包
messages : activeMessages, // 当前语言包
....
// 以下为每一种语言生成一个异步打包语句
loaders:{
"en" : ()=>import("./en.js")
"de" : ()=>import("./de.js")
"jp" : ()=>import("./jp.js")
})
```
利用异步打包机制,从而避免将多个语言静态打包到源码包。

View File

@ -0,0 +1,8 @@
# 语言包补丁
在实际应用中,我们经常会在应用上线后,发现应用中的语言翻译错误,此时就可以利用`voerkai18n`的语言包补丁特性来解决此问题。
利用`voerkai18n`的语言包补丁特性,您就可以随时修复翻译错误,而不需要重新打包应用。
**基本思路是,应用上线后发现翻译错误时,可以在服务器上约定位置放置语言包补丁,应用会自动进行更新修复,很实用的一个特性。**
使用方法详见`动态加载语言包`介绍。

View File

@ -0,0 +1,16 @@
# 多库联动
`voerkai18n `支持多个库国际化的联动和协作,即**当主程序切换语言时,所有引用依赖库也会跟随主程序进行语言切换**,整个切换过程对所有库开发都是透明的。
库开发者不需要特殊配置,只需要像普通应用一样进行开发即可。
整体原理框架如下:
![结构图](./arch.png)
当我们在开发一个应用或者库并`import "./languages"`时,在`langauges/index.js`进行了如下处理:
- 创建一个`i18nScope`作用域实例
- 检测当前应用环境下是否具有全局单例`VoerkaI18n`
- 如果存在`VoerkaI18n`全局单例,则会将当前`i18nScope`实例注册到`VoerkaI18n.scopes`
- 如果不存在`VoerkaI18n`全局单例,则使用当前`i18nScope`实例的参数来创建一个`VoerkaI18n`全局单例。
- 在每个应用与库中均可以使用`import { t } from ".langauges`导入本工程的`t`翻译函数,该`t`翻译函数被绑定当前`i18nScope`作用域实例,因此翻译时就只会使用到本工程的文本。这样就割离了不同工程和库之间的翻译。
- 由于所有引用的`i18nScope`均注册到了全局单例`VoerkaI18n`,当切换语言时,`VoerkaI18n`会刷新切换所有注册的`i18nScope`,这样就实现了各个`i18nScope`即独立,又可以联动语言切换。

View File

@ -0,0 +1,246 @@
# 远程加载语言包
## 前言
`voerkai18n`默认将要翻译的文本内容经编译后保存在当`languages`文件夹下,当打包应用时会与工程一起进行打包进工程源码中。这会带来以下问题:
- 翻译语言包是源码工程的一部分,当要翻译的语种较多时,会增加源码包大小。
- 如果产品上线后发现翻译问题,则需要重新进行整个工程的打包
- 上线后要增加一种语言,同样需要再次进行走一次打包流程
`voerkai18n`针对这些问题,支持了远程加载语言包的功能,可以支持线上`动态增加支持的语言``在线翻译补丁`等特性。
## 使用方法
### 准备
为说明如何从远程加载语言包,我们将假设以下的应用:
应用`chat`,依赖于`user``manager``log`等三个库,均使用了`voerkiai18n`作为多语言解决方案
当执行完`voerkai18n compile`后,项目结构大概如下:
```javascript | pure
chat
|-- languages
| |-- index.js
| |-- idMap.js
| |-- runtime.js
| |-- settings.json
| |-- cn.js
| |-- en.js
| |-- translates
| |-- default.json
|-- index.js
|-- package.json //name=chat
```
打开`languages/index.js`,大概如下:
```javascript | pure
// ....
const scope = new i18nScope({
id: "chat", // 当前作用域的id自动取当前工程的package.json的name
loaders:{
"en" : ()=>import("./en.js")
},
//.....
})
/// ....
```
- 可以看到在`languages/index.js`中创建了一个以当前工程`package.json``name``id``i18nScope`实例,然后注册到全局`VoerkaI18n`实例中。
- 为`en`语言创建了一个异步加载器,用来异步加载`en`语言包。
- 当打包`chat`应用时,`cn.js``en.js`等语言包均作为源码的一部分打包,差别在于非默认语言`en.js`单独作为一个`chunk`打包以便能异步加载。
下面假设,当应用上线后,客户要求增加`de`语言,但是我们的源码包中并没有包含`de`语言,利用`voerkiai18n`语言加载器功能,可以比较方便地实现动态增加支持语言的功能。
### 第一步:注册默认的语言加载器
`voerkiai18n`是采用语言加载器来加载语言包的,默认语言包以静态方法打包到源码中,而非默认语言则采用异步加载方式进行加载。
当注册了一个默认的语言包加载器后,如果切换到一个未注册的语言时,会调用默认的语言包加载器来获取语言包。
利用此特性就可以实现随时动态为应用增加语言支持的特性。
首先需要在应用中导入`i18nScope`实例,然后注册一个默认的语言加载器。
```javascript | pure
// 从当前工程导入`scope`实例
import { i18nScope } from "./languages"
// 注册默认的语言加载器
i18nScope.registerDefaultLoader(async (language,scope)=>{
// language: 要切换到此语言
// scope: 语言作用域实例
// 在此向服务器发起请求,请返回翻译后的其他语言文本
return {.....}
})
```
### 第二步:编写语言包加载器
然后,我们就可以在此向服务器发起异步请求来读取语言包文件。
```javascript | pure
// 从当前工程导入`scope`实例
import { i18nScope } from "./languages"
i18nScope.registerDefaultLoader(async (language,scope)=>{
return await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
})
```
语言加载器函数需要返回JSON格式的语言包大概如下
```json
{
"1":"xxxxx",
"2":"xxxxx",
"3":"xxxxx",
//....
}
```
**重点:为什么要向服务器传递`scope.id`参数?**
在多包环境下,按照多包/库开发的规范,每一个库或包均具有一个**唯一的id**,默认会使用`package.json`中的`name`字段。
**例如**
- 应用`A`,依赖于包/库`X``Y``Z`,并且`A/X/Y/Z`均使用了`voerkiai18n`作为多语言解决方案
- 当应用启动时,`A/X/Y/Z`均会创建一个`i18nScope`实例,其`id`分别是`A/X/Y/Z`,然后这些`i18nScope`实例会注册到全局的`VoerkaI18n`实例中(详见多库联动介绍)。
- 假如应用`A`配置支持`zh``cn`两种语言,当应用要切换到`de`语言时,那么不仅是`A`应用本身需要切换到`de`语言,所依赖的库也需要切换到`de`语言。但是库`X``Y``Z`本身可能支持`de`语言,也可以不支持。如果不支持,则同样需要向服务器请求该库的翻译语言。因此,在向服务器请求时就需要带上`scope.id`
### 第三步:将语言包文件保存在服务器
在上一步中,我们通过`fetch(/languages/${scope.id}/${language}.json)`来传递读取语言包您可以使用任意您喜欢的方式这意味着我们需要在web服务器上根据此URL来组织语言包以便可以下载到语言包。比如可以使用如下
```javascript | pure
webroot
|-- languages
<chat>
|-- de.json
<user>
|-- de.json
<manager>
|-- de.json
<log>
|-- de.json
```
`VoerkaI18n`将编写**如何语言加载器**和**如何在服务器上组织语言包**交由开发者自行决定,您完全可以根据自己的喜好来决定如何加载。
### 第四步:生成语言包文件
在本例中,我们要增加`de`语言,这就需要在服务器上生成一个对应的`de`语言包文件。
方法很简单,打开`languages/cn.js`文件,该文件大概如下:
```javascript | pure
module.exports = {
"1": "支持的语言",
"2": "默认语言",
"3": "激活语言",
"4": "名称空间s",
....
}
```
复制一份修改和更名为`de.json`,内容大概概如下:
```javascript | pure
{
"1": "支持的语言",
"2": "默认语言",
"3": "激活语言",
"4": "名称空间s",
....
}
```
然后将`de.json`复制到`languages/chat/de.json`即可。
同样地,我们也需要对`user``manager``log`等三个库的语言文件如法泡制,生成语言包文件`languages/user/de.json`,`languages/manager/de.json`,`languages/log/de.json`,这样这三个库也能实现支持`de`语言。
### 第五步:编写语言包补丁
至此,我们已经实现了可以为应用动态添加语言支持的功能。但是默认语言加载器只是针对的未知的语言起作用,而对内置的语言是不起作用的。也就是说上例中的内置语言`cn``en`不能通过此方法来加载。
在实际应用中,我们经常会在应用上线的,发现应用中的某此语言翻译错误,此时就可以利用`voerkai18n`的语言包补丁特性来解决此问题。
利用`voerkai18n`的语言包补丁特性,您就可以随时修复翻译错误,而不需要重新打包应用。
`voerkai18n`的语言包补丁特性的工作机制同样也是利用了默认语言加载器来加载语言包补丁。其工作原理很简单,如下:
- 按上例中的方式注册默认语言加载器
- 当i18nScope注册到全局VoerkaI18n时会调用默认的语言加载器,从服务器加载语言包,然后合并到本地语言包中,这样就很轻松地实现了动态言包的特性。
在本例中我们假设chat应用的中文语言发现翻译错误需要一个语言包补丁来修复方法如下
```javascript | pure
webroot
|-- languages
<chat>
|-- cn.json
```
按上例说明的方式,在服务器上编辑一个`cn.json`文件,保存到`languages/char/cn.json`,里面内容只需要包括出错的内容即可。
```javascript | pure
{
"4": "名称空间"
}
```
然后,当应用切换到指定`cn`语言时,就会下载该语言包合并到源码中的语言包,从而实现为语言包打补丁的功能,修复翻译错误。此功能简单而实用,强烈推荐。
### 小结
- 当注册了一个默认的语言加载器后,当切换到未配置过的语言时,会调用默认的文本加载器来从服务器加载语言文本。
- 对于已配置的语言,会在注册时从服务器加载进行合并,从而实现为语言包打补丁的功能。
- 您需要自己在服务器上组织存放配套的语言包文件,然后编写通过`fetch/axios`等从服务器加载
## 指南
### 语言包加载器
语言加载器是一个普通`异步函数`或者`返回Promise`的函数,可以用来从远程加载语言包文件。
语言加载器时会传入两个参数:
| 参数 | 说明 |
| --- | --- |
| language | 要切换的此语言|
| scope |语言作用域实例,其中`scope.id`值默认等于`package.json`中的`name`字段。详见[参考](../../reference/i18nscope)。 |
- 典型的语言加载器非常简单,如下:
```javascript | pure
import { i18nScope } from "./languages"
i18nScope.registerDefaultLoader(async (language,scope)=>{
return await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
})
```
- 为什么要应用自己编写语言加载器,而不是提供约定开箱即用?
主要原因是编写语言加载器很简单,但是如何组织在服务器上的保存,想让应用开发者自行决定。比如,开发者完全可以将语言包保存在数据库中等。 另外考虑安全、兼容性等原因,因此`voerkai18n`就将此交由开发者自行编写。
### 编写语言切换界面
当编写语言切换界面时对未注册的语言是无法枚举出来的需要应用自行处理逻辑。例如在Vue应用中
```javascript | pure
<div>
<button
@click="i18n.activeLanguage=lng.name"
v-for="lng of i18n.langauges">
{{ lng.title }}
</button>
</div>
```
还是以本例来说明上面的Vue应用是无法枚举出来`de`语言的,这就需要应用自己处理,比如:
```html
<template>
<div>
<button
@click="i18n.activeLanguage=lng.name"
v-for="lng of i18n.langauges">
{{ lng.title }}
</button>
<!-- 预期要支持的语言 -->
<button @click="i18n.activeLanguage=lng.name"
v-for="lng of ['de','jp',.......]">
{{ lng }}
</button>
</div>
</template>
```
通过编写合适的语言切换界面,您可以在后期随时在线增加语种支持。
### 关于语言包补丁
语言包补丁仅对在`settings.json`配置的语言起作用,而动态增加的语种因为其语言包本身就保存在服务器,因此就不存在补丁的问题。
语言包补丁会在加载时自动合并到源码中的语言包,并且会自动在本地`localStorage`中缓存。

View File

@ -0,0 +1,37 @@
# 运行时
`@voerkai18n/runtime``voerkai18n`的运行时依赖,支持两种依赖方式。
## 源码依赖
默认情况下,运行`voerkai18n compile`时会在`languages`文件下生成运行时文件`runtime.js`,该文件被`languages/index.js`引入,里面是核心运行时`ES6`源代码(`@voerkai18n/runtime`源码),也就是在您的工程中是直接引入的运行时代码,因此就不需要额外安装`@voerkai18n/runtime`了。
此时,`@voerkai18n/runtime`源码就成为您工程是一部分。
## 库依赖
当运行`voerkai18n compile --no-inline-runtime`时,就不会生成运行时文件`runtime.js`,而是采用`import "@voerkai18n/runtime`的方式导入运行时,此时会自动/手动安装`@voerkai18n/runtime`到运行依赖中。
## 如何选择
**那么应该选择`源码依赖`还是`库依赖`呢?**
问题的重点在于,在`monorepo`工程或者`开发库`时,`源码依赖`会导致存在重复的运行时源码。而采用`库依赖`,则不存在此问题。因此:
- 普通应用采用`源码依赖`方式,运行`voerkai18n compile `来编译语言包。
- `monorepo`工程或者`开发库`采用`库依赖``voerkai18n compile --no-inline-runtime`来编译语言包。
## 注意
- `@voerkai18n/runtime`发布了`commonjs``esm`两个经过`babel/rollup`转码后的`ES5`版本。
- 每次运行`voerkai18n compile`时均会重新生成`runtime.js`源码文件,为了确保最新的运行时,请及时更新`@voerkai18n/cli`
- 当升级了`@voerkai18n/runtime`后,需要重新运行`voerkai18n compile`以重新生成`runtime.js`文件。

View File

@ -0,0 +1,45 @@
# 文本映射
虽然`VoerkaI18n`推荐采用`t("中华人民共和国万岁")`形式的符合直觉的翻译形式,而不是采用`t("xxxx.xxx")`这样不符合直觉的形式,但是为什么大部份的国际化方案均采用`t("xxxx.xxx")`形式?
在我们的方案中t("中华人民共和国万岁")形式相当于采用原始文本进行查表,语言名形式如下:
```javascript | pure
// en.js
{
"中华人民共和国":"the people's Republic of China"
}
// jp.js
{
"中华人民共和国":"中華人民共和国"
}
```
很显然,直接使用文本内容作为`key`,虽然符合直觉,但是会造成大量的冗余信息。因此,`voerkai18n compile`会将之编译成如下:
```javascript | pure
//idMap.js
{
"1":"中华人民共和国万岁"
}
// en.js
{
"1":"Long live the people's Republic of China"
}
// jp.js
{
"2":"中華人民共和国"
}
```
如此,就消除了在`en.js``jp.js`文件中的冗余。但是在源代码文件中还存在`t("中华人民共和国万岁")`,整个运行环境中存在两份副本,一份在源代码文件中,一份在`idMap.js`中。
为了进一步减少重复内容,因此,我们需要将源代码文件中的`t("中华人民共和国万岁")`更改为`t("1")`,这样就能确保无重复冗余。但是,很显然,我们不可能手动来更改源代码文件,这就需要由`voerkai18n`提供的一个编译区插件来做这一件事了。
`babel-plugin-voerkai18n`插件为例,该插件同时还完成一份任务,就是自动读取`voerkai18n compile`生成的`idMap.js`文件,然后将`t("中华人民共和国万岁")`自动更改为`t("1")`,这样就完全消除了重复冗余信息。
所以在最终形成的代码中实际上每一个t函数均是`t("1")``t("2")``t("3")``...``t("n")`的形式,最终代码还是采用了用`key`来进行转换,只不过这个过程是自动完成的而已。
**注意:**
- 如果没有启用`babel-plugin-voerkai18n``vite`等编译区插件,还是可以正常工作,但是会有一份默认语言的冗余信息存在。

View File

@ -0,0 +1,251 @@
---
title: 快速入门
---
# 快速入门
本节以标准的`Nodejs`应用程序为例,简要介绍`VoerkaI18n`国际化框架的基本使用。其他`vue``react`应用的使用也基本相同。
```shell
myapp
|--package.json
|--index.js
```
在本项目的所有支持的源码文件中均可以使用`t`函数对要翻译的文本进行包装,简单而粗暴。
```javascript | pure
// index.js
console.log(t("中华人民共和国万岁"))
console.log(t("中华人民共和国成立于{}",1949))
```
`t`翻译函数是从`myapp/languages/index.js`文件导出的翻译函数,但是现在`myapp/languages`还不存在,后续会使用工具自动生成。`voerkai18n`后续会使用正则表达式对提取要翻译的文本。
## 第一步:安装命令行工具
```shell
> npm install -g @voerkai18n/cli
> yarn global add @voerkai18n/cli
>pnpm add -g @voerkai18/cli
```
## 第二步:初始化工程
在工程目录中运行`voerkai18n init`命令进行初始化。
```javascript | pure
> voerkai18n init
```
上述命令会在当前工程目录下创建`languages/settings.json`文件。如果您的源代码在`src`子文件夹中,则会创建在`src/languages/settings.json`
`settings.json`内容如下:
```json
{
"languages": [
{
"name": "zh",
"title": "zh"
},
{
"name": "en",
"title": "en"
}
],
"defaultLanguage": "zh",
"activeLanguage": "zh",
"namespaces": {}
}
```
上述命令代表了:
- 本项目拟支持`中文``英文`两种语言。
- 默认语言是`中文`(即在源代码中直接使用中文)
- 激活语言是`中文`
**注意:**
- `voerkai18n init`是可选的,`voerkai18n extract`也可以实现相同的功能。
- 一般情况下,您可以手工修改`settings.json`,如定义名称空间。
## 第三步:提取文本
接下来我们使用`voerkai18n extract`命令来自动扫描工程源码文件中的需要的翻译的文本信息。
```shell
myapp>voerkai18n extract
```
执行`voerkai18n extract`命令后,就会在`myapp/languages`通过生成`translates/default.json``settings.json`等相关文件。
- **translates/default.json** 该文件就是需要进行翻译的文本信息。
- **settings.json** 语言环境的基本配置信息,可以进行修改。
最后文件结构如下:
```shell
myapp
|-- languages
|-- settings.json // 语言配置文件
|-- translates // 此文件夹是所有需要翻译的内容
|-- default.json // 默认名称空间内容
|-- package.json
|-- index.js
```
**如果略过第一步中的`voerkai18n init`,也可以使用以下命令来为创建和更新`settinbgs.json`**
```javascript | pure
myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
```
以上命令代表:
- 扫描当前文件夹下所有源码文件,默认是`js``jsx``html``vue`文件类型。
- 计划支持`zh``en``de``jp`四种语言
- 默认语言是中文。(指在源码文件中我们直接使用中文即可)
- 激活语言是中文(即默认切换到中文)
- `-D`代表显示扫描调试信息
## 第四步:翻译文本
接下来就可以分别对`language/translates`文件夹下的所有`JSON`文件进行翻译了。每个`JSON`文件大概如下:
```json
{
"中华人民共和国万岁":{
"en":"<在此编写对应的英文翻译内容>",
"de":"<在此编写对应的德文翻译内容>"
"jp":"<在此编写对应的日文翻译内容>",
"$files":["index.js"] // 记录了该信息是从哪几个文件中提取的
},
"中华人民共和国成立于{}":{
"en":"<在此编写对应的英文翻译内容>",
"de":"<在此编写对应的德文翻译内容>"
"jp":"<在此编写对应的日文翻译内容>",
"$files":["index.js"]
}
}
```
我们只需要修改该文件翻译对应的语言即可。
**重点:如果翻译期间对源文件进行了修改,则只需要重新执行一下`voerkai18n extract`命令,该命令会进行以下操作:**
- 如果文本内容在源代码中已经删除了,则会自动从翻译清单中删除。
- 如果文本内容在源代码中已修改了,则会视为新增加的内容。
- 如果文本内容已经翻译了一部份了,则会保留已翻译的内容。
因此,反复执行`voerkai18n extract`命令是安全的,不会导致进行了一半的翻译内容丢失,可以放心执行。
大部分国际化解决方案至此就需要交给人工进行翻译了,但是`voerkai18n`除了手动翻译外,通过`voerkai18n translate`命令来实现**调用在线翻译服务**进行自动翻译。
```javascript | pure
>voerkai18n translate --provider baidu --appkey <在百度翻译上申请的密钥> --appid <在百度翻译上申请的appid>
```
在项目文件夹下执行上面的语句将会自动调用百度的在线翻译API进行翻译以现在的翻译水平而言您只需要进行少量的微调即可。关于`voerkai18n translate`命令的使用请查阅后续介绍。
## 第五步:编译语言包
当我们完成`myapp/languages/translates`下的所有`JSON语言文件`的翻译后(如果配置了名称空间后,每一个名称空间会对应生成一个文件,详见后续`名称空间`介绍),接下来需要对翻译后的文件进行编译。
```shell
myapp> voerkai18n compile
```
`compile`命令根据`myapp/languages/translates/*.json``myapp/languages/settings.json`文件编译生成以下文件:
```javascript | pure
|-- languages
|-- settings.json // 语言配置文件
|-- idMap.js // 文本信息id映射表
|-- runtime.js // 运行时源码
|-- index.js // 包含该应用作用域下的翻译函数等
|-- zh.js // 语言包
|-- en.js
|-- jp.js
|-- de.js
|-- translates // 此文件夹包含了所有需要翻译的内容
|-- default.json
|-- package.json
|-- index.js
```
## 第六步:导入翻译函数
第一步中我们在源文件中直接使用了`t`翻译函数包装要翻译的文本信息,该`t`翻译函数就是在编译环节自动生成并声明在`myapp/languages/index.js`中的。
```javascript | pure
import { t } from "./languages"
```
因此,我们需要在需要进行翻译时导入该函数即可。
但是如果源码文件很多,重次重复导入`t`函数也是比较麻烦的,所以我们也提供了一个`babel/vite`等插件来自动导入`t`函数。
## 第六步:切换语言
当需要切换语言时,可以通过调用`change`方法来切换语言。
```javascript | pure
import { i18nScope } from "./languages"
// 切换到英文
await i18nScope.change("en")
// VoerkaI18n是一个全局单例可以直接访问
await VoerkaI18n.change("en")
```
`i18nScope.change``VoerkaI18n.change`两者是等价的。
一般可能也需要在语言切换后进行界面更新渲染,可以订阅事件来响应语言切换。
```javascript | pure
import { i18nScope } from "./languages"
// 切换到英文
i18nScope.on((newLanguage)=>{
...
})
//
VoerkaI18n.on((newLanguage)=>{
...
})
```
## 第七步:语言包补丁
一般情况下,多语言的工程化过程就结束了,`voerkai18n`在多语言实践考虑得更加人性化。有没有经常发现这样的情况,当项目上线后,才发现:
- 翻译有误
- 客户对某些用语有个人喜好,要求你更改。
- 临时要增加支持一种语言
一般碰到这种情况,只好重新打包构建工程,重新发布,整个过程繁琐而麻烦。
现在`voerkai18n`针对此问题提供了完美的解决方案,可以通过服务器来为应用打语言包补丁和增加语言支持,而不需要重新打包应用和修改应用。
方法如下:
1. 注册一个默认的语言包加载器函数,用来从服务器加载语言包文件
```javascript | pure
import { i18nScope } from "./languages"
i18nScope.registerDefaultLoader(async (language,scope)=>{
return await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
})
```
2. 将语言包补丁文件保存在服务器上指定的位置`/languages/<应用名称>/<语言名称>.json`即可。
3. 当应用启动后会自动从服务器上加载语言补丁包,从而实现动为语言包打补丁的功能。也可以实现动态增加临时支持一种语言的功能
更完整的说明详见[`动态加载语言包`](../advanced/remoteLoad.md)和[`语言包补丁`](../advanced/lngpatch.md)功能介绍。

View File

@ -0,0 +1,9 @@
---
title: 更新历史
---
# 更新历史
## 2022/8/5
- 增加语言包补丁功能,可以在应用上线后动态更新修复翻译错误
- 增加动态加载语言包机制,可以在应用上线后动态添加语言支持

View File

@ -0,0 +1,47 @@
---
title: 安装
---
# 安装
`VoerkaI18n`国际化框架是一个开源多包工程,主要由以下几个包组成:
## **@voerkai18/cli**
包含文本提取/编译等命令行工具,一般应该安装到全局。
```javascript | pure
npm install --g @voerkai18n/cli
yarn global add @voerkai18n/cli
pnpm add -g @voerkai18n/cli
```
## **@voerkai18/runtime**
**可选的**,运行时,`@voerkai18/cli`的依赖。大部分情况下不需要手动安装,一般仅在开发库项目时采用独立的运行时依赖。
```javascript | pure
npm install --save @voerkai18n/runtime
yarn add @voerkai18n/runtime
pnpm add @voerkai18n/runtime
```
## **@voerkai18/formatters**
**可选的**,一些额外的格式化器,可以按需进行安装到`dependencies`中,用来扩展翻译时对插值变量的额外处理。
## **@voerkai18/babel**
可选的`babel`插件,用来实现自动导入翻译函数和翻译文本映射自动替换。
## **@voerkai18/vue**
可选的`vue`插件用来为Vue应用提供语言动态切换功能。
## **@voerkai18/react**
可选的用来为React应用提供语言动态切换功能。
## **@voerkai18/vite**
可选的`vite`插件,用来为`vite`应用提供自动导入翻译函数和翻译文本映射自动替换。

View File

@ -0,0 +1,3 @@
---
title: 常见问题
---

View File

@ -0,0 +1,38 @@
# 概述
基于`javascript`的国际化方案很多,比较有名的有`fbt``i18next``react-i18next``vue-i18n``react-intl`等等,每一种解决方案均有大量的用户。为什么还要再造一个轮子?好吧,再造轮子的理由不外乎不满足于现有方案,总想着现有方案的种种不足之处,然后就撸起袖子想造一个轮子,也不想想自己什么水平。
那么到底是对现有解决方案有什么不满?最主要有三点:
- 大部份均为要翻译的文本信息指定一个`key`,然后在源码文件中使用形如`$t("message.login")`之类的方式,然后在翻译时将之转换成最终的文本信息。此方式最大的问题是,在源码中必须人为地指定每一个`key`,在中文语境中,想为每一句中文均配套想一句符合语义的`英文key`是比较麻烦的,也很不直观不符合直觉。我希望在源文件中就直接使用中文,如`t("中华人民共和国万岁")`,然后国际化框架应该能自动处理后续的一系列麻烦。
- 要能够比较友好地支持多库多包`monorepo`场景下的国际化协作,当主程序切换语言时,其他包或库也可以自动切换,并且在开发上每个包或库均可以独立地进行开发,集成到主程序时能无缝集成。这点在现有方案上没有找到比较理想的解决方案。
- 大部份国际化框架均将中文视为二等公民,大部份情况下您应该采用英文作为第一语言,虽然这不是太大的问题,但是既然要再造一个轮子,为什么不将中文提升到一等公民呢。
基于此就开始造出`VoerkaI18n`这个**全新的国际化多语言解决方案**,主要特性包括:
- 全面工程化解决方案,提供初始化、提取文本、自动翻译、编译等工具链支持。
- 符合直觉,不需要手动定义文本`Key`映射。
- 强大的插值变量`格式化器`机制,可以扩展出强大的多语言特性。
- 支持`babel`插件自动导入`t`翻译函数。
- 支持`nodejs`、浏览器(`vue`/`react`)前端环境。
- 采用`工具链``运行时`分开设计,发布时只需要集成很小的运行时。
- 高度可扩展的`复数``货币``数字`等常用的多语言处理机制。
- 翻译过程内,提取文本可以自动进行同步,并保留已翻译的内容。
- 支持远程加载语言包,并且可以在线打语言补丁包
- 支持调用在线自动翻译对提取文本进行翻译。

View File

@ -0,0 +1,4 @@
# 获取支持
- 通过Gitgee或Github提交[issues](https://github.com/zhangfisher/voerka-i18n/issues)
- 国内用户可以加[QQ群](https://qm.qq.com/cgi-bin/qm/qr?k=jKyZR9KupT9Ith5ZsulB-i04OaJDkCwe&jump_from=webapi).

View File

@ -0,0 +1,11 @@
# 版本信息
| 包| 版本号| 最后更新|说明|
| --- | :---:| --- |---|
|**@voerkai18n/utils**|1.0.12|08/05|公共工具库
|**@voerkai18n/runtime**|1.0.27|08/05|核心运行时
|**@voerkai18n/formatters**|1.0.6|04/15|格式化器,提供对要翻译文本的转换功能
|**@voerkai18n/react**|1.0.4|04/16|React支持,提供语言切换等功能
|**@voerkai18n/cli**|1.0.32|08/05|命令行工具,用来初始化/提取/编译/自动翻译等工具链
|**@voerkai18n/babel**|1.0.23|08/05|Babel插件实现自动导入t函数和自动文本映射
|**@voerkai18n/vite**|1.0.12|08/05|Vite插件,提供自动插入翻译函数和文本映射等功能
|**@voerkai18n/vue**|1.0.5|04/15|Vue3插件,提供自动插件翻译函数和语言切换功能

View File

@ -0,0 +1,73 @@
# Babel插件
全局安装`@voerkai18n/babel`插件用来进行自动导入`t`函数和自动文本映射。
## 安装
```javascript | pure
> npm install -g @voerkai18n/babel
> yarn global add @voerkai18n/babel
> pnpm add -g @voerkai18n/babel
```
## 启用插件
使用方法如下:
- 在`babel.config.js`中配置插件
```javascript | pure
const i18nPlugin = require("@voerkai18n/babel")
module.expors = {
plugins: [
[
i18nPlugin,
{
// 可选,指定语言文件存放的目录,即保存编译后的语言文件的文件夹
// 可以指定相对路径,也可以指定绝对路径
// location:"",
autoImport:"#/languages"
}
]
]
}
```
这样,当在进行`babel`转码时,就会自动在`js`源码文件中导入`t`翻译函数。
## 插件参数
插件支持以下参数:
- **location**
配置`langauges`文件夹位置,默认会使用当前文件夹下的`languages`文件。
因此,如果你的`babel.config.js`在项目根文件夹,而`languages`文件夹位于`src/languages`,则可以将`location="src/languages"`,这样插件会自动从该文件夹读取需要的数据。
- **autoImport**
用来配置导入的路径。比如 `autoImport="#/languages" `则当在babel转码时如果插件检测到t函数的存在并没有导入就会自动在该源码中自动导入`import { t } from "#/languages"`
配置`autoImport`时需要注意的是,为了提供一致的导入路径,视所使用的打包工具或转码插件,如`webpack``rollup`等。比如使用`babel-plugin-module-resolver`
```javascript | pure
module.expors = {
plugins: [
[
"module-resolver",
{
root:"./",
alias:{
"languages":"./src/languages"
}
}
]
]
}
```
这样配置`autoImport="languages"`,则自动导入`import { t } from "languages"`
`webpack``rollup`等打包工具也有类似的插件可以实现别名等转换,其目的就是让`@voerkai18n/babel`插件能自动导入固定路径,而不是各种复杂的相对路径。

251
docs/src/guide/tools/cli.md Normal file
View File

@ -0,0 +1,251 @@
# 命令行工具
`@voerkai18n/cli`命令行工具用来实现工程初始化、扫描提取文本、自动翻译和编译语言等功能。
::: info
建议将`@voerkai18n/cli`命令行工具安装在全局
:::
## 安装
全局安装`@voerkai18n/cli`工具。
```javascript | pure
> npm install -g @voerkai18n/cli
> yarn global add @voerkai18n/cli
> pnpm add -g @voerkai18n/cli
```
然后就可以执行:
```javascript | pure
> voerkai18n init
> voerkai18n extract
> voerkai18n compile
```
如果没有全局安装,则需要:
```javascript | pure
> yarn voerkai18n init
> yarn voerkai18n extract
> yarn voerkai18n compile
---
> pnpm voerkai18n init
> pnpm voerkai18n extract
> pnpm voerkai18n compile
```
## 初始化 - init
用于在指定项目创建`voerkai18n`国际化配置文件。
```shell
> voerkai18n init --help
初始化项目国际化配置
Arguments:
location 工程项目所在目录
Options:
-D, --debug 输出调试信息
-r, --reset 重新生成当前项目的语言配置
-lngs, --languages <languages...> 支持的语言列表 (default: ["zh","en"])
-d, --defaultLanguage 默认语言
-a, --activeLanguage 激活语言
-h, --help display help for command
```
**使用方法如下:**
首先需要在工程文件下运行`voerkai18n init`命令对当前工程进行初始化。
```javascript | pure
//- `lngs`参数用来指定拟支持的语言名称列表
> voerkai18n init . -lngs zh en jp de -d zh
```
运行`voerkai18n init`命令后,会在当前工程中创建相应配置文件。
```javascript | pure
myapp
|-- languages
|-- settings.json // 语言配置文件
|-- package.json
|-- index.js
```
`settings.json`文件很简单,主要是用来配置要支持的语言等基本信息。
```javascript | pure
module.exports = {
// 拟支持的语言列表
"languages": [
{
"name": "zh",
"title": "中文"
},
{
"name": "en",
"title": "英文"
}
],
// 默认语言,即准备在源码中写的语言,一般我们可以直接使用中文
"defaultLanguage": "zh",
// 激活语言即默认要启用的语言一般等于defaultLanguage
"activeLanguage": "zh",
// 翻译名称空间定义,详见后续介绍。
"namespaces": {}
}
```
**说明:**
- 您也可以手动自行创建`languages/settings.json`文件。这样就不需运行`voerkai18n init`命令了。
- 如果你的源码放在`src`文件夹,则`init`命令会自动在在`src`文件夹下创建`languages`文件夹。
- `voerkai18n init`是可选的,直接使用`extract`时也会自动创建相应的文件。
- `-m`参数用来指定生成的`settings.json`的模块类型:
- 当`-m=auto`时,会自动读取前工程`package.json`中的`type`字段
- 当`-m=esm`时,会生成`ESM`模块类型的`settings.json`
- 当`-m=cjs`时,会生成`commonjs`模块类型的`settings.json`
- `location`参数是可选的,如果没有指定则采用当前目录。
如果你想将`languages`安装在`src/languages`下,则可以指定`voerkai18n init ./src`
## 提取文本 - extract
扫描提取当前项目中的所有源码,提取出所有需要翻译的文本内容并保存在到`<工程源码目录>/languages/translates/*.json`
```shell
> voerkai18n extract --help
扫描并提取所有待翻译的字符串到<languages/translates>文件夹中
Arguments:
location 工程项目所在目录 (default: "./")
Options:
-D, --debug 输出调试信息
-lngs, --languages 支持的语言
-d, --defaultLanguage 默认语言
-a, --activeLanguage 激活语言
-ns, --namespaces 翻译名称空间
-e, --exclude <folders> 排除要扫描的文件夹,多个用逗号分隔
-u, --updateMode 本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并
-f, --filetypes 要扫描的文件类型
-h, --help display help for command
```
**说明:**
- 启用`-d`参数时会输出提取过程,显示从哪些文件提取了几条信息。
- 如果已手动创建或通过`init`命令创建了`languages/settings.json`文件,则可以不指定`-ns``-lngs``-d``-a`参数。`extract`会优先使用`languages/settings.json`文件中的参数来进行提取。
- `-u`参数用来指定如何将提取的文本与现存的文件进行合并。因为在国际化流程中,我们经常面临源代码变更时需要更新翻译的问题。支持三种合并策略。
- **sync**:同步(默认值),两者自动合并,并且会删除在源码文件中不存在的文本。如果某个翻译已经翻译了一半也会保留。此值适用于大部情况,推荐。
- **overwrite**:覆盖现存的翻译内容。这会导致已经进行了一半的翻译数据丢失,**慎用**。
- **merge**合并与sync的差别在于不会删除源码中已不存在的文本。
- `-e`参数用来排除扫描的文件夹,多个用逗号分隔。内部采用`gulp.src`来进行文件提取,请参数。如 `-e !libs,core/**/*`。默认会自动排除`node_modules`文件夹
- `-f`参数用来指定要扫描的文件类型,默认`js,jsx,ts,tsx,vue,html`
- `extract`是基于正则表达式方式进行匹配的,而不是像`i18n-next`采用基于`AST`解析。
>**重点:**
>
>默认情况下,`voerkai18n extract`可以安全地反复多次执行,不会导致已经翻译一半的内容丢失。
>
>如果想添加新的语言支持,也`voerkai18n extract`也可以如预期的正常工作。
## 自动翻译 - translate
在工程文件夹下执行`voerkai18n translate`命令,该命令会读取`languages/settings.json`配置文件,并调用在线翻译服务(如百度在线翻译)对提取的文本(`languages/translates/*.json`)进行自动翻译。
```shell
Usage: voerkai18n translate [options] [location]
调用在线翻译服务商的API翻译译指定项目的语言包,如使用百度云翻译服务
Arguments:
location 工程项目所在目录
Options:
-p, --provider <value> 在线翻译服务提供者名称或翻译脚本文件 (default: "baidu")
-m, --max-package-size <value> 将多个文本合并提交的最大包字节数 (default: 3000)
--appkey [key] API密钥
--appid [id] API ID
--no-backup 备份原始文件
--mode 翻译模式取值auto=仅翻译未翻译的,full=全部翻译
-q, --qps <value> 翻译速度限制,即每秒可调用的API次数 (default: 1)
-h, --help 显示帮助
```
- 内置支持调用百度的在线翻译服务,您需要百度的网站上(http://api.fanyi.baidu.com/)申请开通服务,开通后可以得到`appid``appkey`(密钥)。
- `--provider`用来指定在线翻译服务提供者内置支持的是百度在线翻译。也可以传入一个js脚本如下
```javascript | pure
// youdao.js
module.exports = async function(options){
let { appkey,appid } = options
return {
translate:async (texts,from,to){
// texts是一个Array
// from,to代表要从哪一种语言翻译到何种语言
.....
// 在此对texts内容调用在线翻译API
// 翻译结果应该返回与texts对应的数组
// 如果出错则应该throw new Error()
return [...]
}
}
}
```
- `qps`用来指定调用在线翻译API的速度默认是1代表每秒调用一次此参数的引入是考虑到有些翻译平台的免费API有QPS限制。比如百度在线翻译免费版本限制`QPS`就是1即每秒只能调用一次。如果您购买了服务则可以将`QPS`调高。
- 默认情况下,每次运行时均会备份原始的翻译文件至`languages/translates/backup``--no-backup`可以禁止备份。
- 默认情况下,`voerkai18n translate`会在每次运行时跳过已经翻译过的内容,这样可以保留翻译成果。此特性在您对自动翻译的内容进行修改后,再多次运行`voerkai18n translate`命令时均能保留翻译内容,不会导致您修改调整过的内容丢失。`--mode full`参数可以完全覆盖翻译,请慎用。
- 为了提高在线翻译的速度,`voerkai18n translate`并不是一条文本调用一次API而是将多条文本合并起来进行调用但是单次调用也是有数据包大小的限制的`--max-package-size`参数用来指定数据包的最大值。比如百度建议,为保证翻译质量,请将单次请求长度控制在 6000 bytes以内汉字约为输入参数 2000 个)。
- 需要注意的是,自动翻译虽然准确性还不错,真实场景还是需要进行手工调整的,特别是自动翻译一般不能识别插值变量。
## 编译 - compile
编译当前工程的语言包,编译结果输出在.`/langauges`文件夹。
```shell
Usage: voerkai18n compile [options] [location]
编译指定项目的语言包
Arguments:
location 工程项目所在目录 (default: "./")
Options:
-D, --debug 输出调试信息
-m, --moduleType [types] 输出模块类型,取值auto,esm,cjs (default: "esm")
--no-inline-runtime 不嵌入运行时源码
-h, --help display help for command
```
`voerkai18n compile`执行后会在`langauges`文件夹下输出:
```javascript | pure
myapp
|--- langauges
|-- index.js // 当前作用域的源码
|-- idMap.js // 翻译文本与id的映射文件
|-- formatters.js // 自定义格式化器
|-- zh.js // 中文语言包
|-- en.js // 英文语言包
|-- xx.js // 其他语言包
|-- ...
```
**说明:**
- 在当前工程目录下,一般不需要指定参数就可以反复多次进行编译。
- 您每次修改了源码并`extract`后,均应该再次运行`compile`命令。
- 如果您修改了`formatters.js`,执行`compile`命令不会重新生成和修改该文件。
- `--no-inline-runtime `参数用来指示如何引用运行时。默认会将运行时代码生成保存在`languages/runtime.js`,应用以源码形式引用。当启用`--no-inline-runtime `参数时会采用`require("@voerkai18n/runtime")`的方式。

View File

@ -0,0 +1,97 @@
# Vite插件
`@voerkai18n/babel`插件在`vite`应用中不能正常使用,需要使用`@voerkai18n/vite`插件来完成类似的功能,包括自动文本映射和自动导入`t`函数。
## 安装
`@voerkai18n/vite`只需要作为开发依赖安装即可。
```javascript | pure
npm install --save-dev @voerkai18n/vite
yarn add -D @voerkai18n/vite
pnpm add -D @voerkai18n/vite
```
## 启用插件
接下来在`vite.config.js`中配置启用`@voerkai18n/vite`插件。
```javascript | pure
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Inspect from 'vite-plugin-inspect'// 可选的
import Voerkai18nPlugin from "@voerkai18n/vite"
export default defineConfig({
plugins: [
Inspect(), // 可选的
Voerkai18nPlugin({debug:true}),
vue()
]
})
```
- ` vite-plugin-inspect`是开发`vite`插件时的调试插件,启用后就可以通过`localhost:3000/__inspect/ `查看Vue源码文件经过插件处理前后的内容一般是Vite插件开发者使用。上例中安装后就可以查看`Voerkai18nPlugin``Vue`文件干了什么事,可以加深理解,**正常使用不需要安装**。
## 插件功能
`@voerkai18n/vite`插件配置启用后,`vite`在进行`dev``build`时,就会在`<script setup>....</script>`自动注入`import { t } from "languages" `,同时会扫描源代码文件(包括`vue`,`js`等),根据`idMap.js`文件里面的文本映射表,将`t('"xxxx")`转换成`t("<id>")`的形式。
不同于`@voerkai18n/babel`插件,`@voerkai18n/vite`插件不需要配置`location``autoImport`参数,能正确地处理导入`languages`路径。
## 插件参数
`vite`插件支持以下参数:
```javascript | pure
import Voerkai18nPlugin from "@voerkai18n/vite"
export default defineConfig({
plugins: [
Inspect(), // 可选的
Voerkai18nPlugin({
location: "./", // 可选的,指定当前工程目录
autoImport: true, // 是否自动导入t函数
debug:false, // 是否输出调试信息,当=true时在控制台输出转换匹配的文件清单
patterns:[
"!(?<!.vue\?.*).(css|json|scss|less|sass)$", // 排除所有css文件
/\.vue(\?.*)?/, // 所有vue文件
]
}),
vue()
]
})
```
- `location`:可选的,用来指定当前工程目录,一般情况是不需要配置的,会自动取当前文件夹。并且假设`languages`文件夹在`<location>/src/languages`文件夹下。
- `autoImport`:可选的,默认`true`,用来配置是否自动导入`t`函数。当vue文件没有指定导入时才会自动导入并且根据当前vue文件的路径处理好导入位置。
- `debug`:可选的,开启后会在控制台输出一些调试信息,对一般用户没有用。
- `patterns`:可选的,一些正则表达式匹配规则,用来过滤匹配哪一些文件需要进行扫描和处理。默认的规则:
```javascript | pure
const patterns={
"!(?<!.vue\?.*).(css|json|scss|less|sass)$", // 排除所有css文件
/\.vue(\?.*)?/, // 所有vue文件
"!.*\/node_modules\/.*", // 排除node_modules
"!/.*\/languages\/.*", // 默认排除语言文件
"!\.babelrc", // 排除.babelrc
"!babel\.config\.js", // 排除babel.config.js
"!package\.json$", // 排除package.json
"!vite\.config\.js", // 排除vite.config.js
"!^plugin-vue:.*" // 排除plugin-vue
}
```
`patterns`的匹配规则语法支持`正则表达式字符串``正则表达式`两种用来对经vite处理的文件名称进行匹配和处理。
- `正则表达式`比较容易理解,匹配上的就进行处理。
- `正则表达式字符串`支持一些简单的语法扩展,包括:
- `!`符号:添加在字符串前面来进行排除匹配。
- `**`:将`**`替换为`.*`,允许使用类似`"/code/apps/test/**/node_modules/**"`的形式来匹配连续路径。
- ``:将``替换为`[^\/]?`,用来匹配单个字符
- `*`:将`*`替换为`[^\/]*`,匹配路径名称

View File

@ -0,0 +1,92 @@
# Vue插件
`vue3`项目中可以安装`@voerkai18n/vue`来实现`枚举语言``变量注入``语言切换`等功能。
## **安装**
`@voerkai18n/vue`安装为运行时依赖
```javascript | pure
npm install @voerkai18n/vue
pnpm add @voerkai18n/vue
yarn add @voerkai18n/vue
```
## 启用插件
```javascript | pure
import { createApp } from 'vue'
import Root from './App.vue'
import i18nPlugin from '@voerkai18n/vue'
import { i18nScope } from './languages'
const app = createApp(Root)
app.use(i18nPlugin,{ i18nScope }) // 重点需要引入i18nScope
app.mount('#app')
```
插件安装成功后,在当前`Vue App`实例上`provide`一个`i18n`响应式实例。
## 注入`i18n`实例
接下来在组件中按需注入`i18n`实例,可以用来访问当前的`激活语言``默认语言``切换语言`等。
```javascript | pure
<script>
import {reactive } from 'vue'
export default {
inject: ['i18n'], // 注入i18n实例该实例由@voerkai18n/vue插件提供
....
}
</script>
```
声明`inject: ['i18n']`后在当前组件实例中就可以访问`this.i18n`,该实例是一个经过`reactive`封闭的响应式对象,其内容是:
```javascript | pure
this.i18n = {
activeLanguage, // 当前激活语言,可以通过直接赋值来切换语言
defaultLanguage, // 默认语言名称
languages // 支持的语言列表=[{name,title},...]
}
```
## 应用示例
注入`i18n`实例后就可以在此基础上实现`激活语言``默认语言``切换语言`等功能。
```vue
<script>
import {reactive } from 'vue'
export default {
inject: ['i18n'], // 注入i18n实例该实例由@voerkai18n/vue插件提供
....
}
</script>
<template>
<div>当前语言:{{i18n.activeLanguage}}</div>
<div>默认语言:{{i18n.defaultLanguage}}</div>
<div>
<button
@click="i18n.activeLanguage=lng.name"
v-for="lng of i18n.langauges">
{{ lng.title }}
</button>
</div>
</templage>
```
## 插件参数
`@voerkai18n/vue`插件支持以下参数:
```javascript | pure
import { i18nScope } from './languages'
app.use(i18nPlugin,{
i18nScope, // 重点需要引入当前作用域的i18nScope
forceUpdate:true // 当语言切换时是否强制重新渲染
})
```
- 当`forceUpdate=true`时,`@voerkai18n/vue`插件在切换语言时会调用`app._instance.update()`对整个应用进行强制重新渲染。大部分情况下,切换语言时强制对整个应用进行重新渲染的行为是符合预期的。您也可以能够通过设`forceUpdate=false`来禁用强制重新渲染,此时,界面就不会马上看到语言的切换,需要您自己控制进行重新渲染。

View File

@ -0,0 +1,32 @@
---
title: 切换语言
---
# 切换语言
## 切换语言
可以通过全局单例或当前作用域实例的`change`方法来切换语言。
```javascript | pure
import { i18nScope } from "./languages"
// 切换到英文
await i18nScope.change("en")
// VoerkaI18n是一个全局单例可以直接访问
await VoerkaI18n.change("en")
```
## 侦听语言切换事件
```javascript | pure
import { i18nScope } from "./languages"
// 切换到英文
i18nScope.on((newLanguage)=>{
...
})
// 直接在全局单例上调用
VoerkaI18n.on((newLanguage)=>{
...
})
```

View File

@ -0,0 +1,5 @@
---
title: 货币
---
# 货币

View File

@ -0,0 +1,19 @@
---
title: 日期时间
---
# 日期时间
`@voerkai18n/runtime`内置了对日期时间进行处理的格式化器,可以直接使用,不需要额外的安装。
```javascript | pure
// 切换到中文
t("现在是{d | date}",new Date()) // == 现在是2022年3月12日
t("现在是{d | time}",new Date()) // == 现在是18点28分12秒
t("现在是{d | shorttime}",new Date()) // == 现在是18:28:12
t("现在是{}",new Date()) // == 现在是2022年3月12日 18点28分12秒
// 切换到英文
t("现在是{d | date}",new Date()) // == Now is 2022/3/12
t("现在是{d | time}",new Date()) // == Now is 18:28:12
t("现在是{}",new Date()) // == Now is 2022/3/20 19:17:24'
```

View File

@ -0,0 +1,84 @@
---
title: 插值变量
---
# 插值变量
`voerkai18n``t`函数支持使用**插值变量**,用来传入一个可变内容。
插值变量有`命名插值变量``位置插值变量`
## **命名插值变量**
可以在t函数中使用`{变量名称}`表示一个命名插值变量。
```javascript | pure
t("我姓名叫{name},我今年{age}岁",{name:"tom",age:12})
// 如果值是函数会自动调用
t("我姓名叫{name},我今年{age}岁",{name:"tom",age:()=>12})
```
仅当`t`函数仅有两个参数且第2个参数是`{}`类型时,启用字典插值变量,翻译时会自动进行插值。
## 位置插值变量
可以在t函数中使用一个空的`{}`表示一个位置插值变量。
```javascript | pure
t("我姓名叫{},我今年{}岁","tom",12)
// 如果值是函数会自动调用
t("我姓名叫{},我今年{}岁","tom",()=>12})
// 如果只有两个参数且第2个参数是一个数组会自动展开
t("我姓名叫{},我今年{}岁",["tom",12])
//如果第2个参数不是{}时就启用位置插值。
t("我姓名叫{name},我今年{age}岁","tom",()=>12)
```
## 插值变量格式化
`voerka-i18n`支持强大的插值变量格式化机制,可以在插值变量中使用`{变量名称 | 格式化器名称 | 格式化器名称(...参数) | ... }`类似管道操作符的语法,将上一个输出作为下一个输入,从而实现对变量值的转换。此机制是`voerka-i18n`实现复数、货币、数字等多语言支持的基础。
我们假设定义以下格式化器(如果定义格式化器,详见后续)来进行示例。
- **UpperCase**:将字符转换为大写
- **division**对数字按每n位一个逗号分割支持一个可选参数分割位数`division(123456)===123,456``division(123456,4)===12,3456`
- **mr** : 自动添加一个先生称呼
```javascript | pure
// My name is TOM
t("My name is { name | UpperCase }",{name:"tom"})
// 我国2021年的GDP是¥14,722,730,697,890
t("我国2021年的GDP是¥{ gdp | division}",{gdp:14722730697890})
// 支持为格式化器提供参数按4位一逗号分割
// 我国2021年的GDP是¥14,7227,3069,7890
t("我国2021年的GDP是¥{ gdp | division(4)}",{gdp:14722730697890})
// 支持连续使用多个格式化器
// My name is Mr.TOM
t("My name is { name | UpperCase | mr }",{name:"tom"})
```
每个格式化器本质上是一个`(value)=>{...}`的函数,并且能**将上一个格式化器的输出作为下一个格式化器的输入**,格式化器具有如下特性:
### **无参数格式化器**
使用无参数格式化器时只需传入名称即可。例如:`My name is { name | UpperCase }`
### **有参数格式化器**
格式化器支持传入参数,如`{ gdp | division(4)}``{ date | format('yyyy/MM/DD')}`
特别需要注意的是,格式化器的参数只能支持简单的类型的参数,如`数字``布尔型``字符串`
**不支持数组、对象和函数参数,也不支持复杂的表达式参数。**
### **连续使用多个格式化器**
就如您预期的一样,**将上一个格式化器的输出作为下一个格式化器的输入**。
`data | f1 | f2 | f3(1)`等效于` f3(f2(f1(data)),1)`

View File

@ -0,0 +1,47 @@
# 名称空间
`voerkai18n `的名称空间是为了解决当源码文件非常多时,通过名称空间对翻译内容进行分类翻译的。
假设一个大型项目,其中源代码文件有上千个。默认情况下,`voerkai18n extract`会扫描所有源码文件将需要翻译的文本提取到`languages/translates/default.json`文件中。由于文件太多会导致以下问题:
- 内容太多导致`default.json`文件太大,有利于管理
- 有些翻译往往需要联系上下文才可以作出更准确的翻译,没有适当分类,不容易联系上下文。
因此,引入`名称空间`就是目的就是为了解决此问题。
配置名称空间,需要配置`languages/settings.json`文件。
```javascript | pure
// 工程目录d:/code/myapp
// languages/settings.json
module.exports = {
namespaces:{
//"名称":"相对路径"
“routes”:“routes”,
"auth":"core/auth",
"admin":"views/admin"
}
}
```
以上例子代表:
- 将`d:\code\myapp\routes`中扫描到的文本提取到`routes.json`中。
- 将`d:\code\myapp\auth`中扫描到的文本提取到`auth.json`中。
- 将`d:\code\myapp\views/admin`中扫描到的文本提取到`admin.json`中。
最终在` languages/translates`中会包括:
```shell
languages
|-- translates
|-- default.json
|-- routes.sjon
|-- auth.json
|-- admin.json
```
然后,`voerkai18n compile`在编译时会自动合并这些文件,后续就不再需要名称空间的概念了。
`名称空间`仅仅是为了解决当翻译内容太多时的分类问题。

View File

@ -0,0 +1,112 @@
---
title: 复数
---
# 复数
当翻译文本内容是一个`数组`时启用复数处理机制。即在`langauges/tranclates/*.json`中的文本翻译项是一个数组。
## 启用复数处理机制
假设在`index.html`文件中具有一个翻译内容
```javascript | pure
t("我{}一辆车")
```
经过`extract`命令提取为翻译文件后,如下:
```json
// languages/translates/default.json
{
"我有{}辆车":{
"en":"",
"de":"...."
}
}
```
现在我们要求引入复数处理机制,为不同数量采用不同的翻译,只需要将上述翻译文本更改为数组形式。
```json
{
"我有{}辆车":{
"en":["I don't have car","I have a car","I have two cars","I have {} cars"],
"en":["I don't have car","I have a car","I have {} cars"],
"en":["I don't have car","I have {} cars"],
"de":"...."
}
}
```
上例中,只需要在翻译文件中将上述的`en:""`更改为`[<0对应的复数文本>,<1对应的复数文本>,...,<n对应的复数文本>]`形式代表启动复数机制.
- 可以灵活地为每一个数字(`0、1、2、...、n`)对应的复数形式进行翻译
- 数量数字大于数组长度,则总是取最后一个复数形式
- 复数形式的文本同样支持位置插值和变量插值。
## 对应的翻译函数
启用复数处理机制后,在`t`函数根据变量值来决定采用单数还是复数,按如下规则进行处理。
- **不存在插值变量且t函数的第2个参数是数字**
```javascript | pure
t("我有一辆车",0) // == "I don't have a car"
t("我有一辆车",1) // == "I have a car"
t("我有一辆车",2) // == "I have two cars"
t("我有一辆车",100) // == "I have 100 cars"
```
- **存在插值变量且t函数的第2个参数是数字**
就中文而言,上述没有指定插值变量是比较别扭的,一般可以引入一个位置插值变量更加友好。
```javascript | pure
t("我有{}辆车",0) // == "I don't have a car"
t("我有{}辆车",1) // == "I have a car"
t("我有{}辆车",2) // == "I have two cars"
t("我有{}辆车",100) // == "I have 100 cars"
```
- **复数命名插值变量**
当启用复数功能时,`t`函数需要知道根据哪个变量来决定采用何种复数形式。
**当采用位置变量插值时,`t`函数取第一个数字类型参数作为位置插值复数。**
```javascript | pure
t("{}有{}辆车","张三",0)
```
**当采用命名变量插值时,`t`函数约定当插值字典中存在以`$字符开头`的变量时,并且值是`数字`时,根据该变量来引用复数。**
下例中,`t`函数根据`$count`值来处理复数。
```javascript | pure
t("{name}有{$count}辆车",{name:"张三",$count:1})
```
## **示例**
```javascript | pure
// languages/translates/default.json
{
"第{}章":{
en:[
"Chapter Zero","Chapter One", "Chapter Two", "Chapter Three","Chapter Four",
"Chapter Five","Chapter Six","Chapter Seven","Chapter Eight","Chapter Nine",
"Chapter {}"
],
zh:["起始","第一章", "第二章", "第三章","第四章","第五章","第六章","第七章","第八章","第九章",“第{}章”]
}
}
// 翻译函数
t("第{}章",0) // == Chapter Zero
t("第{}章",1) // == Chapter One
t("第{}章",2) // == Chapter Two
t("第{}章",3) // == Chapter Three
t("第{}章",4) // == Chapter Four
t("第{}章",5) // == Chapter Five
t("第{}章",6) // == Chapter Six
t("第{}章",7) // == Chapter Seven
...
// 超过取最后一项
t("第{}章",100) // == Chapter 100
```

View File

@ -0,0 +1,74 @@
# React应用
开发`React`应用一般可以采用`create-react-app``Vite+"@vitejs/plugin-react`工具来创建工程。
本节介绍如何为`Vite`+`@vitejs/plugin-react`创建的工程添加`voerkai18n`支持。
## 第一步:引入
```javascript | pure
// 初始化工程
> voerka18n init
// 提取要翻译的文本到src/languages/translates/*.json
> voerkai18n extract
// 进行人工翻译或自动翻译(百度)
> voerkai18n translate --apikey xxxx --apiid xxxxx
// 编译语言包
> voerkai18n compile
```
## 第二步:导入`t`翻译函数
无论采用何种工具创建`React`应用,均可以直接从`languages`直接导入`t`函数。
```javascript | pure
import { t } from "./languages"
```
取决于您是从哪一个文件中导入,需要修改导入位置,可能类似这样:
```javascript | pure
import { t } from "./languages"
import { t } from "../languages"
import { t } from "../../languages"
import { t } from "../../../languages"
```
导入`t`函数后就可以直接使用了。
## 第三步:自动导入`t`翻译函数
当源码文件非常多时,手动导入`t`函数比较麻烦,我们提供了`vite``babel`两个插件可以实现自动导入`t`函数。
如果应用是采用`Vite`+`@vitejs/plugin-react`创建的工程,则可以通过配置`@voerkai18n/vite`插件实现自动导入`t`函数。
详见`@voerkai18n/vite`插件介绍。
## 第四步:切换语言
最后,一般需要在应用中提供切换语言并自动重新渲染界面的功能。针对`React`应用,提供了`useVoerkaI18n`来实现此功能。
```jsx | pure
// 如果没有在vite.config.js中配置`@voerkai18n/vite`插件则需要手工导入t函数
import { t } from "./languages"
import { useVoerkaI18n } from "@voerkai18n/react"
export default App(){
const { activeLanguage,changeLanguage,languages } = useVoerkaI18n()
return (<div>
<h1>{t("当前语言")}:{activeLanguage}</h1>
<div> {
languages.map(lang=>{
return (<button
key={lang.name}
onclick={()=>changeLanguage(lang.name)}>
{lang.title}
</button>)
})}
</div>
</div> )
```
## 小结
- `useVoerkaI18n`返回当前激活语言、切换语言函数、支持的语言列表。
- 如果需要在切换语言时进行全局重新渲染,一般需要在顶层`App组件`中使用此`hook`, 这样可以确保在切换语言时整个应用进行重新渲染。
- 一般切换语言的功能界面不会直接在`App组件`中使用,您可以使用一个专门的组件来切换语言。

40
docs/src/guide/use/t.md Normal file
View File

@ -0,0 +1,40 @@
---
title: 翻译函数
---
# 翻译函数
默认提供翻译函数`t`用来进行翻译。一般情况下,`t`函数声明在执行`voerkai18n compile`命令生成在工程目录下的`languages/index.js`文件中。
```javascript | pure
// 从当前语言包文件夹index.js中导入翻译函数
import { t } from "<myapp>/languages"
// 不含插值变量
t("中华人民共和国")
// 位置插值变量
t("中华人民共和国{}","万岁")
t("中华人民共和国成立于{}年,首都{}",1949,"北京")
// 当仅有两个参数且第2个参数是[]类型时,自动展开第一个参数进行位置插值
t("中华人民共和国成立于{year}年,首都{capital}",[1949,"北京"])
// 当仅有两个参数且第2个参数是{}类型时,启用字典插值变量
t("中华人民共和国成立于{year}年,首都{capital}",{year:1949,capital:"北京"})
// 插值变量可以是同步函数,在进行插值时自动调用。
t("中华人民共和国成立于{year}年,首都{capital}",()=>1949,"北京")
// 对插值变量启用格式化器
t("中华人民共和国成立于{birthday | year}年",{birthday:new Date()})
```
**特别注意:**
- 不要使用js的模板字符串来生成翻译内容如t(`我是中国人,我喜欢%{car}`)是无效的。
- `voerkai18n`使用正则表达式来提取要翻译的内容,因此`t("")`可以使用在任意地方。

135
docs/src/guide/use/vue.md Normal file
View File

@ -0,0 +1,135 @@
# Vue应用
创建`Vue`应用可以采用`Vite``Vue Cli`来创建工程。
`Vue3`应用中引入`voerkai18n`来添加国际化应用需要由两个插件来简化应用。
- **@voerkai18n/vue**
**Vue插件**,在初始化`Vue`应用时引入,提供访问`当前语言``切换语言``自动更新`等功能。
- **@voerkai18n/vite**
**Vite插件**,在`vite.config.js`中配置,用来实现`自动文本映射``自动导入t函数`等功能。
`@voerkai18n/vue``@voerkai18n/vite`两件插件相互配合,安装配置好这两个插件后,就可以在`Vue`文件使用多语言`t`函数。
以下介绍当采用`Vite`创建应用时,如何引入`Voerkai18n`
## 第一步:引入
```javascript | pure
// 初始化工程
> voerka18n init
// 提取要翻译的文本到src/languages/translates/*.json
> voerkai18n extract
// 进行人工翻译或自动翻译(百度)
> voerkai18n translate --apikey xxxx --apiid xxxxx
// 编译语言包
> voerkai18n compile
```
## 第二步:导入`t`翻译函数
无论采用何种工具创建`Vite`应用,均可以直接从`languages`直接导入`t`函数。
```vue
<script setup>
import { t } from "./languages"
</script>
```
取决于您是从哪一个文件中导入,需要修改导入位置,可能类似这样:
```javascript | pure
import { t } from "./languages"
import { t } from "../languages"
import { t } from "../../languages"
import { t } from "../../../languages"
```
导入`t`函数后就可以直接使用了。
```vue
<Script setup>
// 如果没有在vite.config.js中配置`@voerkai18n/vite`插件则需要手工导入t函数
// import { t } from "./languages"
</Script>
<script>
export default {
data(){
return {
username:"",
password:"",
title:t("认证")
}
},
methods:{
login(){
alert(t("登录"))
}
}
}
</script>
<template>
<div>
<h1>{{ t("请输入用户名称") }}</h1>
<div>
<span>{{t("用户名:")}}</span><input type="text" :placeholder="t('邮件/手机号码/帐号')"/>
<span>{{t("密码:")}}</span><input type="password" :placeholder="t('至少6位的密码')"/>
</div>
</div>
<button @click="login">{{t("登录")}}</button>
</div>
</template>
```
## 第三步:自动导入`t`翻译函数
当源码文件非常多时,手动导入`t`函数比较麻烦,我们提供了`vite``babel`两个插件可以实现自动导入`t`函数。
如果应用是采用`Vite`+`@vitejs/plugin-vite`创建的工程,则可以通过配置`@voerkai18n/vite`插件实现自动导入`t`函数。
详见`@voerkai18n/vite`插件介绍。
**重点:`t`函数会在使用`@voerkai18n/vite`插件后自动注入,因此在`Vue`文件中可以直接使用。**
## 第四步:切换语言
引入`@voerkai18n/vue`插件来实现切换语言和自动重新渲染的功能。
```javascript | pure
import { createApp } from 'vue'
import Root from './App.vue'
import i18nPlugin from '@voerkai18n/vue'
import { i18nScope } from './languages'
const app = createApp(Root)
app.use(i18nPlugin,{ i18nScope }) // 重点需要引入i18nScope
app.mount('#app')
```
`@voerkai18n/vue`插件安装后,提供了一个`i18n`实例,可以在组件中进行`inject`。就可以按如下方式使用:
```vue
<script>
export default {
inject: ['i18n'] // 此值由`@voerkai18n/vue`插件提供
}
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ t("中华人民共和国")}} </h1>
<h2>{{ t("迎接中华民族的伟大复兴")}} </h2>
<h5>默认语言:{{ i18n.defaultLanguage }}</h5>
<h5>当前语言:{{ i18n.activeLanguage.value }}</h5>
<button v-for="lng of i18n.languages" @click="i18n.activeLanguage = lng.name">{{ lng.title }}</button>
</template>
```
**说明:**
- 事实上,就算没有`@voerkai18n/vue``@voerkai18n/vite`两件插件相互配合,只需要导入`t`函数也就可以直接使用。这两个插件只是很简单的封装而已。
- 如果要在应用中进行`语言动态切换`,则需要在应用中引入`@voerkai18n/vue`,请参阅`@voerkai18n/vue`插件使用说明。
- `@voerkai18n/vite`的使用请参阅后续说明。

74
docs/src/index.md Normal file
View File

@ -0,0 +1,74 @@
---
hero:
title: VoerkaI18n
desc: 适用于Nodejs/React/Vue/Svelte/Javascript的国际化解决方案
actions:
- text: 快速入门
link: https://zhangfisher.github.io/voerka-i18n/voerkao18n/guide/intro/get-started
features:
- title: 全流程支持
icon: /images/flow.png
desc: 从文本提取/自动翻译/编译/动态切换的全流程工程化支持,适用于大型项目
link:
- title: 集成自动翻译
icon: /images/trans.png
desc: 调用在线翻译服务API支持对提取的文本进行自动翻译大幅度提高工程效率
link:
- title: 符合直觉
icon: /images/simple.png
desc: 在源码中直接使用符合直觉的翻译形式不需要绞尽脑汁想种种key
link:
- title: 自动提取文本
icon: /images/extract.png
desc: 提供扫描提取工具对源码文件中需要翻译的文本进行提取
link:
- title: 适用性
icon: /images/all.png
desc: 支持任意Javascript应用,包括Nodejs/Vue/React/ReactNative等。
link:
- title: 多库联动
icon: /images/con.png
desc: 支持多包工程下多库进行语言切换的联动
link:
- title: 工具链
icon: /images/tools.png
desc: 提供Vue/React/Babel等扩展插件简化各种应用开发
link:
- title: 插值变量
icon: /images/var.png
desc: 强大的插值变量机制,能扩展支持复数、日期、货币等灵活强大的多语言特性
link:
- title: 语言补丁
icon: /images/patch.png
desc: 在应用上线后发现错误时可以在线修复
link:
- title: 动态增加语种
icon: /images/dadd.png
desc: 可以在应用上线后动态增加语种支持
link:
- title: 一健国际化
icon: /images/edit.png
desc: 从扫描提取/编译/自动翻译/语言补丁一健国际化
link:
- title: 复数支持
icon: /images/n.png
desc: 灵活而强大的复数机制
link:
footer: Open-source MIT Licensed | Copyright © 2022<br />Powered by wxzhang
---

View File

@ -0,0 +1,3 @@
# 格式化器

View File

@ -0,0 +1,40 @@
# i18nScope
每个工程会创建一个`i18nScope`实例,并自动注册到全局单例`VoerkaI18n`
- 在多包项目中,一般每一个包均会创建自己的`i18nScope`实例。
- 当第三方库也使用voerkai18n作为多语言解决方案时引入库时也会创建该库自己的`i18nScope`实例
```javascript | pure
import { i18nScope } from "./languages"
// 订阅语言切换事件
i18nScope.on((newLanguage)=>{...})
// 取消语言切换事件订阅
i18nScope.off(callback)
// 当前语言作用域配置
i18nScope.settings
// 当前语言名称
i18nScope.activeLanguage // 如zh
// 默认语言名称
i18nScope.defaultLanguage
// 返回当前支持的语言列表,可以用来显示
i18nScope.languages // [{name:"zh",title:"中文"},{name:"en",title:"英文"},...]
// 返回当前作用域的格式化器
i18nScope.formatters
// 当前作用id
i18nScope.id
// 切换语言,异步函数
await i18nScope.change(newLanguage)
// 当前语言包
i18nScope.messages // {1:"...",2:"...","3":"..."}
// 引用全局VoerkaI18n实例
i18nScope.global
// 注册当前作用域格式化器
i18nScope.registerFormatter(name,formatter,{language:"*"})
// 注册默认的语言包加载器
i18nScope.registerDefaultLoader(async function(language,scope){....})
```
| 成员 | 类型 | 说明 |
| --- |--- |--- |

View File

@ -0,0 +1,35 @@
# 语言代码
常见的语言代码如下:
| 名称 | 代码 |
|---|:---:|
|中文| zh |
|繁体中文| cht |
|英语 |en|
|日语 |jp|
|韩语 |kor |
|法语 |fra |
|西班牙语| spa|
|泰语 |th |
|阿拉伯语| ara |
|俄语| ru|
|葡萄牙语 |pt |
|德语 |de |
|意大利语| it|
|希腊语 |el |
|荷兰语 |nl |
|波兰语 |pl|
|保加利亚语 |bul |
|爱沙尼亚语 |est |
|丹麦语 |dan|
|芬兰语 |fin |
|捷克语 |cs |
|罗马尼亚语 |rom|
|斯洛文尼亚语| slo |
|瑞典语 |swe |
|匈牙利语 |hu|
|越南语 |vie |
更完整的请参阅[这里](https://fanyi-api.baidu.com/doc/21)

View File

@ -0,0 +1,12 @@
---
---
# 参考
- 作用域实例
- 全局VoerkaI18n实例
- 格式化器
- 可用的语言代码

View File

@ -0,0 +1,27 @@
# VoerkaI18n
`import { i18nScope } form "./languages"`时会自动创建全局单例`VoerkaI18n`
```javascript | pure
// 订阅语言切换事件
VoerkaI18n.on((newLanguage)=>{...})
// 取消语言切换事件订阅
VoerkaI18n.off(callback)
// 取消所有语言切换事件订阅
VoerkaI18n.offAll()
// 返回当前默认语言
VoerkaI18n.defaultLanguage
// 返回当前激活语言
VoerkaI18n.activeLanguage
// 返回当前支持的语言
VoerkaI18n.languages
// 切换语言
await VoerkaI18n.change(newLanguage)
// 返回全局格式化器
VoerkaI18n.formatters
// 注册全局格式化器
VoerkaI18n.registerFormatter(name,formatter,{language:"*"})
```

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"jsx": "react",
"esModuleInterop": true,
"sourceMap": true,
"baseUrl": "./",
"strict": true,
"paths": {
"@/*": ["src/*"],
"@@/*": ["src/.umi/*"]
},
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules",
"lib",
"es",
"dist",
"typings",
"**/__test__",
"test",
"docs",
"tests"
]
}

2
typings.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare module '*.css';
declare module '*.less';