前情提要

  最近维护了一个微信小程序的老项目,维护的其中一项是添加国际化。由于踩了蛮多坑,所以就有了这篇文档!!!

miniprogram-i18n

  对除小程序外的其他框架开发做过国际化的朋友来说i18n这几个字母应该不陌生,i18n之所以叫i18n是因为次单词长度为20,以i开头以n结束,i和n之间间隔18位。

国际化全流程及踩坑

  • 首先,在小程序项目中引入依赖。官方文档上有依赖安装位置及文件关系,详情可点击miniprogram-i18n查看,简单理解为,在小程序根目录下使用命令行安装依赖
npm i -D gulp @miniprogram-i18n/gulp-i18n-locales @miniprogram-i18n/gulp-i18n-wxml

这一步做了什么呢?

安装了gulp、和miniprogram-i18n的gulp插件。

为什么要安装这些呢?

为了打包生成翻译文件以及编译`.wxml`文件。

为什么要打包呢?

这就是一个需要了解国际化原理的问题了,我将其理解为:在打包之前,我们写的语言翻译配置文件与页面还没有真正地联系起来,打包之后会生成一个`locales.js`和`locales.wxs`文件,配合`gulp-i18n-xml`插件将`.wxs`引入到每一个页面中,这样才能真正的实现页面翻译。

为什么要用`gulp-i18n-xml`打包`.wxml`文件呢?

因为按照官方时使用指南,我们在页面的使用中直接是`{{t('key')}}`这样的方式进行文本翻译的,然而真正实现国际化的`.xml`写法的语句是`<wxs src="生成的locale.wxs文件路径" module="i18n"/> <view>{{i18n.t('key', $_locale)}}</view>`这样的。也就是说,使用此插件可以自动为`.wxml`引入`.wxs`并且将翻译的文本转换为实现所需要的格式,如此可以减少开发者的代码编写数量。

一定要使用`gulp-i18n-xml`才能实现翻译吗?

不是。如上所说,此插件实际上只是减少开发者的代码编写数量,如果开发者手动引入文件,并在使用时以`i18n.t('key', $_locale)`这样的形式实现文本翻译时,即可不用此插件。

顺便一说,如果要直接使用此插件生成翻译文件,那几乎是将现有文件完全生成一份新的到目标文件夹下,项目发布时,直接发布打包后的文件到生产。这对于从0开始的项目来说这并没有什么影响甚至能减少开发者工作量(也许),但如果是维护老项目的话,此举可能并不是明智的。

如果使用`i18n.t('key', $_locale)`的形式实现,引入的文件从哪里获得呢?

前面说过,打包之后会生成一个`locales.js`和`locales.wxs`文件,引入的就是此`locales.wxs`文件。

完全交由gulp工具打包需要注意什么呢?

