微信小程序-国际化(miniprogram-i18n)

前情提要

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

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>
<view>{{t('day', {day: '12'})}}</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?

  先说结果: this.t('hello') || this.t('day', {day : this.data.day}) // 文本插值语法

  我在最初编写这篇博文的时候,是没有成功在自己的js中成功使用js的,尽管官方的example成功了,我几番比对都没有发现自己漏掉了哪一步。直到前天突然灵光一闪,会不会是js中使用i18n的机制问题。结果我将原本单独存放locale.*文件的i18n文件夹与国际化配置文件的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文件。(从配置会在第一次build时,在i18n文件夹下生成locales.js和locales.wxs文件,之后每一次build这两个文件都会随着配置文件的更新而更新

 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('i18n/'))
 9 }
10 export.default = series(mergeAndGenerateLocales)
  • package.json文件"script"配置
{
  ...
  "script": {
    "build": "gulp",
    ...
  },
 ...
}
  •  i18n初始化

  在app.js文件中

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

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

npm run build
  • 在需要使用到国际化翻译页面的wxml文件中引入并书写翻译文本
<wxs src="../..i18n/locales.wxs" module="i18n"></wxs>
<view class="warpper">
  <view>{{ i18n.t('index', $_locale) }}</view>
  <text>{{ i18n.t('hello', { name: 'Jone' }, $_locale) }}</text>
</view>
  • 在需要使用到国际化翻译的js文件中
import { I18n } from "@miniprogram-i18n/core"
Component({
    behaviors: [I18n]
    data: {name: 'Developer'}
    methods: {
        OnLoad: function () {
            console.log(this.t('index'), this.t('hello', {name: this.data.name})
        }
    }
})
//或者
import { I18nPage } from "@miniprogram-i18n/core"
I18nPage({
    data: {name: 'Lii'}
    OnLoad: function () {
        console.log(this.t('index'), this.t('hello', {name: this.data.name})
    }
})
  • 当翻译配置文件有变动的时候,重新build并更新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`有没有/有哪些副作用?
posted @ 2022-01-20 10:10  JoneOnenine  阅读(3204)  评论(0编辑  收藏  举报