diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..b45c6e3 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,44 @@ + +name: 部署文档 + +on: + push: + branches: + # 确保这是你正在使用的分支名称 + - master + +jobs: + deploy-gh-pages: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + # 如果你文档需要 Git 子模块,取消注释下一行 + # submodules: true + + - uses: actions/cache@v3 + id: node-modules + with: + path: node_modules/ + key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node-modules- + + - name: 安装依赖 + if: steps.node-modules.outputs.cache-hit != 'true' + run: yarn install --frozen-lockfile + + - name: 构建文档 + env: + NODE_OPTIONS: --max_old_space_size=4096 + run: yarn run docs:build + + - name: 部署文档 + uses: JamesIves/github-pages-deploy-action@v4 + with: + # 这是文档部署到的分支名称 + branch: gh-pages + folder: docs/.vuepress/dist + diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts new file mode 100644 index 0000000..1f070cc --- /dev/null +++ b/docs/.vuepress/config.ts @@ -0,0 +1,29 @@ +import { defineHopeConfig } from "vuepress-theme-hope"; +import themeConfig from "./themeConfig"; + +export default defineHopeConfig({ + base: "/voerka-i18n/", + head: [ + [ + "link", + { + rel: "stylesheet", + href: "//at.alicdn.com/t/font_2410206_mfj6e1vbwo.css", + }, + ], + ], + locales: { + "/": { + lang: "zh-CN", + title: "VoerkaI18n", + description: "适用于Nodejs/Vue/React的国际化解决方案", + }, + "/en/": { + lang: "en-US", + title: "VoerkaI18n", + description: "适用于Nodejs/Vue/React的国际化解决方案", + } + }, + + themeConfig, +}); diff --git a/docs/.vuepress/navbar/en.ts b/docs/.vuepress/navbar/en.ts new file mode 100644 index 0000000..be1a716 --- /dev/null +++ b/docs/.vuepress/navbar/en.ts @@ -0,0 +1,7 @@ +import { defineNavbarConfig } from "vuepress-theme-hope"; + +export const en = defineNavbarConfig([ + "/en/", + "/home", + { text: "Guide", icon: "creative", link: "/guide/" } +]); diff --git a/docs/.vuepress/navbar/index.ts b/docs/.vuepress/navbar/index.ts new file mode 100644 index 0000000..67e2901 --- /dev/null +++ b/docs/.vuepress/navbar/index.ts @@ -0,0 +1,2 @@ +export * from "./en"; +export * from "./zh"; diff --git a/docs/.vuepress/navbar/zh.ts b/docs/.vuepress/navbar/zh.ts new file mode 100644 index 0000000..92bdf5a --- /dev/null +++ b/docs/.vuepress/navbar/zh.ts @@ -0,0 +1,23 @@ +import { defineNavbarConfig } from "vuepress-theme-hope"; + +export const zh = defineNavbarConfig([ + { + text: "主页", + icon: "home", + link: "/" + }, + { + text: "指南", + link: "/zh/guide/intro" + }, + { + text: "参考", + link: "/zh/reference", + }, + { + text: "贡献源码", + link: "/zh/contribute", + } +]); + + diff --git a/docs/.vuepress/public/favicon.ico b/docs/.vuepress/public/favicon.ico new file mode 100644 index 0000000..b411f79 Binary files /dev/null and b/docs/.vuepress/public/favicon.ico differ diff --git a/docs/.vuepress/public/images/arch.png b/docs/.vuepress/public/images/arch.png new file mode 100644 index 0000000..c474dfc Binary files /dev/null and b/docs/.vuepress/public/images/arch.png differ diff --git a/docs/.vuepress/public/logo.png b/docs/.vuepress/public/logo.png new file mode 100644 index 0000000..ccd732e Binary files /dev/null and b/docs/.vuepress/public/logo.png differ diff --git a/docs/.vuepress/public/logo.svg b/docs/.vuepress/public/logo.svg new file mode 100644 index 0000000..23301fd --- /dev/null +++ b/docs/.vuepress/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/.vuepress/sidebar/en.ts b/docs/.vuepress/sidebar/en.ts new file mode 100644 index 0000000..6439dba --- /dev/null +++ b/docs/.vuepress/sidebar/en.ts @@ -0,0 +1,16 @@ +import { defineSidebarConfig } from "vuepress-theme-hope"; + +export const en = defineSidebarConfig({ + "/en/": [ + "", + "home", + "slide", + { + icon: "creative", + text: "Guide", + prefix: "guide/", + link: "guide/", + children: "structure", + } + ], +}); diff --git a/docs/.vuepress/sidebar/index.ts b/docs/.vuepress/sidebar/index.ts new file mode 100644 index 0000000..67e2901 --- /dev/null +++ b/docs/.vuepress/sidebar/index.ts @@ -0,0 +1,2 @@ +export * from "./en"; +export * from "./zh"; diff --git a/docs/.vuepress/sidebar/zh.ts b/docs/.vuepress/sidebar/zh.ts new file mode 100644 index 0000000..b39d3cf --- /dev/null +++ b/docs/.vuepress/sidebar/zh.ts @@ -0,0 +1,60 @@ +import { defineSidebarConfig } from "vuepress-theme-hope"; + +export const zh = defineSidebarConfig({ + "/zh/guide/": [ + { + text:"开始", + prefix:"intro/", + children:[ + "", + "install.md", + "get-started.md", + ] + }, + { + text:"指南", + link:false, + prefix:"use/", + children:[ + "t", + "interpolation", + "datetime", + "plural", + "currency", + "namespace", + "change-langeuage", + "vue", + "react" + ] + }, + { + text:"高级特性", + prefix:"advanced/", + children:[ + "runtime", + "textMap", + "multi-libs", + "autoimport", + "customformatter", + "langpack", + "autotranslate" + ] + }, + { + text:"工具", + prefix:"tools/", + children:[ + "cli", + "babel", + "vue", + "vite", + ] + } + ], + "/zh/reference": [ + "i18nscope", + "voerkai18n", + "formatters", + "lang-code" + ] +}); diff --git a/docs/.vuepress/styles/index.scss b/docs/.vuepress/styles/index.scss new file mode 100644 index 0000000..8cdf641 --- /dev/null +++ b/docs/.vuepress/styles/index.scss @@ -0,0 +1,5 @@ + + +.sidebar-group > p.sidebar-heading > span.title { + font-weight: bold; +} \ No newline at end of file diff --git a/docs/.vuepress/styles/palette.scss b/docs/.vuepress/styles/palette.scss new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/docs/.vuepress/styles/palette.scss @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/.vuepress/themeConfig.ts b/docs/.vuepress/themeConfig.ts new file mode 100644 index 0000000..799ad72 --- /dev/null +++ b/docs/.vuepress/themeConfig.ts @@ -0,0 +1,84 @@ +import { defineThemeConfig } from "vuepress-theme-hope"; +import * as navbar from "./navbar"; +import * as sidebar from "./sidebar"; + +export default defineThemeConfig({ + hostname: "https://vuepress-theme-hope-v2-demo.mrhope.site", + author: { + name: "wxzhang", + url: "https://mrhope.site", + }, + + iconPrefix: "iconfont icon-", + + logo: "/logo.svg", + + home:"/zh/home", + repo: "vuepress-theme-hope/vuepress-theme-hope", + + docsDir: "docs", + breadcrumb :false, + + pageInfo: ["Author", "Original", "Date", "Category", "Tag", "ReadingTime"], + + locales: { + /** + * Chinese locale config + */ + "/": { + // navbar + navbar: navbar.zh, + + // sidebar + sidebar: sidebar.zh, + + //footer: "默认页脚", + + displayFooter: true + }, + "/en/": { + // navbar + navbar: navbar.en, + + // sidebar + sidebar: sidebar.en, + + footer: "Default footer", + displayFooter: true, + } + }, + plugins: { + // If you don't need comment feature, you can remove following option + // The following config is for demo ONLY, if you need comment feature, please generate and use your own config, see comment plugin documentation for details. + // To avoid disturbing the theme developer and consuming his resources, please DO NOT use the following config directly in your production environment!!!!! + // comment: { + // /** + // * Using giscus + // */ + // type: "giscus", + // repo: "vuepress-theme-hope/giscus-discussions", + // repoId: "R_kgDOG_Pt2A", + // category: "Announcements", + // categoryId: "DIC_kwDOG_Pt2M4COD69", + + // /** + // * Using twikoo + // */ + // // type: "twikoo", + // // envId: "https://twikoo.ccknbc.vercel.app", + + // /** + // * Using Waline + // */ + // // type: "waline", + // // serverURL: "https://vuepress-theme-hope-comment.vercel.app", + // }, + + mdEnhance: { + enableAll: true, + presentation: { + plugins: ["highlight", "math", "search", "notes", "zoom"], + }, + }, + }, +}); diff --git a/docs/en/guide/disable.md b/docs/en/guide/disable.md new file mode 100644 index 0000000..53857bf --- /dev/null +++ b/docs/en/guide/disable.md @@ -0,0 +1,42 @@ +--- +index: 3 +title: Component disabled +icon: config +category: + - Guide +tag: + - disable + +navbar: false +sidebar: false + +breadcrumb: false +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +prev: false +next: false +comment: false +footer: false + +backtotop: false +--- + +You can disable some functions on the page by setting the Frontmatter of the page. + + + +These should be disabled on this page: + +- Navbar +- Sidebar +- Breadcrumb +- Page information +- Contributors +- Edit link +- Update time +- Prev/Next link +- Comment +- Footer +- Back to top button diff --git a/docs/en/guide/encrypt.md b/docs/en/guide/encrypt.md new file mode 100644 index 0000000..695bfed --- /dev/null +++ b/docs/en/guide/encrypt.md @@ -0,0 +1,15 @@ +--- +icon: lock +category: + - Guide +tag: + - encryption +--- + +# Encryption article + +The actual article content. + +Paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text paragraph 1 text. + +Paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text paragraph 2 text. diff --git a/docs/en/guide/install.md b/docs/en/guide/install.md new file mode 100644 index 0000000..7d16a2d --- /dev/null +++ b/docs/en/guide/install.md @@ -0,0 +1,40 @@ + +# 安装 + +`VoerkaI18n`国际化框架是一个开源多包工程,主要由以下几个包组成: + +- **@voerkai18/cli** + + 包含文本提取/编译等命令行工具,一般应该安装到全局。 + + ```javascript + npm install --g @voerkai18/cli + yarn global add @voerkai18/cli + pnpm add -g @voerkai18/cli + ``` + +- **@voerkai18/runtime** + + **可选的**,运行时,`@voerkai18/cli`的依赖。大部分情况下不需要手动安装,一般仅在开发库项目时采用独立的运行时依赖。 + + ```javascript + npm install --save @voerkai18/runtime + yarn add @voerkai18/runtime + pnpm add @voerkai18/runtime + ``` + +- **@voerkai18/formatters** + + **可选的**,一些额外的格式化器,可以按需进行安装到`dependencies`中,用来扩展翻译时对插值变量的额外处理。 + +- **@voerkai18/babel** + + 可选的`babel`插件,用来实现自动导入翻译函数和翻译文本映射自动替换。 + +- **@voerkai18/vue** + + 可选的`vue`插件,用来为Vue应用提供语言动态切换功能。 + +- **@voerkai18/vite** + + 可选的`vite`插件,用来为`vite`应用提供自动导入翻译函数和翻译文本映射自动替换。 diff --git a/docs/en/guide/markdown.md b/docs/en/guide/markdown.md new file mode 100644 index 0000000..03780d1 --- /dev/null +++ b/docs/en/guide/markdown.md @@ -0,0 +1,341 @@ +--- +index: 2 +icon: markdown +title: Markdown Enhance +category: + - Guide +tag: + - markdown +--- + +Every document page in VuePress is rendered by Markdown. + +You need to build your document or blog page by creating and writing Markdown in the corresponding path. + + + +## Markdown introduction + +If you are a new learner and don’t know how to write Markdown, please read [Markdown Intro](https://vuepress-theme-hope.github.io/v2/basic/markdown/README.html) and [Markdown Demo](https://vuepress-theme-hope.github.io/v2/basic/markdown/demo.html). + +::: info Frontmatter + +Frontmatter is a important concept in VuePress. If you don’t know it, you need to read [Frontmatter Introduction](https://vuepress-theme-hope.github.io/v2/basic/vuepress/page.html#front-matter). + +::: + +## VuePress Enhancement + +To enrich document writing, VuePress has extended Markdown syntax. + +For these extensions, please read [Markdown extensions in VuePress](https://vuepress-theme-hope.github.io/v2/basic/vuepress/markdown.html). + +## Theme Enhancement + +### Enable all + +You can set `themeconfig.plugins.htmlEnhance.enableAll` to enable all features of the [md-enhance](https://vuepress-theme-hope.github.io/v2/md-enhance/) plugin. + +```js {3-5} +module.exports = { + themeConfig: { + plugins: { + mdEnhance: { + enableAll: true, + }, + }, + }, +}; +``` + +## New Feature + +### Custom Container + +::: v-pre + +Safely use {{ variable }} in markdown. + +::: + +::: info Custom Title + +A custom information container with `code`, [link](#markdown). + +```js +const a = 1; +``` + +::: + +::: tip Custom Title + +A custom tip container + +::: + +::: warning Custom Title + +A custom warning container + +::: + +::: danger Custom Title + +A custom danger container + +::: + +::: details Custom Title + +A custom details container + +::: + +:::: details Code + +```md +::: v-pre + +Safely use {{ variable }} in markdown. + +::: + +::: info Custom Title + +A custom information container + +::: + +::: tip Custom Title + +A custom tip container + +::: + +::: warning Custom Title + +A custom warning container + +::: + +::: danger Custom Title + +A custom danger container + +::: + +::: details Custom Title + +A custom details container + +::: +``` + +:::: + +### CodeGroup + +:::: code-group + +::: code-group-item yarn + +```bash +yarn add -D vuepress-theme-hope +``` + +::: + +::: code-group-item npm:active + +```bash +npm i -D vuepress-theme-hope +``` + +::: + +:::: + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/code-group.html) + +### Superscript and Subscript + +19^th^ H~2~O + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/sup-sub.html) + +### Align + +::: center + +I am center + +::: + +::: right + +I am right align + +::: + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/align.html) + +### Footnote + +This text has footnote[^first]. + +[^first]: This is footnote content + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/footnote.html) + +### Mark + +You can mark ==important words== . + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/mark.html) + +### Tasklist + +- [x] Plan A +- [ ] Plan B + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/tasklist.html) + +### Chart + +::: chart A Scatter Chart + +```json +{ + "type": "scatter", + "data": { + "datasets": [ + { + "label": "Scatter Dataset", + "data": [ + { "x": -10, "y": 0 }, + { "x": 0, "y": 10 }, + { "x": 10, "y": 5 }, + { "x": 0.5, "y": 5.5 } + ], + "backgroundColor": "rgb(255, 99, 132)" + } + ] + }, + "options": { + "scales": { + "x": { + "type": "linear", + "position": "bottom" + } + } + } +} +``` + +::: + +- [View Detail](<[chart.md](https://vuepress-theme-hope.github.io/v2/guide/markdown/chart.html)>) + +### Flowchart + +```flow +cond=>condition: Process? +process=>operation: Process +e=>end: End + +cond(yes)->process->e +cond(no)->e +``` + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/flowchart.html) + +### Mermaid + +```mermaid +flowchart TB + c1-->a2 + subgraph one + a1-->a2 + end + subgraph two + b1-->b2 + end + subgraph three + c1-->c2 + end + one --> two + three --> two + two --> c2 +``` + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/mermaid.html) + +### Tex + +$$ +\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right) += \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^i r \cdots (r-i+1) (\log y)^{r-i}} {\omega^i} \right\} +$$ + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/tex.html) + +### Code Demo + +::: demo A normal demo + +```html +