如果是维护老项目,那么目录结构一般都是`pages、app.js、app.json、app.wxss、otherFolders/页面文件(夹),此时打包需要注意将同级目录每一个需要的文件都copy一份到目标文件夹下,并注意路径。如果是新项目可以将需要打包的文件全部放置于一个src文件夹内(注意:需要的文件多半还包括package.json,也就是src文件夹内外部可能都需要一个package.json文件,可参照官方example的目录结构搭建)。

注意:如果没有将`node_modules`或`miniprogram_npm`打包到目标文件夹下,需要在目标文件夹下执行`npm i`和`构建npm`

根据官方文档和example可以发现,官方打包前的文件目录结构是app.js/json/wxs以及page、i18n、静态资源文件夹等都由一个src文件夹包裹着,gulp配置文件中将i18n交由`gulp-i18n-locale`处理,wxml文件交由`gulp-i18n-xml`处理,src目录下的其他文件全部copy到目标目标生成文件夹下。

  • 上一步我们已经往安装了依赖,现在需要在小程序中使用工具构建npm,如此才能在小程序js文件中成功引用依赖。

`工具`->`构建npm`

构建npm出错?

可能的原因是:未成功安装依赖,可以先卸载依赖再安装;当前构建npm的位置没有package.json文件;

  • 上一步我们已经往小程序中注入了依赖,接下来就是如何使用的问题。通过官方文档可以直到需要在i18n文件夹内新建语言配置的json文件(比如en-US、zh-CN)
// en
{
"hello": "Hello"
}
// zh
{
"hello": "你好"
}

   然后在需要使用国际化的页面的js中

...
import { I18n } from '@miniprogram-i18n/core';
Component({
behaviores: [I18n]
})

   页面使用

<view>{{t('hello')}}</view>

  必须要用Component吗?

    我们在小程序官方文档发现`behaviors`是Component构造器所有的,那么如果当前是Page怎么办呢?国际化官方文档中建议都使用Component构造器定义,否则就需要引入I18nPage代替Page构造器。然而在实践中发现直接在Page构造器中直接使用`behaviors`并不会报错,i18n也能被正确引入(直到2022/01/19)。当然这只是针对维护老项目而言,也许这样做存在系列潜在风险,出于安全考虑,在使用Component构造器或者I18nPage都方便的前提下,最好还是不要尝试以上的方法。

  为什么照官方文档做了,还是不能正常翻译?

    实际上,到这一步无法正常翻译才是正确的结果,甚至在控制台可能会看到这样的报错:

    根据以上报错,提示我们在使用I18n之前确保在app.js文件 中运行了initI18n()。回顾我们之前的操作,没见过也没有运行过这一函数。不必惊慌,导致这一错误并不完全是我们的问题,因为官方的快速开始文档里确实没有对这一步的相关描述(不过在接口文档里有描述)。

  按照控制台报错,我们在app.js中执行InitI18n()

...
import { initI18n } from '@miniprogram-i18n/core'
initI18n('en-US')
App({
...
})

  再检查页面,此时翻译文本就正常显示了,并且控制台不再有出现上面描述的错误

  • 为什么官方的select,我不能成功使用?

  截止2022/01/19,本人尚未成功使用过select。根据文档,我想文档中特性部分的`目前 miniprogram-i18n 仅支持纯文本及文本插值,后续会对其他 i18n 特性进行支持。`这句话也许是答案。

  • 如何在js中使用i18n?

  截止2022/01/19,本人也尚未成功在js中成功使用i18n(官方的example可以),不过项目与官方example不同的地方是:使用Page构造器,并且没有使用I18nPage代替;国际化是通过将gulp生成的两个国际化文件copy出来手动引入实现的。(事实上,更改Page,和自动生成.wxml文件,也依旧没有成功)。

  在官方给出的样本中,是能够在js文件中使用i18n的,只是同步渲染存在一点小问题(也就是通过js文件处理展示在页面上的国际化文本,在语言切换后,在不进行任何其他函数处理的前提下,不会同步转换为当前语言的文本)。

  • 什么时候build呢?

  每当翻译配置文件有内容修改时。如果是手动引入locales.wxs,每当翻译配置文件有内容修改时,都需要build生成新的文件。

  每当涉及国际化的文件有变动时。如果是自动build实现locales.wxs文件引入,每当wxml有国际化相关内容变动、翻译配置文件有内容修改时,都需要build重新生成。

本人项目参考

前提了解:维护老项目,没有一个用于包裹pages、utils、assets、app.*等文件(夹)的src文件夹。

  • 安装依赖
  • 构建npm
  • 新建语言配置文件

   在根目录下(与app.*文件同级)新建i18n文件夹,文件夹内新建两个json文件,分别是`en-US.json`与`zh-CN`

{
"index": "首页",
"hello": "你好{name}, 欢迎!"
...
}
{
"index": "Index",
"hello": "Hello {name}, Welcome!"
...
}
  • gulp配置

  在根目录下(与app.*文件同级)新建gulpfile.js文件

 1 const { src, dest, series } = require('gulp')
2 const gulpI18nWxml = require('@miniprogram-i18n/gulp-i18n-wxml')
3 const gulpI18nLocales = require('@miniprogram-i18n/gulp-i18n-locales')
4
5 function mergeAndGenerateLocales() {
6 return src('i18n/*.json')
7 .pipe(gulpI18nLocales({ defaultLocale: 'zh-CN', fallbackLocale: 'zh-CN' }))
8 .pipe(dest('dist/src/i18n/'))
9 }
10 export.default = series(mergeAndGenerateLocales)
  • package.json文件"script"配置
{
...
"script": {
"build": "gulp",
...
},
 ...
}
  • i18n初始化

  在app.js文件中

...
import { initI18n, getI18nInstance } from '@miniprogram-i18n/core'
initI18n("en-US")
App({
onLaunch: function() {
/** 获得本地语言 */
const lang = wx.getAppBaseInfo().language
const i18n = getI18nInstance()
/** 根据本地语言设置小程序语言 */
i18n.setLocale(lang.toLowerCase().includes('zh') ? 'zh-CN' : 'en-US')
}
})
  • 在需要使用到国际化翻译页面的js文件引入I18n
...
const { I18n } = require('@miniproogram-i18n/core')
Page({
behabiors: [I18n],
...
})
  • 打包生成locale.*文件

  在gulpfile.js文件所在文件夹,用命令行运行

npm run build
  • 从dist/src/i18n文件夹下,将生成的`locales.js`及`locales.wxs`拷贝到任意位置(我的位置是utils/i18n/)
  • 在需要使用到国际化翻译页面的wxml文件中引入并书写翻译文本
<wxs src="../../utils/i18n/locales.wxs" module="i18n"></wxs>
<view class="warpper">
<view>{{ i18n.t('index', $_locale) }}</view>
<text>{{ i18n.t('hello', { name: 'Jone' }, $_locale) }}</text>
</view>
  • 当翻译配置文件有变动的时候,重新build并更新utils/i18n/文件夹下的两个文件

官方样本

在社区看到不少朋友有无法顺利打开官方提供的example的困扰,在这里也单独说一下。

  • 先从glthub将example下载到本地,此时可以先不在开发者工具中打开此项目,即使打开页面也不会正常显示,因为根文件夹下没有app.*文件。
  • 在gulpfile.js所在的文件位置,命令行运行。
  • 此时会有一个新的`dist`文件夹,进入dist文件夹。
  • 命令行依次运行。
npm install
npm run build
  • 在微信开发者工具中导入dist文件夹这个项目(注意是dist)。
  • 点击`工具`->`构建npm`
  • 此时页面应该已经加载在模拟器中了,如果没有,可以点击`预览`(Windows系统也可以使用Ctrl+B快捷键)。

下载下来的example不能直接打开的可能原因一是:没有依赖。二是:根目录下没有`app.*`文件。

example就是典型的将所有都交给gulp打包的案例,我们编辑的文件只是为了用于生成dist文件夹中的内容,而真正查看效果以及上传到版本的是dist内的生成的文件。

个人遗留问题

  1. 在Page构造器中使用`behaviors`有没有/有哪些副作用?
  2. 为什么不能在js文件中使用I18n?

最新文章

  1. BPM配置故事之案例14-数据字典与数据联动
  2. 模拟CSS3 多组位移运动属性的框架封装
  3. 一、javascript中的类
  4. js =&gt; ES6一个新的函数写法
  5. 通过LoadRunner - Analyze详细分析页面元素请求
  6. python基于Django框架编译报错“django.core.exceptions.ImproperlyConfigured”的解决办法?
  7. nginx自动检测后台服务器健康状态
  8. 算法系列:Fibonacci
  9. Ajax Step By Step3
  10. java.lang.RuntimeException: Method setUp in android.test.ApplicationTestCase not mocked. See http://g.co/androidstudio/not-mocked for details.
  11. python leetcode 日记 --Contains Duplicate II --219
  12. Server-Side UI Automation Provider - WinForm Sample
  13. 基于私钥加密公钥解密的RSA算法C#实现
  14. POJ 3660 Cow Contest (最短路dijkstra)
  15. js简单实现删除记录时的提示效果
  16. Maven学习-优化和重构POM
  17. React Native (一) 入门实践
  18. Linux常用操作指令(面试专用)
  19. 使用JDBC连接ElasticSearch6.3(ElasticSearch SQL JDBC)
  20. 【python基础】sys

热门文章

  1. ElasticSearch 使用
  2. java 图形化工具Swing 基本使用
  3. Spring Boot整合Thymeleaf及Thymeleaf页面基本语法
  4. 【Android开发】微信精选,文章资讯类App开发记录总结
  5. 【LeetCode】899. Orderly Queue 解题报告(Python)
  6. 【LeetCode】908. Smallest Range I 解题报告(Python)
  7. 【LeetCode】756. Pyramid Transition Matrix 解题报告(Python & C++)
  8. 【LeetCode】222. Count Complete Tree Nodes 解题报告(Python)
  9. fastapi(一)
  10. CapstoneCS5265设计替代CH7211 |Type-C转HDMI2.0方案|替代CH7211