在项目中定义 i18n 结构化对象的简单思路
简介
本文介绍一种在项目定义 i18n 文本结构的简单思路,以及定义一些简单的全局函数,用于获取 i18n 文本。
文件结构
src
├── i18n
└── index.js
├── zhCN
├── index.js
├── common.js // 放置项目中通用的文本
├── moduleName.js // 放置各模块独立的文本
├── ...
├── en
├── index.js
├── common.js
├── moduleName.js
├── ...
├── ... // 其他语言
在项目根路径下新建 i18n 文件夹,也就是 /src/i18n (可配置别名 @/i18n),并新建 index.js 文件。这里省略 i18n 的创建过程。
在同级目录下新建各语言的对应文件夹,并统一创建 index.js ,内容都一样:
const allModules = import.meta.globEager('./*');
let modules = {};
Object.keys(allModules).forEach((key) => {
// 这是将单个模块文件作为一个属性,若模块导出了多个值,这些值位于该属性值的对象中
// modules[key.replace(/(\.\/|\.js)/g, '')] = allModules[key].default;
// 这是将所有模块直接打平,再导出为一个对象
modules = Object.assign(modules, allModules[key].default);
});
export default modules;
通用模块
common.js
// 全局通用但暂时想不到分类可以先放这里
const common = {
all: 'All',
action: 'Action',
success: 'Success',
fail: 'Fail',
};
// 操作
const action = {
submit: 'Submit',
cancel: 'Cancel',
confirm: 'Confirm',
reset: 'Reset',
view: 'View',
add: 'Add New',
edit: 'Edit',
delete: 'Delete',
copy: 'Copy',
};
// 这里并不是表示货币符号与语言直接挂钩,而是可能有特殊的格式化,比如中文下的 RMB 格式化为 ¥(元)
const currency = {
en: '$',
zhCN: '¥',
};
export default {
common,
form,
currency,
};
各模块
主要包括 info、fieldKey、filter、message、placeholder 等,主要分为两大类。
- 一类是与视图构建相关的文本,如 fieldKey ,开发输出组件时常使用配置化的方式减少模板的使用,而是通过 v-if、v-for 等指令快速构建;
- 另外一类是与各类辅助信息、提示消息等相关的文本,包括 info、message、placeholder 等单纯用于不同语言下切换的文本,以及 filter 这类用于过滤后端返回数据的文本。比如 select 组件的 value 为了存储方便,通常会使用纯数字表示,但不便于用户记忆和使用,在 vue2 中可以通过 filters 搭配管道操作符进行替换, vue3 中虽然移除了 filters ,但这种需求场景仍然存在,因此仍然有必要为了该需求设置一个专门的对象用于过滤并输出方便人看的内容。即使未使用 i18n ,该对象也可以作为组件内的私有变量存在。
下面以产品模块为例。
const productManagement = {
info: {
moduleName: 'ProductManagement', // 可能用于特殊用途
},
fieldKey: {
productId: 'Product Id',
productName: 'Product Name',
},
filter: {
warehouseType: {
[0]: 'Domectic Warehouse',
[1]: 'Oversea Warehouse',
},
},
message: {
field: {
productName: {
required: 'Product name is required',
length: 'Please input no more than 20 characters',
},
},
form: {
add: {
success: 'Product added',
fail: 'Fail to add product, please check',
},
edit: {
success: 'Product edited',
fail: 'Fail to edit product, please check',
},
delete: {
success: 'Product deleted',
fail: 'Fail to delete product, please check',
},
},
},
placeholder: {
productName: 'Please input the product name',
},
};
export default { productManagement};
全局的 i18n 转换函数
这里仅展示获取上一段有提到的结构化对象中文本的函数。
@/utils/setupI18n/index.js
import { i18n } from '@/i18n'; // 这里是已经在项目中的 i18n 模块下通过 createI18n 方法创建好了对象,所以直接导入
const { t: $t } = i18n.global; // 在普通 js/ts 模块中使用 $t
// 字段
export const setupFieldKeyI18nGlobal = (moduleName) => (fieldName) =>
$t(`${moduleName}.fieldKeys.${fieldName}`);
// 过滤
export const setupFiltersI18nGlobal = (moduleName) => (fieldName, fieldVal) =>
$t(`${moduleName}.filters.${fieldName}.${fieldVal}`);
// placeholder
export const setupPlaceholderI18nGlobal = (moduleName) => (fieldName) =>
$t(`${moduleName}.placeholder.${fieldName}`);
// 提示消息
export const setupFieldMessageI18nGlobal =
(moduleName) => (componentType, actionType) =>
$t(`${moduleName}.messages.field.${componentType}.${actionType}`);
export const setupFormMessageI18nGlobal =
(moduleName) => (componentType, actionType) =>
$t(`${moduleName}.messages.form.${componentType}.${actionType}`);
组件中的使用示例:
import {setupFieldKeyI18nGlobal } from '@/utils/setupI18n';
const moduleName = ref('ProductManagement'); // 业务组件的模块名
// 返回一个新函数 (fieldName) => $t(`ProductManagement.fieldKeys.${fieldName}`);
const setupFiltersI18n = setupFiltersI18nGlobal(moduleName.value);
通过这种方式,可以将实际的参数结构统一到一个地方进行管理,避免 i18n 模块中的对象结构变更后,还得修改各个组件。