VuePress Theme Hope

+

Is very powerful!

+``` + +```js +document.querySelector("#very").addEventListener("click", () => { + alert("Very powerful!"); +}); +``` + +```css +span { + color: red; +} +``` + +::: + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/demo.html) + +### Presentation + +@slidestart + +## Slide 1 + +A paragraph with some text and a [link](https://mrhope.site) + +--- + +## Slide 2 + +- Item 1 +- Item 2 + +--- + +## Slide 3.1 + +```js +const a = 1; +``` + +-- + +## Slide 3.2 + +$$ +J(\theta_0,\theta_1) = \sum_{i=0} +$$ + +@slideend + +- [View Detail](https://vuepress-theme-hope.github.io/v2/guide/markdown/presentation.html) diff --git a/docs/en/guide/page.md b/docs/en/guide/page.md new file mode 100644 index 0000000..90b4ae2 --- /dev/null +++ b/docs/en/guide/page.md @@ -0,0 +1,66 @@ +--- +# This control sidebar index +index: 1 +# This is the icon of the page +icon: page +# This is the title of the article +title: page config +# Set author +author: Ms.Hope +# Set writing time +date: 2020-01-01 +# A page can have multiple categories +category: + - Guide +# A page can have multiple tags +tag: + - Page config + - Guide +# this page is sticky in article list +sticky: true +# this page will appear in aricle channel in home page +star: true +# You can customize the footer +footer: Footer content for test +--- + +Content before `more` comment is regarded as page excerpt. + + + +## Page information + +You can set page information in Markdown’s Frontmatter. + +- The author is set to Ms.Hope. + +- The writing time should be January 1, 2020 + +- Category is "Guide" + +- Tags are "Page Config" and "Guide" + +## Page content + +You are free to write your Markdown here. + +::: tip + +- Please use the relative link `./` for pictures in the Markdown folder. + +- For pictures in `.vuepress/public` folder, please use absolute link `/` for reference + +::: + +The theme contains a custom badge: + +> A dark blue badge text badge at the end of line. + +## Page structure + +This page should contain: + +- Back to top button +- Route navigation +- Comments +- Footer diff --git a/docs/en/guide/readme.md b/docs/en/guide/readme.md new file mode 100644 index 0000000..988f87c --- /dev/null +++ b/docs/en/guide/readme.md @@ -0,0 +1,82 @@ +--- +headerDepth: 3 +--- + +# 前言 + +基于`javascript`的国际化方案很多,比较有名的有`fbt`、`i18next`、`react-i18next`、`vue-i18n`、`react-intl`等等,每一种解决方案均有大量的用户。为什么还要再造一个轮子?好吧,再造轮子的理由不外乎不满足于现有方案,总想着现有方案的种种不足之处,然后就撸起袖子想造一个轮子,也不想想自己什么水平。 + +哪么到底是对现有解决方案有什么不满?最主要有三点: + +- 大部份均为要翻译的文本信息指定一个`key`,然后在源码文件中使用形如`$t("message.login")`之类的方式,然后在翻译时将之转换成最终的文本信息。此方式最大的问题是,在源码中必须人为地指定每一个`key`,在中文语境中,想为每一句中文均配套想一句符合语义的`英文key`是比较麻烦的,也很不直观不符合直觉。我希望在源文件中就直接使用中文,如`t("中华人民共和国万岁")`,然后国际化框架应该能自动处理后续的一系列麻烦。 + +- 要能够比较友好地支持多库多包`monorepo`场景下的国际化协作,当主程序切换语言时,其他包或库也可以自动切换,并且在开发上每个包或库均可以独立地进行开发,集成到主程序时能无缝集成。这点在现有方案上没有找到比较理想的解决方案。 + +- 大部份国际化框架均将中文视为二等公民,大部份情况下您应该采用英文作为第一语言,虽然这不是太大的问题,但是既然要再造一个轮子,为什么不将中文提升到一等公民呢。 + + + + 基于此就开始造出`VoerkaI18n`这个**全新的国际化多语言解决方案**,主要特性包括: + +# 主要特性 + +- 全面工程化解决方案,提供初始化、提取文本、自动翻译、编译等工具链支持。 + +- 符合直觉,不需要手动定义文本`Key`映射。 + +- 强大的插值变量`格式化器`机制,可以扩展出强大的多语言特性。 + +- 支持`babel`插件自动导入`t`翻译函数。 + +- 支持`nodejs`、浏览器(`vue`/`react`)前端环境。 + +- 采用`工具链`与`运行时`分开设计,发布时只需要集成很小的运行时。 + +- 高度可扩展的`复数`、`货币`、`数字`等常用的多语言处理机制。 + +- 翻译过程内,提取文本可以自动进行同步,并保留已翻译的内容。 + +- 可以随时添加支持的语言 + +- 支持调用在线自动翻译对提取文本进行翻译。 + + +# 安装 + +`VoerkaI18n`国际化框架是一个开源多包工程,主要由以下几个包组成: + +- **@voerkai18/cli** + + 包含文本提取/编译等命令行工具,一般应该安装到全局。 + + ```javascript + npm install --g @voerkai18/cli + yarn global add @voerkai18/cli + pnpm add -g @voerkai18/cli + ``` + +- **@voerkai18/runtime** + + **可选的**,运行时,`@voerkai18/cli`的依赖。大部分情况下不需要手动安装,一般仅在开发库项目时采用独立的运行时依赖。 + + ```javascript + npm install --save @voerkai18/runtime + yarn add @voerkai18/runtime + pnpm add @voerkai18/runtime + ``` + +- **@voerkai18/formatters** + + **可选的**,一些额外的格式化器,可以按需进行安装到`dependencies`中,用来扩展翻译时对插值变量的额外处理。 + +- **@voerkai18/babel** + + 可选的`babel`插件,用来实现自动导入翻译函数和翻译文本映射自动替换。 + +- **@voerkai18/vue** + + 可选的`vue`插件,用来为Vue应用提供语言动态切换功能。 + +- **@voerkai18/vite** + + 可选的`vite`插件,用来为`vite`应用提供自动导入翻译函数和翻译文本映射自动替换。 diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..8e3ae11 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,60 @@ +--- +home: true +icon: home +title: 主页 +heroImage: /logo.svg +heroText: VoerkaI18n +tagline: 适用于Nodejs/Vue/React的国际化解决方案 +actions: + - text: 快速入门 + link: /zh/guide/intro/get-started + + - text: 源码 + link: https://gitee.com/zhangfisher/voerka-i18n + type: secondary + +features: + - title: 工程化支持 + icon: markdown + details: 从文本提取/自动翻译/编译/动态切换的全流程工程化支持,适用于大型项目 + link: + + - title: 集成自动翻译 + icon: slides + details: 调用在线翻译服务API支持对提取的文本进行自动翻译,大幅度提高工程效率 + link: + + - title: 符合直觉 + icon: layout + details: 在源码中直接使用符合直觉的翻译形式,不需要绞尽脑汁想种种key + link: + + - title: 自动提取文本 + icon: comment + details: 提供扫描提取工具对源码文件中需要翻译的文本进行提取 + link: + + - title: 适用性 + icon: info + details: 支持任意Javascript应用,包括Nodejs/Vue/React/ReactNative等。 + link: + + - title: 多库协作 + icon: blog + details: 支持monorepo工程下多库进行语言切换的联动机制 + link: + + - title: 自动扩展工具 + icon: palette + details: 提供Vue/React/Babel等扩展插件,简化各种应用下 + link: + + - title: 扩展特性 + icon: contrast + details: 强大的插值变量机制,能扩展支持复数、日期、货币等灵活强大的多语言机制 + link: + + +footer: MIT Licensed | Copyright © 2022-present wxzhang +--- + \ No newline at end of file diff --git a/docs/zh/contribute/readme.md b/docs/zh/contribute/readme.md new file mode 100644 index 0000000..12ec3d6 --- /dev/null +++ b/docs/zh/contribute/readme.md @@ -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 +> npm install -g pnpm +``` + +然后再使用`pnpm install` + +## 源码结构 + +```javascript +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) + +## 开发格式化器 + + + +## 单元测试 + + + +## 文档 + + + + +## 发布 + + + + + + + diff --git a/docs/zh/guide/advanced/autoimport.md b/docs/zh/guide/advanced/autoimport.md new file mode 100644 index 0000000..c0d3252 --- /dev/null +++ b/docs/zh/guide/advanced/autoimport.md @@ -0,0 +1,25 @@ +# 自动导入翻译函数 + +使用`voerkai18 compile`后,要进行翻译时需要从`./languages`导入`t`翻译函数。 + +```javascript +import { t } from "./languages" +``` + +由于默认情况下,`voerkai18 compile`命令会在当前工程的`/languages`文件夹下,这样我们为了导入`t`翻译函数不得不使用各种相对引用,这即容易出错,又不美观,如下: + +```javascript +import { t } from "./languages" +import { t } from "../languages" +import { t } from "../../languages" +import { t } from "../../../languages" +``` + +作为国际化解决方案,一般工程的大部份源码中均会使用到翻译函数,这种使用体验比较差。 + +为此,我们提供了一个几个插件可以来自动完成翻译函数的自动引入,包括: + +- `babel`插件 +- `vite`插件 + +关于插件如何使用请参阅文档。 \ No newline at end of file diff --git a/docs/zh/guide/advanced/autotranslate.md b/docs/zh/guide/advanced/autotranslate.md new file mode 100644 index 0000000..447c580 --- /dev/null +++ b/docs/zh/guide/advanced/autotranslate.md @@ -0,0 +1,9 @@ +# 自动翻译 + +传统的国际化解决方案均是需要手工进行翻译的,`voerkai18n`解决方案支持调用在线翻译服务进行自动翻译。 + +- 内置的`voerkai18n translate`命令能调用在线翻译服务完成对提取的文本的自动翻译。 + +- 目前支持访问百度在线API进行自动翻译。百度提供了免费的在线API,虽然只支持`QPS=1`,即每秒调用一次。但是`voerkai18n translate`命令会对要翻译的文本进行合并后再调用,因此大部分情况下,均足够使用了。 + +`voerkai18n translate`命令的使用请参阅扩展文档。 diff --git a/docs/zh/guide/advanced/customformatter.md b/docs/zh/guide/advanced/customformatter.md new file mode 100644 index 0000000..b3be29b --- /dev/null +++ b/docs/zh/guide/advanced/customformatter.md @@ -0,0 +1,197 @@ +# 自定义格式化器 + + +当我们使用`voerkai18n compile`编译后,会生成`languages/formatters.js`文件,可以在该文件中自定义您自己的格式化器。 + +`formatters.js`文件内容如下: + +```javascript +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 +//formatters.js + +module.exports = { + // 在所有语言下只作用于特定数据类型的格式化器 + $types:{ + Boolean:(value)=> value ? "ON" : "OFF" + } +} +t("灯状态:{status}",true) // === 灯状态:ON +t("灯状态:{status}",false) // === 灯状态:OFF +``` + +在上例中,如果我们想在不同的语言环境下,翻译为不同的显示文本,则可以为不同的语言指定类型格式化器 + +```javascript +//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 + module.exports = { + "*":{ + $types:{...} + }, + zh:{ + $types:{...} + }, + en:{ + $types:{....} + } + } + ``` + + 在匹配应用格式化时会先在当前语言的`$types`中查找匹配的格式化器,如果找不到再上`*.$types`中查找。 + +- `*.$types`代表当所有语言中均没有定义时才匹配的类型格式化。 + +- 类型格式化器是**默认执行的,不需要指定名称**。 + +- 当前作用域的格式化器优先于全局的格式化器。 + +## 通用的格式化器 + +类型格式化器只针对特定数据类型,并且会默认调用。而通用的格式化器需要使用`|`管道符进行显式调用。 + +同样的,通用的格式化器定义在`languages/formatters.js`中。 + +```javascript +module.exports = { + "*":{ + $types:{...}, + [格式化名称]:(value)=>{.....}, + }, + zh:{ + $types:{...}, + [格式化名称]:(value)=>{.....}, + }, + en:{ + $types:{....}, + [格式化名称]:(value)=>{.....}, + [格式化名称]:(value,arg)=>{.....}, + } +} +``` + +每一个格式化器均需要指定一个名称,在进行插值替换时会优先依据当前语言来匹配查找格式化器,如果找不到,再到键名为`*`中查找。 + +```javascript +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`项目用来包含了更多的格式化器。 + +作为开源项目,欢迎大家提交贡献更多的格式化器。 diff --git a/docs/zh/guide/advanced/langpack.md b/docs/zh/guide/advanced/langpack.md new file mode 100644 index 0000000..473613e --- /dev/null +++ b/docs/zh/guide/advanced/langpack.md @@ -0,0 +1,22 @@ +# 语言包 + +当使用`webpack`、`rollup`、`esbuild`进行项目打包时,默认语言包采用静态加载,会被打包进行源码中,而其他语言则采用异步打包方式。在`languages/index.js`中。 + +```javascript +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") + }) +``` + +利用异步打包机制,从而避免将多个语言静态打包到源码包。 \ No newline at end of file diff --git a/docs/zh/guide/advanced/multi-libs.md b/docs/zh/guide/advanced/multi-libs.md new file mode 100644 index 0000000..8dbb7ae --- /dev/null +++ b/docs/zh/guide/advanced/multi-libs.md @@ -0,0 +1,14 @@ +# 多库联动 + +`voerkai18n `支持多个库国际化的联动和协作,即**当主程序切换语言时,所有引用依赖库也会跟随主程序进行语言切换**,整个切换过程对所有库开发都是透明的。 + +![结构图](/images/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`即独立,又可以联动语言切换。 diff --git a/docs/zh/guide/advanced/runtime.md b/docs/zh/guide/advanced/runtime.md new file mode 100644 index 0000000..a0fb5b4 --- /dev/null +++ b/docs/zh/guide/advanced/runtime.md @@ -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`文件。 + + + + diff --git a/docs/zh/guide/advanced/textMap.md b/docs/zh/guide/advanced/textMap.md new file mode 100644 index 0000000..6ae4e49 --- /dev/null +++ b/docs/zh/guide/advanced/textMap.md @@ -0,0 +1,45 @@ +# 文本映射 + +虽然`VoerkaI18n`推荐采用`t("中华人民共和国万岁")`形式的符合直觉的翻译形式,而不是采用`t("xxxx.xxx")`这样不符合直觉的形式,但是为什么大部份的国际化方案均采用`t("xxxx.xxx")`形式? + +在我们的方案中,t("中华人民共和国万岁")形式相当于采用原始文本进行查表,语言名形式如下: + +```javascript +// en.js +{ + "中华人民共和国":"the people's Republic of China" +} +// jp.js +{ + "中华人民共和国":"中華人民共和国" +} +``` + +很显然,直接使用文本内容作为`key`,虽然符合直觉,但是会造成大量的冗余信息。因此,`voerkai18n compile`会将之编译成如下: + +```javascript +//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`等编译区插件,还是可以正常工作,但是会有一份默认语言的冗余信息存在。 \ No newline at end of file diff --git a/docs/zh/guide/intro/get-started.md b/docs/zh/guide/intro/get-started.md new file mode 100644 index 0000000..7796c7a --- /dev/null +++ b/docs/zh/guide/intro/get-started.md @@ -0,0 +1,223 @@ +--- +title: 快速入门 +--- + + +# 快速入门 + + +本节以标准的`Nodejs`应用程序为例,简要介绍`VoerkaI18n`国际化框架的基本使用。其他`vue`或`react`应用的使用也基本相同。 + +```shell +myapp + |--package.json + |--index.js +``` + +在本项目的所有支持的源码文件中均可以使用`t`函数对要翻译的文本进行包装,简单而粗暴。 + +```javascript +// 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 +> 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 +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 +>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 + |-- 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 +import { t } from "./languages" +``` + +因此,我们需要在需要进行翻译时导入该函数即可。 + +但是如果源码文件很多,重次重复导入`t`函数也是比较麻烦的,所以我们也提供了一个`babel/vite`等插件来自动导入`t`函数。 + +## 第六步:切换语言 + +当需要切换语言时,可以通过调用`change`方法来切换语言。 + +```javascript +import { i18nScope } from "./languages" + +// 切换到英文 +await i18nScope.change("en") +// VoerkaI18n是一个全局单例,可以直接访问 +await VoerkaI18n.change("en") +``` + +`i18nScope.change`与`VoerkaI18n.change`两者是等价的。 + +一般可能也需要在语言切换后进行界面更新渲染,可以订阅事件来响应语言切换。 + +```javascript +import { i18nScope } from "./languages" + +// 切换到英文 +i18nScope.on((newLanguage)=>{ + ... +}) +// +VoerkaI18n.on((newLanguage)=>{ + ... +}) +``` diff --git a/docs/zh/guide/intro/history.md b/docs/zh/guide/intro/history.md new file mode 100644 index 0000000..29ff8e2 --- /dev/null +++ b/docs/zh/guide/intro/history.md @@ -0,0 +1,3 @@ +--- +title: 版本历史 +--- \ No newline at end of file diff --git a/docs/zh/guide/intro/install.md b/docs/zh/guide/intro/install.md new file mode 100644 index 0000000..e1df446 --- /dev/null +++ b/docs/zh/guide/intro/install.md @@ -0,0 +1,43 @@ +--- +title: 安装 +--- + +# 安装 + +`VoerkaI18n`国际化框架是一个开源多包工程,主要由以下几个包组成: + +## **@voerkai18/cli** + +包含文本提取/编译等命令行工具,一般应该安装到全局。 + +```javascript +npm install --g @voerkai18/cli +yarn global add @voerkai18/cli +pnpm add -g @voerkai18/cli +``` + +## **@voerkai18/runtime** + +**可选的**,运行时,`@voerkai18/cli`的依赖。大部分情况下不需要手动安装,一般仅在开发库项目时采用独立的运行时依赖。 + +```javascript +npm install --save @voerkai18/runtime +yarn add @voerkai18/runtime +pnpm add @voerkai18/runtime +``` + +## **@voerkai18/formatters** + +**可选的**,一些额外的格式化器,可以按需进行安装到`dependencies`中,用来扩展翻译时对插值变量的额外处理。 + +## **@voerkai18/babel** + +可选的`babel`插件,用来实现自动导入翻译函数和翻译文本映射自动替换。 + +## **@voerkai18/vue** + +可选的`vue`插件,用来为Vue应用提供语言动态切换功能。 + +## **@voerkai18/vite** + +可选的`vite`插件,用来为`vite`应用提供自动导入翻译函数和翻译文本映射自动替换。 diff --git a/docs/zh/guide/intro/question.md b/docs/zh/guide/intro/question.md new file mode 100644 index 0000000..4737654 --- /dev/null +++ b/docs/zh/guide/intro/question.md @@ -0,0 +1,3 @@ +--- +title: 常见问题 +--- \ No newline at end of file diff --git a/docs/zh/guide/intro/readme.md b/docs/zh/guide/intro/readme.md new file mode 100644 index 0000000..fb98e2b --- /dev/null +++ b/docs/zh/guide/intro/readme.md @@ -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`)前端环境。 + +- 采用`工具链`与`运行时`分开设计,发布时只需要集成很小的运行时。 + +- 高度可扩展的`复数`、`货币`、`数字`等常用的多语言处理机制。 + +- 翻译过程内,提取文本可以自动进行同步,并保留已翻译的内容。 + +- 可以随时添加支持的语言 + +- 支持调用在线自动翻译对提取文本进行翻译。 + diff --git a/docs/zh/guide/tools/babel.md b/docs/zh/guide/tools/babel.md new file mode 100644 index 0000000..5780af9 --- /dev/null +++ b/docs/zh/guide/tools/babel.md @@ -0,0 +1,73 @@ +# Babel插件 + +全局安装`@voerkai18n/babel`插件用来进行自动导入`t`函数和自动文本映射。 + +## 安装 + +```javascript +> npm install -g @voerkai18n/babel +> yarn global add @voerkai18n/babel +> pnpm add -g @voerkai18n/babel +``` + +## 启用插件 + +使用方法如下: + +- 在`babel.config.js`中配置插件 + +```javascript +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 + module.expors = { + plugins: [ + [ + "module-resolver", + { + root:"./", + alias:{ + "languages":"./src/languages" + } + } + ] + ] + } + ``` + + 这样配置`autoImport="languages"`,则自动导入`import { t } from "languages"`。 + + 如`webpack`、`rollup`等打包工具也有类似的插件可以实现别名等转换,其目的就是让`@voerkai18n/babel`插件能自动导入固定路径,而不是各种复杂的相对路径。 + \ No newline at end of file diff --git a/docs/zh/guide/tools/cli.md b/docs/zh/guide/tools/cli.md new file mode 100644 index 0000000..f13f139 --- /dev/null +++ b/docs/zh/guide/tools/cli.md @@ -0,0 +1,251 @@ +# 命令行工具 + +`@voerkai18n/cli`命令行工具用来实现工程初始化、扫描提取文本、自动翻译和编译语言等功能。 + +::: info +建议将`@voerkai18n/cli`命令行工具安装在全局 +::: + +## 安装 + +全局安装`@voerkai18n/cli`工具。 + +```javascript +> npm install -g @voerkai18n/cli +> yarn global add @voerkai18n/cli +> pnpm add -g @voerkai18n/cli +``` + +然后就可以执行: + +```javascript +> voerkai18n init +> voerkai18n extract +> voerkai18n compile +``` + +如果没有全局安装,则需要: + +```javascript +> 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 支持的语言列表 (default: ["zh","en"]) + -d, --defaultLanguage 默认语言 + -a, --activeLanguage 激活语言 + -h, --help display help for command +``` + +**使用方法如下:** + +首先需要在工程文件下运行`voerkai18n init`命令对当前工程进行初始化。 + +```javascript +//- `lngs`参数用来指定拟支持的语言名称列表 +> voerkai18n init . -lngs zh en jp de -d zh +``` + +运行`voerkai18n init`命令后,会在当前工程中创建相应配置文件。 + +```javascript +myapp + |-- languages + |-- settings.json // 语言配置文件 + |-- package.json + |-- index.js +``` + +`settings.json`文件很简单,主要是用来配置要支持的语言等基本信息。 + +```javascript +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 +扫描并提取所有待翻译的字符串到文件夹中 + +Arguments: + location 工程项目所在目录 (default: "./") + +Options: + -D, --debug 输出调试信息 + -lngs, --languages 支持的语言 + -d, --defaultLanguage 默认语言 + -a, --activeLanguage 激活语言 + -ns, --namespaces 翻译名称空间 + -e, --exclude 排除要扫描的文件夹,多个用逗号分隔 + -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 在线翻译服务提供者名称或翻译脚本文件 (default: "baidu") + -m, --max-package-size 将多个文本合并提交的最大包字节数 (default: 3000) + --appkey [key] API密钥 + --appid [id] API ID + --no-backup 备份原始文件 + --mode 翻译模式,取值auto=仅翻译未翻译的,full=全部翻译 + -q, --qps 翻译速度限制,即每秒可调用的API次数 (default: 1) + -h, --help 显示帮助 +``` + +- 内置支持调用百度的在线翻译服务,您需要百度的网站上(http://api.fanyi.baidu.com/)申请开通服务,开通后可以得到`appid`和`appkey`(密钥)。 + +- `--provider`用来指定在线翻译服务提供者,内置支持的是百度在线翻译。也可以传入一个js脚本,如下: + + ```javascript + // 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 +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")`的方式。 \ No newline at end of file diff --git a/docs/zh/guide/tools/vite.md b/docs/zh/guide/tools/vite.md new file mode 100644 index 0000000..6124f28 --- /dev/null +++ b/docs/zh/guide/tools/vite.md @@ -0,0 +1,97 @@ +# Vite插件 + +`@voerkai18n/babel`插件在`vite`应用中不能正常使用,需要使用`@voerkai18n/vite`插件来完成类似的功能,包括自动文本映射和自动导入`t`函数。 + +## 安装 + +`@voerkai18n/vite`只需要作为开发依赖安装即可。 + +```javascript +npm install --save-dev @voerkai18n/vite +yarn add -D @voerkai18n/vite +pnpm add -D @voerkai18n/vite +``` + +## 启用插件 + +接下来在`vite.config.js`中配置启用`@voerkai18n/vite`插件。 + +```javascript +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`时,就会在``自动注入`import { t } from "languages" `,同时会扫描源代码文件(包括`vue`,`js`等),根据`idMap.js`文件里面的文本映射表,将`t('"xxxx")`转换成`t("")`的形式。 + +不同于`@voerkai18n/babel`插件,`@voerkai18n/vite`插件不需要配置`location`和`autoImport`参数,能正确地处理导入`languages`路径。 + +## 插件参数 + +`vite`插件支持以下参数: + +```javascript +import Voerkai18nPlugin from "@voerkai18n/vite" +export default defineConfig({ + plugins: [ + Inspect(), // 可选的 + Voerkai18nPlugin({ + location: "./", // 可选的,指定当前工程目录 + autoImport: true, // 是否自动导入t函数 + debug:false, // 是否输出调试信息,当=true时,在控制台输出转换匹配的文件清单 + patterns:[ + "!(?/src/languages`文件夹下。 + +- `autoImport`:可选的,默认`true`,用来配置是否自动导入`t`函数。当vue文件没有指定导入时才会自动导入,并且根据当前vue文件的路径处理好导入位置。 + +- `debug`:可选的,开启后会在控制台输出一些调试信息,对一般用户没有用。 + +- `patterns`:可选的,一些正则表达式匹配规则,用来过滤匹配哪一些文件需要进行扫描和处理。默认的规则: + + ```javascript + const patterns={ + "!(? +import {reactive } from 'vue' +export default { + inject: ['i18n'], // 注入i18n实例,该实例由@voerkai18n/vue插件提供 + .... +} + +``` + +声明`inject: ['i18n']`后在当前组件实例中就可以访问`this.i18n`,该实例是一个经过`reactive`封闭的响应式对象,其内容是: + +```javascript +this.i18n = { + activeLanguage, // 当前激活语言,可以通过直接赋值来切换语言 + defaultLanguage, // 默认语言名称 + languages // 支持的语言列表=[{name,title},...] +} +``` + +## 应用示例 + +注入`i18n`实例后就可以在此基础上实现`激活语言`、`默认语言`、`切换语言`等功能。 + +```vue + +