# 国际化i18n

Mpx 支持国际化 i18n,使用方式及支持能力与 vue-i18n 非常接近。

Mpx 自带 i18n 能力,无需额外安装插件。由于小程序模板中的 i18n 函数是通过 wxs 编译注入进行实现,我们需要将 i18n 配置传入到 MpxWebpackPlugin 中来使 i18n 生效,这是与 vue-i18n 最大的区别。

# 开启 i18n

I18n 配置传入到 MpxWebpackPlugin 选项中即可生效,额外支持 messagesPath 配置,通过模块路径传入语言集,其余配置参考 vue-i18n。

由于小程序的双线程特性,在选项式 API 场景下,默认情况下模板中调用的 i18n 函数由 wxs 实现,而 js 中调用的 i18n 函数由 js 实现,该设计能够将视图层和逻辑层之间的通信开销降至最低,得到最优的性能表现,但是由于 wxs 和 js 之间无法共享数据,在最终的编译产物中语言集会同时存在于 js 和 wxs 当中,对包体积产生负面影响。

为了平衡上述影响,自2.6.56版本之后我们新增了编译配置项 i18n.useComputed ,改配置项开启的情况下对于模板中的 i18n 调用将不再使用 wxs 实现,而是通过在 computed 进行实现,语言集将只存在于 js 逻辑层当中,对于节省了包体积的同时双线程通信成本也会增加,用时间换空间,具体是否开启可以根据实际项目的使用情况及资源瓶颈由开发者自行决定, 另外需要注意的是,在 Mpx 组合式 API 场景下 i18n 仅支持通过 computed 实现

开启 i18n.useComputed 配置时,由于 computed 技术架构的限制,i18n 函数无法对模板循环渲染中的 itemindex 生效。

new MpxWebpackPlugin({
  i18n: {
    locale: 'en-US',
    // messages既可以通过对象字面量传入,也可以通过messagesPath指定一个js模块路径,在该模块中定义配置并导出,dateTimeFormats/dateTimeFormatsPath和numberFormats/numberFormatsPath同理
    messages: {
      'en-US': {
        message: {
          hello: '{msg} world'
        }
      },
      'zh-CN': {
        message: {
          hello: '{msg} 世界'
        }
      }
    },
    useComputed: false // 默认 false,  此开关将模板中的 i18n 函数注入 computed,在包体积空间紧张的情况下可以使用
    // messagesPath: path.resolve(__dirname, '../src/i18n.js')
  }
})

@mpxjs/[email protected] 版本配置如下

// vue.config.js
module.exports = defineConfig({
  pluginOptions: {
    mpx: {
      plugin: {
        i18n: {
          locale: 'en-US',
          // messages既可以通过对象字面量传入,也可以通过messagesPath指定一个js模块路径,在该模块中定义配置并导出,dateTimeFormats/dateTimeFormatsPath和numberFormats/numberFormatsPath同理
          messages: {
            'en-US': {
              message: {
                hello: '{msg} world'
              }
            },
            'zh-CN': {
              message: {
                hello: '{msg} 世界'
              }
            }
          },
          useComputed: false // 默认 false,  此开关将模板中的 i18n 函数注入 computed,在包体积空间紧张的情况下可以使用
          // messagesPath: path.resolve(__dirname, '../src/i18n.js')
        }
      }
    }
  }
})

# 选项式 API 中使用

同 vue-i18n,在组件中直接调用翻译函数使用,由于 wxs 执行环境的限制,目前 js 中支持了 vue-i18n 中 $t/$tc/$te 翻译函数,$d/$n 暂不支持,详细使用方法可参考 vue-i18n。

此外类似于 vue-i18n,在组件模板的 Mustache 插值中直接调用翻译函数。

<template>
    <view>
        <view wx:if="{{isMessageExist}}">{{ $t('message.hello', { msg: 'hello' }) }}</view>
    </view>
</template>
<script>
    createComponent({
        ready () {
            console.log(this.$t('message.hello', { msg: 'hello' }))
        },
        computed: {
            isMessageExist () {
                return this.$te('message.hello')
            }
        }
    })
</script>

# 在组合式 API 中使用

