用到的工具
quicktype
step1:根据JSON文件自动生成对应的interface
比如下面有个json文件(string.json):
// string.json
{
"strings":{
"login": {
"zh_CN": "登录",
"en_US": "Login in"
},
"login_out": {
"zh_CN": "退出",
"en_US": "Login out"
},
}
}
想让它自动生成以下的interface,并将其自动保存在strings.interface.ts文件里:
export interface StringsInterface {
strings: {
login: String,
login_out: String
};
}
export interface String {
zh_CN?: string;
en_US?: string;
}
可以在终端执行命令:
quicktype ./strings.json -o ./strings.interface.ts
此时会发现生成的interface是这样的:
export interface StringsInterface {
strings: { [key: string]: String };
}
export interface String {
zh_CN?: string;
en_US?: string;
}
需要修正一下strings.interface.ts,解决方法:
step1: 新建fix_strings_interface.js
const fs = require("fs");
const stringInterfacePath = "/strings.interface.ts";
const stringJSONPath = "/strings.auto.json";
const jsonStr = fs.readFileSync(stringJSONPath);
const stringsObj = JSON.parse(jsonStr);
const tmpStart = `
export interface StringsInterface {
strings: {
`;
const tmpEnd = `
};
}
`;
const testRegx = /^\w+$/i;
const tmpBody = Object.keys(stringsObj.strings)
.filter((item) => {
if (testRegx.test(item)) {
return true;
} else {
console.warn(`不合法的 key:${item}`);
return false;
}
})
.map((item) => ` ${item}: String;`)
.join("\n");
const tmp = tmpStart + tmpBody + tmpEnd;
const splitText = `export interface String {`;
const interfaceStr = fs.readFileSync(stringInterfacePath).toString();
const [_, rest] = interfaceStr.split(splitText);
const newInterfaceStr = [tmp, splitText, rest].join("\n");
fs.writeFileSync(stringInterfacePath, newInterfaceStr);
step2:在终端执行命令:
quicktype ./strings.auto.json -o ./i18n/strings.interface.ts --just-types && node ./fix_strings_interface.js
step2:编写方法——识别复数,并修改转化字符串
export function plural(str: string, revert: boolean) {
const plural = {
'(quiz)$' : '$1zes',
'^(ox)$' : '$1en',
'([m|l])ouse$' : '$1ice',
'(matr|vert|ind)ix|ex$' : '$1ices',
'(x|ch|ss|sh)$' : '$1es',
'([^aeiouy]|qu)y$' : '$1ies',
'(hive)$' : '$1s',
'(?:([^f])fe|([lr])f)$' : '$1$2ves',
'(shea|lea|loa|thie)f$' : '$1ves',
sis$ : 'ses',
'([ti])um$' : '$1a',
'(tomat|potat|ech|her|vet)o$': '$1oes',
'(bu)s$' : '$1ses',
'(alias)$' : '$1es',
'(octop)us$' : '$1i',
'(ax|test)is$' : '$1es',
'(us)$' : '$1es',
'([^s]+)$' : '$1s',
};
const singular = {
'(quiz)zes$' : '$1',
'(matr)ices$' : '$1ix',
'(vert|ind)ices$' : '$1ex',
'^(ox)en$' : '$1',
'(alias)es$' : '$1',
'(octop|vir)i$' : '$1us',
'(cris|ax|test)es$' : '$1is',
'(shoe)s$' : '$1',
'(o)es$' : '$1',
'(bus)es$' : '$1',
'([m|l])ice$' : '$1ouse',
'(x|ch|ss|sh)es$' : '$1',
'(m)ovies$' : '$1ovie',
'(s)eries$' : '$1eries',
'([^aeiouy]|qu)ies$' : '$1y',
'([lr])ves$' : '$1f',
'(tive)s$' : '$1',
'(hive)s$' : '$1',
'(li|wi|kni)ves$' : '$1fe',
'(shea|loa|lea|thie)ves$': '$1f',
'(^analy)ses$' : '$1sis',
'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$': '$1$2sis',
'([ti])a$' : '$1um',
'(n)ews$' : '$1ews',
'(h|bl)ouses$' : '$1ouse',
'(corpse)s$' : '$1',
'(us)es$' : '$1',
s$ : '',
};
const irregular = {
move : 'moves',
foot : 'feet',
goose : 'geese',
sex : 'sexes',
child : 'children',
man : 'men',
tooth : 'teeth',
person : 'people',
};
const uncountable = [
'sheep',
'fish',
'deer',
'moose',
'series',
'species',
'money',
'rice',
'information',
'equipment',
];
// save some time in the case that singular and plural are the same
if (uncountable.indexOf(str.toLowerCase()) >= 0) {
return str;
}
// check for irregular forms
for (const word in irregular) {
let pattern;
let replace;
if (revert) {
pattern = new RegExp(irregular[word] + '$', 'i');
replace = word;
} else {pattern = new RegExp(word + '$', 'i');
replace = irregular[word];
}
if (pattern.test(str)) {
return str.replace(pattern, replace);
}
}
let array;
if (revert) {
array = singular;
} else {
array = plural;
}
// check for matches using regular expressions
for (const reg in array) {
const pattern = new RegExp(reg, 'i');
if (pattern.test(str)) {
return str.replace(pattern, array[reg]);
}
}
return str;
}
step3:编写多语言配置调用方法
import { StringsInterface, String as StringInterface } from './strings.interface';
import { template } from 'lodash';
import stringsConfigJson from '../config/strings.auto.json';
import { plural } from './plural';
export type { String as StringInterface } from './strings.interface';
export const ERROR_STR = '[ERROR STR]';
export const StringsConfig: StringsInterface = stringsConfigJson as any as StringsInterface;
export const Strings = StringsConfig.strings; // 字符串的key集合传出去
export function getLanguage() {
...
}
/**
* 多语言获取,传入对象或key
* @param StringObject 对应strings.json文件的字符串对象
* @param options JavaScript Object, 支持字符串格式化(string format),支持lodash _.tempalte
* @example t(Strings.early_bird) 或 t('early_bird')
*/
function getText(stringObj: StringInterface | string, options: any = null, isPlural: boolean = false): string {
if(!stringObj){
console.error('Cannot find this string key');
return ERROR_STR;
}
const lang = getLanguage(); // default Language
let obj: StringInterface;
if (typeof stringObj === 'string' || stringObj instanceof String) {
const key: string = stringObj as string;
obj = StringsConfig[key];
if (obj === undefined) {
return key;
}
}
obj = stringObj as StringInterface;
const text = lang in obj ? obj[lang] : obj['zh_CN'] || ERROR_STR;
const str = options ? template(text)(options) : text;
if (isPlural) {
return plural(str, false);
}
return str;
}