在组合式 API setup 中,this 不是该活跃实例的引用,为了继续使用 i18n 的相关能力,我们需要一个新的方法来替代 this ,这里 Mpx 提供 useI18n 方法支持对 i18n 相关功能的使用。

# 开始使用

同 vue-i18n, useI18n 返回一个 i18n 实例,该实例提供文案翻译 API 例如 t 方法,相较于选项式 API 中的翻译方法,在组合式 API 中有部分变化。

  1. 翻译方法 $t/t 和 $tc/tc,在组合式 API 中统一使用翻译方法 t
  2. 翻译方法 $te/te,在组合式 API 中统一使用 te
  3. 翻译方法 $tm/tm,在组合式 API 中统一使用 tm

useI18n 方法的执行必须在 setup 中的顶层。

i18n 同时也支持传入参数,例如 localefallbackLocale, 关于实例的更多信息可移步API章节 查看

在不给 useI18n 传入任何参数时,i18n 实例的上下文将是全局作用域,即翻译函数 t 引用的文案来源是我们在 MpxWebpackPlugin 中配置的文案。

通过在 setup 中返回翻译函数 t,我们可以在模版中直接使用它:

<template>
    <view>{{t("message.hello")}}</view>
</template>
import {createComponent, useI18n} from '@mpxjs/core'

createComponent({
  setup(props, context) {
    const { t } = useI18n() // 从返回中解构出 t 方法
    return {
      t
    } // 将 t 方法返回挂载到渲染实例
  }
})

注意事项:

组合式 API 中,Mpx 在编译时对模版进行 ast 遍历分析,检测到对应的翻译函数 t/tc/te 后,将其转换为 computed 方法注入,computed 中调用 setup 中返回的 t 方法进行文案获取,由此引申出来两个注意项:

1.不可对从 useI18n 中解构出的 t 方法进行重命名,否则模版中将无法正确获取文案。

下方即为错误示例:

<template>
    <view>{{t1('message.hello')}}</view>
</template>
<script>
    import {createComponent, useI18n} from '@mpxjs/core'
    createComponent({
        setup() {
            const {t: t1} = useI18n
            return {
                t1
            }
        }
    })
</script>

2.模版中的 t/tc/te 方法都是使用 computed 实现,由于 computed 技术架构的限制,i18n 函数无法对模板循环渲染中的 itemindex 生效。

例如下方示例将会报错 computed 中找不到 item

<template>
    <!--tc方法使用 computed 实现,最终将无法正确获取item-->
    <view wx:for="{{listData}}" wx:key="index">{{tc('message.hello', {msg: item})}}</view>
</template>
<script>
    import {createComponent, reactive, useI18n} from '@mpxjs/core'
    createComponent({
        setup() {
            const listData = reactive([1,2,3])
            const {tc} = useI18n
            return {
                tc,
                listData
            }
        }
    })
</script>

# 作用域

在 Mpx 组合式 API 中,useI18n 返回的 i18n 实例默认是指向全局作用域,文案来源即为 MpxWebpackPlugin 中配置的 messages。

如果需要在组合式 API 中使用本地作用域,需要给 useI18n 传入响应的配置项,useI18n 将根据传入的 locale、messages 等配置项来生成一个全新的 i18n 实例,该实例的文案源即为用户传入的 messages

<template>
    <!--将展示本地作用域中的文案 哈喽-->
    <view>{{t('message.hello')}}</view>
</template>
<script>
    import {createPage, useI18n} from '@mpxjs/core'
    createPage({
        setup() {
            const { t } = useI18n({
                locale: 'en-US',
                messages: {
                    'en-US': {
                        message: {
                            hello: '哈喽'
                        }
                    }
                }
            })
            return {
                t
            }
        }
    })
</script>

注意事项:

在给 useI18n 传参时,若只传入 messages 属性,则 locale 会自动 fallback 到全局 locale,修改 useI18n 返回的 locale ref 值将改变本地作用域 locale。

若只传入 locale 属性,不传入 messages,则会自动 fallback 到全局 localemessages,且修改 useI18n 返回的 locale全局和本地都不会起作用, 因此建议不要单独传入 locale 属性,若想使用本地 i18n 实例,则必须传入 messages 属性。

在 Mpx 选项式 API 中,i18n 实例仅有全局 global i18n 实例,所有 i18n 翻译方法 $t/$tc/$te/$tm 的执行上下文都为全局作用域。

<template>
    <view>{{$t('message.hello')}}</view>
</template>
<script>
    import {createPage} from '@mpxjs/core'
    createPage({
        onLoad() {
            console.log(this.$i18n)
            /**
             * locale 全局 locale
             * fallbackLocale 全局兜底 locale
             */
        },
        computed: {
            name() {
                // 作用域为 global scope
                return this.$t('message.name')
            }
        }
    })
</script>

# 动态变更locale

在 Mpx 组合式 API 中,你可以通过修改 useI18n 返回的 locale 来更换局部或全局语言,或者更改 mpx.i18n 中的 locale 属性更换全局语言集,并自动更新视图。

  • 变更全局 locale
import mpx, { createComponent, useI18n } from '@mpxjs/core'

createComponent({
  setup () {
    // 局部locale变更,生效范围为当前组件内
    const {t, locale} = useI18n()
    setTimeout(() => {
      // 全局locale变更,生效范围为项目全局,locale 是一个 ref 变量
      locale.value = 'zh-CN'
      // 或者通过mpx.i18n来修改,也能起到全局locale修改作用
      mpx.i18n.global.locale.value = 'zh-CN'
    }, 1000)
    return {
        t
    }  
  }
})

注意: 这里通过 mpx.i18n 修改 locale 时,为和 vue3 保持一直,需要通过 global 属性来获取 locale,同时 locale 也是一个 ref 变量,需要使用 .value 来修改。

  • 变更局部 locale
import mpx, { createComponent, useI18n } from '@mpxjs/core'

createComponent({
  setup () {
    // 局部locale变更,生效范围为当前组件内
    const {t, locale} = useI18n({
        locale: 'en-US',
        messages: {
            'en-US': {
                message: {
                    hello: 'hello'
                }
            },
            'zh-CN': {
                message: {
                    hello: '你好'
                }
            }
        }
    })
    setTimeout(() => {
      // 局部locale变更,生效范围为当前组件/页面,locale 是一个 ref 变量
      locale.value = 'zh-CN'
    }, 1000)
    return {
        t
    }  
  }
})

在选项式 API 中,仅支持修改全局 locale, 不支持修改局部 locale。

在 Vue3 中,选项式 API 中直接修改 this.$i18n.locale 无任何作用,这里为和 Vue3 拉齐建议使用 mpx.i18n 来进行 locale 修改。

import mpx, { createComponent } from '@mpxjs/core'

createComponent({
  ready () {
      // 修改全局 locale
      mpx.i18n.global.locale = 'en-US'
  }
})

# 动态更新语言集

在组合式 API 中默认支持动态更新语言集。

在选项式 API 中,当模板中没有使用 i18n 函数或开启了 i18n.useComputed 配置时, 可以对语言集进行动态更新。

我们提供了 mergeLocaleMessage 对语言集进行动态扩展,setLocaleMessage 对语言集进行覆盖更新。

使用方式如下:

<template>
    <!--2 秒之后展示 哈喽-->
    <view>{{t('message.hello')}}</view>
    <!--1秒之后展示 hello1, 2秒之后展示为 恭喜你-->
    <view>{{t('message.hello1')}}</view>
</template>
<script>
    import mpx, { createComponent, useI18n} from '@mpxjs/core'

    createComponent({
        setup () {
            const { t, mergeLocaleMessage, setLocaleMessage } = useI18n()
            setTimeout(() => {
                // 新增一门语言或针对特定语言更新语言集
                mergeLocaleMessage('en-US', {
                    message: {
                        hello1: 'hello1',
                    }
                })
            }, 1000)
            setTimeout(() => {
                setLocaleMessage('en-US', {
                    message: {
                        hello: '哈喽',
                        hello1: '恭喜你'
                    }
                })
            }, 2000)
            return { t }
        }
    })
</script>

同理我们在选项式 API 中可以使用 mpx.i18n.global.mergeLocaleMessage, mpx.i18n.global.setLocaleMessage 方法来动态更新或扩展语言集。

# 平台支持

目前支持业内所有小程序平台(微信/支付宝/qq/百度/头条)。

在输出 web 时,构建会自动引入 vue-i18n 并进行安装配置,无需修改任何代码即可按照预期正常工作。