update
This commit is contained in:
parent
5b18f5a0e5
commit
feb72c01a2
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
/.vscode
|
||||
/node_modules
|
||||
/demo/apps/app/languages
|
||||
node_modules
|
||||
/packages/demo/apps/app/languages
|
||||
/packages/demo/apps/*/languages
|
||||
/packages/demo/*/node_modules
|
||||
/coverage
|
@ -1,27 +0,0 @@
|
||||
module.exports = {
|
||||
"1": "a1:aaaaa",
|
||||
"2": "no aaaaa",
|
||||
"3": "bbbbb",
|
||||
"4": "eeeeee",
|
||||
"5": "x:from a",
|
||||
"6": "中华人民共和国",
|
||||
"7": "a2:aaaaa",
|
||||
"8": "no erer",
|
||||
"9": "no aaareraa",
|
||||
"10": "no fdfd",
|
||||
"11": "fdgfdgfdg",
|
||||
"12": "aaaaa",
|
||||
"13": "aaaaa1",
|
||||
"14": "aaaaa2",
|
||||
"15": "aaaaa 3",
|
||||
"16": "ccccc",
|
||||
"17": "cccccdc",
|
||||
"18": "ddddd中国",
|
||||
"19": "html-a",
|
||||
"20": "html-b",
|
||||
"21": "html-c",
|
||||
"22": "from a",
|
||||
"23": "from b",
|
||||
"24": "from c1",
|
||||
"25": "from c2"
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
module.exports = {
|
||||
"1": "德文:aaaaa",
|
||||
"2": "no aaaaa",
|
||||
"3": "bbbbb",
|
||||
"4": "eeeeee",
|
||||
"5": "x:from a",
|
||||
"6": "中华人民共和国",
|
||||
"7": "a2:aaaaa",
|
||||
"8": "no erer",
|
||||
"9": "no aaareraa",
|
||||
"10": "no fdfd",
|
||||
"11": "fdgfdgfdg",
|
||||
"12": "aaaaa",
|
||||
"13": "aaaaa1",
|
||||
"14": "aaaaa2",
|
||||
"15": "aaaaa 3",
|
||||
"16": "ccccc",
|
||||
"17": "cccccdc",
|
||||
"18": "ddddd中国",
|
||||
"19": "html-a",
|
||||
"20": "html-b",
|
||||
"21": "html-c",
|
||||
"22": "from a",
|
||||
"23": "from b",
|
||||
"24": "from c1",
|
||||
"25": "from c2"
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
module.exports = {
|
||||
"1": "a1:aaaaa",
|
||||
"2": "no aaaaa",
|
||||
"3": "bbbbb",
|
||||
"4": "eeeeee",
|
||||
"5": "x:from a",
|
||||
"6": "中华人民共和国",
|
||||
"7": "a2:aaaaa",
|
||||
"8": "no erer",
|
||||
"9": "no aaareraa",
|
||||
"10": "no fdfd",
|
||||
"11": "fdgfdgfdg",
|
||||
"12": "aaaaa",
|
||||
"13": "aaaaa1",
|
||||
"14": "aaaaa2",
|
||||
"15": "aaaaa 3",
|
||||
"16": "ccccc",
|
||||
"17": "cccccdc",
|
||||
"18": "ddddd中国",
|
||||
"19": "html-a",
|
||||
"20": "html-b",
|
||||
"21": "html-c",
|
||||
"22": "from a",
|
||||
"23": "from b",
|
||||
"24": "from c1",
|
||||
"25": "from c2"
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
export default {
|
||||
"a1:aaaaa": 1,
|
||||
"no aaaaa": 2,
|
||||
"bbbbb": 3,
|
||||
"eeeeee": 4,
|
||||
"x:from a": 5,
|
||||
"中华人民共和国": 6,
|
||||
"a2:aaaaa": 7,
|
||||
"no erer": 8,
|
||||
"no aaareraa": 9,
|
||||
"no fdfd": 10,
|
||||
"fdgfdgfdg": 11,
|
||||
"aaaaa": 12,
|
||||
"aaaaa1": 13,
|
||||
"aaaaa2": 14,
|
||||
"aaaaa 3": 15,
|
||||
"ccccc": 16,
|
||||
"cccccdc": 17,
|
||||
"ddddd中国": 18,
|
||||
"html-a": 19,
|
||||
"html-b": 20,
|
||||
"html-c": 21,
|
||||
"from a": 22,
|
||||
"from b": 23,
|
||||
"from c1": 24,
|
||||
"from c2": 25
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
|
||||
const messageIds = require("./idMap")
|
||||
const { translate,i18n } = require("voerka-i18n")
|
||||
const defaultMessages = require("./cn.js")
|
||||
const i18nSettings = require("./settings.js")
|
||||
const formatters = require("../formatters")
|
||||
|
||||
|
||||
// 自动创建全局VoerkaI18n实例
|
||||
if(!globalThis.VoerkaI18n){
|
||||
globalThis.VoerkaI18n = new i18n(i18nSettings)
|
||||
}
|
||||
|
||||
let scope = {
|
||||
defaultLanguage: "cn", // 默认语言名称
|
||||
default: defaultMessages, // 默认语言包
|
||||
messages : defaultMessages, // 当前语言包
|
||||
idMap:messageIds, // 消息id映射列表
|
||||
formatters, // 当前作用域的格式化函数列表
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
global:{} // 引用全局VoerkaI18n配置
|
||||
}
|
||||
|
||||
let supportedlanguages = {}
|
||||
|
||||
messages["cn"]= defaultMessages
|
||||
|
||||
scope.loaders["en"] = ()=>import("./en.js")
|
||||
|
||||
scope.loaders["de"] = ()=>import("./de.js")
|
||||
|
||||
scope.loaders["jp"] = ()=>import("./jp.js")
|
||||
|
||||
|
||||
const t = ()=> translate.bind(scope)(...arguments)
|
||||
|
||||
// 注册当前作用域到全局VoerkaI18n实例
|
||||
VoerkaI18n.register(scope)
|
||||
|
||||
|
||||
module.exports.scope = scope
|
||||
module.exports.t = t
|
||||
|
@ -1,27 +0,0 @@
|
||||
module.exports = {
|
||||
"1": "a1:aaaaa",
|
||||
"2": "no aaaaa",
|
||||
"3": "bbbbb",
|
||||
"4": "eeeeee",
|
||||
"5": "x:from a",
|
||||
"6": "中华人民共和国",
|
||||
"7": "a2:aaaaa",
|
||||
"8": "no erer",
|
||||
"9": "no aaareraa",
|
||||
"10": "no fdfd",
|
||||
"11": "fdgfdgfdg",
|
||||
"12": "aaaaa",
|
||||
"13": "aaaaa1",
|
||||
"14": "aaaaa2",
|
||||
"15": "aaaaa 3",
|
||||
"16": "ccccc",
|
||||
"17": "cccccdc",
|
||||
"18": "ddddd中国",
|
||||
"19": "html-a",
|
||||
"20": "html-b",
|
||||
"21": "html-c",
|
||||
"22": "from a",
|
||||
"23": "from b",
|
||||
"24": "from c1",
|
||||
"25": "from c2"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
export default {
|
||||
"languages": [
|
||||
{
|
||||
"name": "en",
|
||||
"title": "英文"
|
||||
},
|
||||
{
|
||||
"name": "cn",
|
||||
"title": "中文",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"name": "de",
|
||||
"title": "德语"
|
||||
},
|
||||
{
|
||||
"name": "jp",
|
||||
"title": "日本語"
|
||||
}
|
||||
],
|
||||
"defaultLanguage": "cn",
|
||||
"activeLanguage": "cn",
|
||||
"namespaces": {
|
||||
"a": [
|
||||
"a"
|
||||
],
|
||||
"b": [
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
{
|
||||
"a1:aaaaa": {
|
||||
"en": "a1:aaaaa",
|
||||
"de": "德文:aaaaa",
|
||||
"jp": "a1:aaaaa",
|
||||
"$file": [
|
||||
"a\\a1.js"
|
||||
]
|
||||
},
|
||||
"no aaaaa": {
|
||||
"en": "no aaaaa",
|
||||
"de": "no aaaaa",
|
||||
"jp": "no aaaaa",
|
||||
"$file": [
|
||||
"a\\a1.js"
|
||||
]
|
||||
},
|
||||
"bbbbb": {
|
||||
"en": "bbbbb",
|
||||
"de": "bbbbb",
|
||||
"jp": "bbbbb",
|
||||
"$file": [
|
||||
"a\\a1.js"
|
||||
]
|
||||
},
|
||||
"eeeeee": {
|
||||
"en": "eeeeee",
|
||||
"de": "eeeeee",
|
||||
"jp": "eeeeee",
|
||||
"$file": [
|
||||
"a\\a1.js",
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"x:from a": {
|
||||
"en": "x:from a",
|
||||
"de": "x:from a",
|
||||
"jp": "x:from a",
|
||||
"$file": [
|
||||
"a\\a1.js"
|
||||
]
|
||||
},
|
||||
"中华人民共和国": {
|
||||
"en": "中华人民共和国",
|
||||
"de": "中华人民共和国",
|
||||
"jp": "中华人民共和国",
|
||||
"$file": [
|
||||
"a\\a1.js"
|
||||
]
|
||||
},
|
||||
"a2:aaaaa": {
|
||||
"en": "a2:aaaaa",
|
||||
"de": "a2:aaaaa",
|
||||
"jp": "a2:aaaaa",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"no erer": {
|
||||
"en": "no erer",
|
||||
"de": "no erer",
|
||||
"jp": "no erer",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"no aaareraa": {
|
||||
"en": "no aaareraa",
|
||||
"de": "no aaareraa",
|
||||
"jp": "no aaareraa",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"no fdfd": {
|
||||
"en": "no fdfd",
|
||||
"de": "no fdfd",
|
||||
"jp": "no fdfd",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"fdgfdgfdg": {
|
||||
"en": "fdgfdgfdg",
|
||||
"de": "fdgfdgfdg",
|
||||
"jp": "fdgfdgfdg",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"aaaaa": {
|
||||
"en": "aaaaa",
|
||||
"de": "aaaaa",
|
||||
"jp": "aaaaa",
|
||||
"$file": [
|
||||
"b\\b1.js"
|
||||
]
|
||||
},
|
||||
"bbbbb": {
|
||||
"en": "bbbbb",
|
||||
"de": "bbbbb",
|
||||
"jp": "bbbbb",
|
||||
"$file": [
|
||||
"b\\b1.js"
|
||||
]
|
||||
},
|
||||
"eeeeee": {
|
||||
"en": "eeeeee",
|
||||
"de": "eeeeee",
|
||||
"jp": "eeeeee",
|
||||
"$file": [
|
||||
"b\\b1.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
{
|
||||
"aaaaa": {
|
||||
"en": "aaaaa",
|
||||
"de": "aaaaa",
|
||||
"jp": "aaaaa",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"aaaaa1": {
|
||||
"en": "aaaaa1",
|
||||
"de": "aaaaa1",
|
||||
"jp": "aaaaa1",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"aaaaa2": {
|
||||
"en": "aaaaa2",
|
||||
"de": "aaaaa2",
|
||||
"jp": "aaaaa2",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"aaaaa 3": {
|
||||
"en": "aaaaa 3",
|
||||
"de": "aaaaa 3",
|
||||
"jp": "aaaaa 3",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"bbbbb": {
|
||||
"en": "bbbbb",
|
||||
"de": "bbbbb",
|
||||
"jp": "bbbbb",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"eeeeee": {
|
||||
"en": "eeeeee",
|
||||
"de": "eeeeee",
|
||||
"jp": "eeeeee",
|
||||
"$file": [
|
||||
"index.js",
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"ccccc": {
|
||||
"en": "ccccc",
|
||||
"de": "ccccc",
|
||||
"jp": "ccccc",
|
||||
"$file": [
|
||||
"c\\c1.js"
|
||||
]
|
||||
},
|
||||
"cccccdc": {
|
||||
"en": "cccccdc",
|
||||
"de": "cccccdc",
|
||||
"jp": "cccccdc",
|
||||
"$file": [
|
||||
"c\\c1.js"
|
||||
]
|
||||
},
|
||||
"a2:aaaaa": {
|
||||
"en": "a2:aaaaa",
|
||||
"de": "a2:aaaaa",
|
||||
"jp": "a2:aaaaa",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"no erer": {
|
||||
"en": "no erer",
|
||||
"de": "no erer",
|
||||
"jp": "no erer",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"no aaareraa": {
|
||||
"en": "no aaareraa",
|
||||
"de": "no aaareraa",
|
||||
"jp": "no aaareraa",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"no fdfd": {
|
||||
"en": "no fdfd",
|
||||
"de": "no fdfd",
|
||||
"jp": "no fdfd",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"fdgfdgfdg": {
|
||||
"en": "fdgfdgfdg",
|
||||
"de": "fdgfdgfdg",
|
||||
"jp": "fdgfdgfdg",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"ddddd中国": {
|
||||
"en": "ddddd中国",
|
||||
"de": "ddddd中国",
|
||||
"jp": "ddddd中国",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
},
|
||||
"html-a": {
|
||||
"en": "html-a",
|
||||
"de": "html-a",
|
||||
"jp": "html-a",
|
||||
"$file": [
|
||||
"c\\h1.html"
|
||||
]
|
||||
},
|
||||
"html-b": {
|
||||
"en": "html-b",
|
||||
"de": "html-b",
|
||||
"jp": "html-b",
|
||||
"$file": [
|
||||
"c\\h1.html"
|
||||
]
|
||||
},
|
||||
"html-c": {
|
||||
"en": "html-c",
|
||||
"de": "html-c",
|
||||
"jp": "html-c",
|
||||
"$file": [
|
||||
"c\\h1.html"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"from a": {
|
||||
"en": "from a",
|
||||
"de": "from a",
|
||||
"jp": "from a",
|
||||
"$file": [
|
||||
"a\\a2.js"
|
||||
]
|
||||
},
|
||||
"from b": {
|
||||
"en": "from b",
|
||||
"de": "from b",
|
||||
"jp": "from b",
|
||||
"$file": [
|
||||
"b\\b1.js"
|
||||
]
|
||||
},
|
||||
"from c1": {
|
||||
"en": "from c1",
|
||||
"de": "from c1",
|
||||
"jp": "from c1",
|
||||
"$file": [
|
||||
"c\\c1.js"
|
||||
]
|
||||
},
|
||||
"from c2": {
|
||||
"en": "from c2",
|
||||
"de": "from c2",
|
||||
"jp": "from c2",
|
||||
"$file": [
|
||||
"c\\c2.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
export default {
|
||||
"a":1,
|
||||
"b":2,
|
||||
"c{}{}":3,
|
||||
"d{a}{b}":4,
|
||||
"e":5
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
"a":1,
|
||||
"b":2,
|
||||
"c{}{}":3,
|
||||
"d{a}{b}":4,
|
||||
"e":5
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
|
||||
|
||||
const compile = require('../src/compile');
|
||||
const path = require("path")
|
||||
|
||||
|
||||
compile(path.resolve(__dirname,"./apps/app/languages"))
|
27
package.json
27
package.json
@ -5,25 +5,28 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"extract": "",
|
||||
"babeldemo": "babel ./demodata/a/a1.js --plugins=./src/babel-plugin-voerkai18n.js"
|
||||
"test:babelplugin": "jest babel",
|
||||
"test:extract": "jest extract",
|
||||
"test:translate": "jest translate",
|
||||
"demo:extract": "node ./packages/demo/extract.demo.js",
|
||||
"demo:compile": "node ./packages/demo/compile.demo.js",
|
||||
"demo:babel": "babel ./demodata/a/a1.js --plugins=./src/babel-plugin-voerkai18n.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"art-template": "^4.13.2",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.5",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^21.0.2",
|
||||
"dayjs": "^1.10.8",
|
||||
"deepmerge": "^4.2.2",
|
||||
"glob": "^7.2.0",
|
||||
"gulp": "^4.0.2",
|
||||
"logsets": "^1.0.6",
|
||||
"readjson": "^2.2.2",
|
||||
"through2": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.6",
|
||||
"@babel/core": "^7.17.5",
|
||||
"jest": "^27.5.1",
|
||||
"rollup": "^2.69.0",
|
||||
"rollup-plugin-clear": "^2.0.7",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// import {t } from "./languages"
|
||||
// import { nanoid } from "./nanoid"
|
||||
//const {t } = reuire("./languages")
|
||||
const { t,languages,scope } = reuire("./languages")
|
||||
|
||||
|
||||
|
||||
function output(){
|
||||
t("aaaaa")
|
||||
|
@ -1,7 +1,7 @@
|
||||
const babel = require("@babel/core");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const i18nPlugin = require("../src/babel-plugin-voerkai18n");
|
||||
const i18nPlugin = require("@voerkai18n/tools/babel-plugin-voerkai18n");
|
||||
|
||||
|
||||
const code = fs.readFileSync(path.join(__dirname, "./apps/app/index.js"), "utf-8");
|
7
packages/demo/compile.demo.js
Normal file
7
packages/demo/compile.demo.js
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
const compile = require('@voerkai18n/tools/compile');
|
||||
const path = require("path")
|
||||
|
||||
|
||||
compile(path.resolve(__dirname,"./apps/app/languages")).then(()=>{})
|
@ -1,5 +1,5 @@
|
||||
const gulp = require('gulp');
|
||||
const extract = require('../src/extract.plugin');
|
||||
const extract = require('@voerkai18n/tools/extract.plugin');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
@ -13,7 +13,15 @@ gulp.src([
|
||||
]).pipe(extract({
|
||||
debug:true,
|
||||
// output: path.join(soucePath , 'languages'),
|
||||
languages: [{name:'en',title:"英文"},{name:'cn',title:"中文",default:true},{name:'de',title:"德语"},{name:'jp',title:"日語"}],
|
||||
languages: [
|
||||
{name:'en',title:"英文"},
|
||||
{name:'cn',title:"中文",default:true},
|
||||
{name:'de',title:"德语"},
|
||||
{name:'fr',title:"法语"},
|
||||
{name:'es',title:"西班牙语"},
|
||||
{name:'it',title:"意大利语"},
|
||||
{name:'jp',title:"日語"}
|
||||
],
|
||||
// extractor:{
|
||||
// default:[new RegExp()], // 默认匹配器,当文件类型没有对应的提取器时使用
|
||||
// "*" : [new RegExp()], // 所有类型均会执行的提取器
|
20
packages/demo/package.json
Normal file
20
packages/demo/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@voerkai18n/demo",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "babel.plugin.demo.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@voerkai18n/runtime": "workspace:^1.0.0",
|
||||
"@voerkai18n/tools": "workspace:^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"gulp": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
@ -7,5 +7,10 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"gulp": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
16
packages/react/package.json
Normal file
16
packages/react/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@voerkai18n/react",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"gulp": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
60
packages/runtime/formatters.js
Normal file
60
packages/runtime/formatters.js
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 内置的格式化器
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 字典格式化器
|
||||
* 根据输入data的值,返回后续参数匹配的结果
|
||||
* dict(data,<value1>,<result1>,<value2>,<result1>,<value3>,<result1>,...)
|
||||
*
|
||||
*
|
||||
* dict(1,1,"one",2,"two",3,"three",4,"four") == "one"
|
||||
* dict(2,1,"one",2,"two",3,"three",4,"four") == "two"
|
||||
* dict(3,1,"one",2,"two",3,"three",4,"four") == "three"
|
||||
* dict(4,1,"one",2,"two",3,"three",4,"four") == "four"
|
||||
* // 无匹配时返回原始值
|
||||
* dict(5,1,"one",2,"two",3,"three",4,"four") == 5
|
||||
* // 无匹配时并且后续参数个数是奇数,则返回最后一个参数
|
||||
* dict(5,1,"one",2,"two",3,"three",4,"four","more") == "more"
|
||||
*
|
||||
* 在翻译中使用
|
||||
* I have { value | dict(1,"one",2,"two",3,"three",4,"four")} apples
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {...any} args
|
||||
* @returns
|
||||
*/
|
||||
function dict(value,...args){
|
||||
for(let i=0;i<args.length;i+=2){
|
||||
if(args[i]===value){
|
||||
return args[i+1]
|
||||
}
|
||||
}
|
||||
if(args.length >0 && (args.length % 2!==0)) return args[args.length-1]
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
"*":{
|
||||
$types:{
|
||||
Date:(value)=>value.toLocaleString()
|
||||
},
|
||||
time:(value)=> value.toLocaleTimeString(),
|
||||
date: (value)=> value.toLocaleDateString(),
|
||||
dict, //字典格式化器
|
||||
},
|
||||
cn:{
|
||||
$types:{
|
||||
Date:(value)=> `${value.getFullYear()}年${value.getMonth()+1}月${value.getDate()}日 ${value.getHours()}点${value.getMinutes()}分${value.getSeconds()}秒`
|
||||
},
|
||||
time:(value)=>`${value.getHours()}点${value.getMinutes()}分${value.getSeconds()}秒`,
|
||||
date: (value)=> `${value.getFullYear()}年${value.getMonth()+1}月${value.getDate()}日`,
|
||||
currency:(value)=>`${value}元`,
|
||||
},
|
||||
en:{
|
||||
currency:(value)=>`$${value}`,
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ function hasInterpolation(str){
|
||||
目前对参数采用简单的split(",")来解析,因为无法正确解析aaa(1,1,"dd,,dd")形式的参数
|
||||
在此场景下基本够用了,如果需要支持更复杂的参数解析,可以后续考虑使用正则表达式来解析
|
||||
|
||||
@returns [<formatterName>,[<formatterName>,[<arg>,<arg>,...]]]
|
||||
@returns [[<formatterName>,[<arg>,<arg>,...]]]
|
||||
*/
|
||||
function parseFormatters(formatters){
|
||||
if(!formatters) return []
|
||||
@ -89,29 +89,51 @@ function parseFormatters(formatters){
|
||||
|
||||
/**
|
||||
* 提取字符串中的插值变量
|
||||
* // [
|
||||
// {
|
||||
name:<变量名称>,formatters:[{name:<格式化器名称>,args:[<参数>,<参数>,....]]}],<匹配字符串>],
|
||||
// ....
|
||||
//
|
||||
* @param {*} str
|
||||
* @param {*} isFull =true 保留所有插值变量 =false 进行去重
|
||||
* @returns {Array} [[变量名称,[],match],[变量名称,[formatter,formatter,...],match],...]
|
||||
* @returns {Array}
|
||||
* [
|
||||
* {
|
||||
* name:"<变量名称>",
|
||||
* formatters:[
|
||||
* {name:"<格式化器名称>",args:[<参数>,<参数>,....]},
|
||||
* {name:"<格式化器名称>",args:[<参数>,<参数>,....]},
|
||||
* ],
|
||||
* match:"<匹配字符串>"
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
function getInterpolatedVars(str){
|
||||
let results = [], match
|
||||
while ((match = varWithPipeRegexp.exec(str)) !== null) {
|
||||
if (match.index === varWithPipeRegexp.lastIndex) {
|
||||
varWithPipeRegexp.lastIndex++;
|
||||
}
|
||||
const varname = match.groups.varname || ""
|
||||
// 解析格式化器和参数 = [<formatterName>,[<formatterName>,[<arg>,<arg>,...]]]
|
||||
const formatters = parseFormatters(match.groups.formatters)
|
||||
const varInfo = [varname,formatters,match]
|
||||
results.push(varInfo)
|
||||
}
|
||||
return results
|
||||
let vars = []
|
||||
forEachInterpolatedVars(str,(varName,formatters,match)=>{
|
||||
let varItem = {
|
||||
name:varName,
|
||||
formatters:formatters.map(([formatter,args])=>{
|
||||
return {
|
||||
name:formatter,
|
||||
args:args
|
||||
}
|
||||
}),
|
||||
match:match
|
||||
}
|
||||
if(vars.findIndex(varDef=>((varDef.name===varItem.name) && (varItem.formatters.toString() == varDef.formatters.toString())))===-1){
|
||||
vars.push(varItem)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
return vars
|
||||
}
|
||||
/**
|
||||
* 遍历插值变量,并进行替换插值变量
|
||||
* 遍历str中的所有插值变量传递给callback,将callback返回的结果替换到str中对应的位置
|
||||
* @param {*} str
|
||||
* @param {*} callback
|
||||
* @returns
|
||||
* @param {Function(<变量名称>,[formatters],match[0])} callback
|
||||
* @returns 返回替换后的字符串
|
||||
*/
|
||||
function forEachInterpolatedVars(str,callback,options={}){
|
||||
let result=str, match
|
||||
@ -126,9 +148,9 @@ function forEachInterpolatedVars(str,callback,options={}){
|
||||
if(typeof(callback)==="function"){
|
||||
try{
|
||||
if(opts.replaceAll){
|
||||
result=result.replaceAll(match[0],callback(varname,formatters))
|
||||
result=result.replaceAll(match[0],callback(varname,formatters,match[0]))
|
||||
}else{
|
||||
result=result.replace(match[0],callback(varname,formatters))
|
||||
result=result.replace(match[0],callback(varname,formatters,match[0]))
|
||||
}
|
||||
}catch{// callback函数可能会抛出异常,如果抛出异常,则中断匹配过程
|
||||
break
|
||||
@ -443,12 +465,11 @@ function translate(message) {
|
||||
}else if(arguments.length >= 2){
|
||||
vars = [...arguments].splice(1).map((arg,index)=>{
|
||||
try{
|
||||
return typeof(arg)==="function" ? arg() : arg
|
||||
}catch(e){
|
||||
return arg
|
||||
}
|
||||
// 位置参数中以第一个数值变量为复数变量
|
||||
if(isNumber(arg)) pluraValue = parseInt(arg)
|
||||
arg = typeof(arg)==="function" ? arg() : arg
|
||||
// 位置参数中以第一个数值变量为复数变量
|
||||
if(isNumber(arg)) pluraValue = parseInt(arg)
|
||||
}catch(e){ }
|
||||
return arg
|
||||
})
|
||||
|
||||
}
|
||||
@ -475,7 +496,7 @@ function translate(message) {
|
||||
// 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式
|
||||
if(Array.isArray(content) && content.length>0){
|
||||
// 如果存在复数命名变量,只取第一个复数变量
|
||||
if(pluraValue){ // 启用的是位置插值,pluraIndex=第一个数字变量的位置
|
||||
if(pluraValue!==null){ // 启用的是位置插值,pluraIndex=第一个数字变量的位置
|
||||
content = getPluraMessage(content,pluraValue)
|
||||
}else if(pluralVar.length>0){
|
||||
content = getPluraMessage(content,parseInt(vars(pluralVar[0])))
|
||||
@ -509,7 +530,7 @@ function translate(message) {
|
||||
* VoerkaI18n.off("change",(language)=>{})
|
||||
*
|
||||
* */
|
||||
class I18n{
|
||||
class I18nManager{
|
||||
static instance = null; // 单例引用
|
||||
callbacks = [] // 当切换语言时的回调事件
|
||||
constructor(settings={}){
|
||||
@ -650,7 +671,7 @@ function translate(message) {
|
||||
module.exports ={
|
||||
getInterpolatedVars,
|
||||
replaceInterpolatedVars,
|
||||
I18n,
|
||||
I18nManager,
|
||||
translate,
|
||||
languages,
|
||||
defaultLanguageSettings
|
@ -1,11 +1,14 @@
|
||||
{
|
||||
"name": "@voerkai18n/runtime",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"description": "Voerkai18n Runtime",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2"
|
||||
}
|
||||
}
|
||||
|
33
packages/runtime/rollup.config.js
Normal file
33
packages/runtime/rollup.config.js
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
import clear from 'rollup-plugin-clear'
|
||||
import { uglify } from "rollup-plugin-uglify";
|
||||
import { babel } from '@rollup/plugin-babel';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
|
||||
export default [
|
||||
{
|
||||
input: './index.js',
|
||||
output: [
|
||||
{
|
||||
file: 'dist/index.mjs',
|
||||
format:"es"
|
||||
},
|
||||
{
|
||||
file: 'dist/index.cjs',
|
||||
exports:"default",
|
||||
format:"cjs"
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
//resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers:"runtime",
|
||||
exclude: 'node_modules/**'
|
||||
}),
|
||||
clear({targets:["dist"]}),
|
||||
uglify()
|
||||
],
|
||||
external:["@babel/runtime"]
|
||||
}
|
||||
]
|
@ -31,32 +31,11 @@ function isPlainObject(obj){
|
||||
function isNumber(value){
|
||||
return !isNaN(parseInt(value))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 支持导入cjs和esm模块
|
||||
* @param {*} url
|
||||
*/
|
||||
async function importModule(url){
|
||||
try{
|
||||
return require(url)
|
||||
}catch(e){
|
||||
// 当加载出错时,尝试加载esm模块
|
||||
if(e.code === "MODULE_NOT_FOUND"){
|
||||
return await import(url)
|
||||
}else{
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
getDataTypeName,
|
||||
isNumber,
|
||||
isPlainObject,
|
||||
importModule
|
||||
isPlainObject
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { isPlainObject } = require("./utils");
|
||||
const { isPlainObject } = require("../runtime/utils");
|
||||
|
||||
const DefaultI18nPluginOptions = {
|
||||
translateFunctionName:"t", // 默认的翻译函数名称
|
@ -27,7 +27,7 @@ const readJson = require("readjson")
|
||||
const glob = require("glob")
|
||||
const createLogger = require("logsets")
|
||||
const path = require("path")
|
||||
const { getMessageId } = require("./utils")
|
||||
const { importModule } = require("./utils")
|
||||
const fs = require("fs")
|
||||
const logger = createLogger()
|
||||
const artTemplate = require("art-template")
|
||||
@ -40,19 +40,30 @@ function normalizeCompileOptions(opts={}) {
|
||||
}, opts)
|
||||
if(options.moduleType==="es") options.moduleType = "esm"
|
||||
if(options.moduleType==="cjs") options.moduleType = "commonjs"
|
||||
if(["commonjs","cjs","esm","es"].includes(options.moduleType)) options.moduleType = "esm"
|
||||
return opts;
|
||||
if(!["commonjs","cjs","esm","es"].includes(options.moduleType)) options.moduleType = "esm"
|
||||
return options;
|
||||
}
|
||||
|
||||
module.exports = function compile(langFolder,opts={}){
|
||||
module.exports =async function compile(langFolder,opts={}){
|
||||
const options = normalizeCompileOptions(opts);
|
||||
const { output,moduleType } = options;
|
||||
|
||||
//1. 加载多语言配置文件
|
||||
import(`file:///${path.join(langFolder,"settings.js")}`).then(module=>{
|
||||
|
||||
// 加载多语言配置文件
|
||||
const settingsFile = path.join(langFolder,"settings.js")
|
||||
try{
|
||||
// 读取多语言配置文件
|
||||
const module =await importModule(`file:///${settingsFile}`)
|
||||
const langSettings = module.default;
|
||||
let { languages,defaultLanguage,activeLanguage,namespaces } = langSettings
|
||||
|
||||
|
||||
logger.log("支持的语言\t: {}",languages.map(item=>`${item.title}(${item.name})`))
|
||||
logger.log("默认语言\t: {}",defaultLanguage)
|
||||
logger.log("激活语言\t: {}",activeLanguage)
|
||||
logger.log("名称空间\t: {}",Object.keys(namespaces).join(","))
|
||||
logger.log("模块类型\t: {}",moduleType)
|
||||
logger.log("")
|
||||
logger.log("编译结果输出至:{}",langFolder)
|
||||
|
||||
// 1. 合并生成最终的语言文件
|
||||
let messages = {} ,msgId =1
|
||||
glob.sync(path.join(langFolder,"translates/*.json")).forEach(file=>{
|
||||
@ -69,6 +80,8 @@ module.exports = function compile(langFolder,opts={}){
|
||||
logger.log("读取语言文件{}失败:{}",file,e.message)
|
||||
}
|
||||
})
|
||||
logger.log(" - 合成语言包文本,共{}条",Object.keys(messages).length)
|
||||
|
||||
// 2. 为每一个文本内容生成一个唯一的id
|
||||
let messageIds = {}
|
||||
Object.entries(messages).forEach(([msg,langs])=>{
|
||||
@ -88,6 +101,7 @@ module.exports = function compile(langFolder,opts={}){
|
||||
}else{
|
||||
fs.writeFileSync(langFile,`module.exports = ${JSON.stringify(langMessages,null,4)}`)
|
||||
}
|
||||
logger.log(" - 语言包文件: {}",path.basename(langFile))
|
||||
})
|
||||
|
||||
// 4. 生成id映射文件
|
||||
@ -97,29 +111,56 @@ module.exports = function compile(langFolder,opts={}){
|
||||
}else{
|
||||
fs.writeFileSync(idMapFile,`module.exports = ${JSON.stringify(messageIds,null,4)}`)
|
||||
}
|
||||
logger.log(" - idMap文件: {}",path.basename(idMapFile))
|
||||
|
||||
// 5. 生成编译后的访问入口文件
|
||||
const entryFile = path.join(langFolder,"index.js")
|
||||
const entryContent = artTemplate(path.join(__dirname,"templates","entry.js"), {languages,defaultLanguage,activeLanguage,namespaces,moduleType } )
|
||||
const entryContent = artTemplate(path.join(__dirname,"templates","entry.js"), {languages,defaultLanguage,activeLanguage,namespaces,moduleType,JSON } )
|
||||
fs.writeFileSync(entryFile,entryContent)
|
||||
logger.log(" - 访问入口文件: {}",path.basename(entryFile))
|
||||
|
||||
|
||||
// 6 . 生成编译后的格式化函数文件
|
||||
const formattersFile = path.join(langFolder,"formatters.js")
|
||||
if(!fs.existsSync(formattersFile)){
|
||||
const formattersContent = artTemplate(path.join(__dirname,"templates","formatters.js"), {languages,defaultLanguage,activeLanguage,namespaces,moduleType } )
|
||||
fs.writeFileSync(formattersFile,formattersContent)
|
||||
}
|
||||
// 7. 生成package.json
|
||||
logger.log(" - 格式化器:{}",path.basename(formattersFile))
|
||||
}else{ // 格式化器如果存在,则需要更改对应的模块类型
|
||||
let formattersContent = fs.readFileSync(formattersFile,"utf8").toString()
|
||||
if(moduleType == "esm"){
|
||||
formattersContent = formattersContent.replaceAll(/\s*module.exports\s*\=/g,"export default ")
|
||||
formattersContent = formattersContent.replaceAll(/\s*module.exports\./g,"export ")
|
||||
}else{
|
||||
formattersContent = formattersContent.replaceAll(/\s*export\s*default\s*/g,"module.exports = ")
|
||||
formattersContent = formattersContent.replaceAll(/\s*export\s*/g,"module.exports.")
|
||||
}
|
||||
fs.writeFileSync(formattersFile,formattersContent)
|
||||
logger.log(" - 更新格式化器:{}",path.basename(formattersFile))
|
||||
}
|
||||
|
||||
// 7. 重新生成settings ,需要确保settings.js匹配模块类型
|
||||
if(moduleType==="esm"){
|
||||
fs.writeFileSync(settingsFile,`export default ${JSON.stringify(langSettings,null,4)}`)
|
||||
}else{
|
||||
fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(langSettings,null,4)}`)
|
||||
}
|
||||
|
||||
// 8. 生成package.json
|
||||
const packageJsonFile = path.join(langFolder,"package.json")
|
||||
let packageJson = {}
|
||||
if(moduleType==="esm"){
|
||||
packageJson = {
|
||||
version:"1.0.0",
|
||||
type:"module",
|
||||
}
|
||||
}else{
|
||||
packageJson = {
|
||||
version:"1.0.0",
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(packageJsonFile,JSON.stringify(packageJson,null,4))
|
||||
})
|
||||
}catch(e){
|
||||
logger.log("加载多语言配置文件<{}>失败: {} ",settingsFile,e.message)
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ const path = require('path')
|
||||
const fs = require('fs')
|
||||
const readJson = require("readjson")
|
||||
const createLogger = require("logsets")
|
||||
const { replaceInterpolateVars,getDataTypeName } = require("./utils")
|
||||
const { replaceInterpolateVars,getDataTypeName } = require("../runtime/utils")
|
||||
const logger = createLogger()
|
||||
|
||||
// 捕获翻译文本的默认正则表达式
|
||||
@ -177,18 +177,23 @@ function getTranslateTexts(content,file,options){
|
||||
},texts)
|
||||
}
|
||||
|
||||
const defaultExtractLanguages = [
|
||||
{name:'en',title:"英文"},
|
||||
{name:'cn',title:"中文",default:true},
|
||||
{name:'de',title:"德语"},
|
||||
{name:'fr',title:"法语"},
|
||||
{name:'es',title:"西班牙语"},
|
||||
{name:'it',title:"意大利语"},
|
||||
{name:'jp',title:"日語"}
|
||||
]
|
||||
|
||||
function normalizeLanguageOptions(options){
|
||||
options = Object.assign({
|
||||
debug : true, // 输出调试信息,控制台输出相关的信息
|
||||
languages : [ // 支持的语言列表
|
||||
{name:"en",title:"英文"},
|
||||
{name:"cn",title:"中文",active:true,default:true} // 通过default指定默认语言
|
||||
],
|
||||
languages :defaultExtractLanguages, // 默认要支持的语言
|
||||
defaultLanguage: "cn", // 默认语言:指的是在源代码中的原始文本语言
|
||||
activeLanguage : "cn", // 当前激活语言:指的是当前启用的语言,比如在源码中使用中文,在默认激活的是英文
|
||||
extractor : { // 匹配翻译函数并提取内容的正则表达式
|
||||
//default : DefaultTranslateExtractor,
|
||||
"*" : DefaultTranslateExtractor,
|
||||
"html,vue,jsx" : DefaultHtmlAttrExtractor
|
||||
},
|
||||
@ -359,14 +364,8 @@ module.exports = function(options={}){
|
||||
if(!outputPath){
|
||||
outputPath = path.join(file.base,"languages")
|
||||
}
|
||||
// 跳过空文件
|
||||
if(file.isNull()){
|
||||
return callback()
|
||||
}
|
||||
// 跳过流文件
|
||||
if(file.isStream()){
|
||||
return callback()
|
||||
}
|
||||
if(file.isNull()) return callback()
|
||||
if(file.isStream()) return callback()
|
||||
|
||||
// 提取翻译文本
|
||||
try{
|
||||
@ -375,10 +374,10 @@ module.exports = function(options={}){
|
||||
fileCount++
|
||||
if(debug){
|
||||
const textCount = Object.values(texts).reduce((sum,item)=>sum+Object.keys(item).length,0)
|
||||
logger.log("Extract <{}>, found [{}] namespaces and {} texts.",file.relative,Object.keys(texts).join(),textCount)
|
||||
logger.log("Extract <{}>, found [{}] namespaces and {} messages.",file.relative,Object.keys(texts).join(),textCount)
|
||||
}
|
||||
}catch(err){
|
||||
logger.log("Error while extract text from <{}> : {}",file.relative,err.message)
|
||||
logger.log("Error while extract messages from <{}> : {}",file.relative,err.message)
|
||||
}
|
||||
|
||||
callback()
|
||||
@ -388,6 +387,7 @@ module.exports = function(options={}){
|
||||
logger.log(" - Total of files\t: {}",fileCount)
|
||||
logger.log(" - Output location\t: {}",outputPath)
|
||||
const translatesPath = path.join(outputPath,"translates")
|
||||
if(!fs.existsSync(outputPath)) fs.mkdirSync(outputPath)
|
||||
if(!fs.existsSync(translatesPath)) fs.mkdirSync(translatesPath)
|
||||
// 每个名称空间对应一个文件
|
||||
for(let [namespace,texts] of Object.entries(results)){
|
||||
@ -402,16 +402,20 @@ module.exports = function(options={}){
|
||||
logger.log(" Save language file : {}",path.relative(outputPath,langFile))
|
||||
}
|
||||
}
|
||||
// 将元数据生成到 i18n.meta.json
|
||||
const metaFile = path.join(outputPath,"settings.js")
|
||||
const meta = {
|
||||
languages : options.languages,
|
||||
defaultLanguage: options.defaultLanguage,
|
||||
activeLanguage : options.activeLanguage,
|
||||
namespaces : options.namespaces
|
||||
}
|
||||
fs.writeFileSync(metaFile,`export default ${JSON.stringify(meta,null,4)}`)
|
||||
logger.log(" - Generate language metadata : {}",metaFile)
|
||||
// 生成语言配置文件 settings.js , 仅当不存在时才生成
|
||||
const settingsFile = path.join(outputPath,"settings.js")
|
||||
if(!fs.existsSync(settingsFile)){
|
||||
const settings = {
|
||||
languages : options.languages,
|
||||
defaultLanguage: options.defaultLanguage,
|
||||
activeLanguage : options.activeLanguage,
|
||||
namespaces : options.namespaces
|
||||
}
|
||||
fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`)
|
||||
logger.log(" - Generate settings of language : {}",settingsFile)
|
||||
}else{
|
||||
logger.log(" - Settings of language already exists : {}",settingsFile)
|
||||
}
|
||||
callback()
|
||||
});
|
||||
}
|
23
packages/tools/index.js
Normal file
23
packages/tools/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
const { Command } = require('commander');
|
||||
const program = new Command();
|
||||
|
||||
|
||||
program
|
||||
.option('-d, --debug', '输出调试信息')
|
||||
|
||||
program
|
||||
.command('extract <source> [destination]')
|
||||
.description('扫描指定的项目目录,提取文件中的国际化字符串')
|
||||
.option('-d, --debug', '输出调试信息')
|
||||
.option('-l, --languages', '支持的语言', 'cn,en,de,fr,es,it,jp')
|
||||
.option('-d, --default', '默认语言', 'cn')
|
||||
.option('-a, --active', '激活语言', 'cn')
|
||||
.action((source, destination) => {
|
||||
console.log('clone command called');
|
||||
});
|
||||
|
||||
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
const options = program.opts();
|
26
packages/tools/package.json
Normal file
26
packages/tools/package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@voerkai18n/tools",
|
||||
"version": "1.0.0",
|
||||
"description": "VoerkaI18n Tools",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.17.6",
|
||||
"@babel/core": "^7.17.5",
|
||||
"art-template": "^4.13.2",
|
||||
"commander": "^9.0.0",
|
||||
"glob": "^7.2.0",
|
||||
"logsets": "^1.0.6",
|
||||
"readjson": "^2.2.2",
|
||||
"through2": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"gulp": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
@ -1,20 +1,18 @@
|
||||
{{if moduleTye === "esm"}}
|
||||
{{if moduleType === "esm"}}
|
||||
import messageIds from "./idMap.js"
|
||||
import { translate,i18n } from "voerka-i18n"
|
||||
import { translate,I18nManager } from "@voerkai18n/runtime"
|
||||
import defaultMessages from "./{{defaultLanguage}}.js"
|
||||
import scopeSettings from "./settings.js"
|
||||
import formatters from "../formatters"
|
||||
{{else}}
|
||||
const messageIds = require("./idMap")
|
||||
const { translate,i18n } = require("voerka-i18n")
|
||||
const { translate,i18n } = require("@voerkai18n/runtime")
|
||||
const defaultMessages = require("./{{defaultLanguage}}.js")
|
||||
const scopeSettings = require("./settings.js")
|
||||
const formatters = require("../formatters")
|
||||
const scopeSettings = require("./settings.js")
|
||||
{{/if}}
|
||||
|
||||
// 自动创建全局VoerkaI18n实例
|
||||
if(!globalThis.VoerkaI18n){
|
||||
globalThis.VoerkaI18n = new i18n(scopeSettings)
|
||||
globalThis.VoerkaI18n = new I18nManager(scopeSettings)
|
||||
}
|
||||
|
||||
let scope = {
|
||||
@ -22,7 +20,7 @@ let scope = {
|
||||
default: defaultMessages, // 默认语言包
|
||||
messages : defaultMessages, // 当前语言包
|
||||
idMap:messageIds, // 消息id映射列表
|
||||
formatters, // 当前作用域的格式化函数列表
|
||||
formatters:{}, // 当前作用域的格式化函数列表
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
global:{} // 引用全局VoerkaI18n配置,注册后自动引用
|
||||
}
|
||||
@ -35,14 +33,16 @@ scope.loaders["{{$value.name}}"] = ()=>import("./{{$value.name}}.js")
|
||||
{{/if}}{{/each}}
|
||||
|
||||
const t = ()=> translate.bind(scope)(...arguments)
|
||||
|
||||
const languages = {{@ JSON.stringify(languages,null,4) }}
|
||||
// 注册当前作用域到全局VoerkaI18n实例
|
||||
VoerkaI18n.register(scope)
|
||||
|
||||
{{if moduleTye === "esm"}}
|
||||
{{if moduleType === "esm"}}
|
||||
export languages
|
||||
export scope
|
||||
export t
|
||||
{{else}}
|
||||
module.exports.languages = languages
|
||||
module.exports.scope = scope
|
||||
module.exports.t = t
|
||||
{{/if}}
|
@ -76,7 +76,7 @@ const formatters = {
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
{{if moduleTye === "esm"}}
|
||||
{{if moduleType === "esm"}}
|
||||
export default formatters
|
||||
{{else}}
|
||||
module.exports = formatters
|
15
packages/tools/utils.js
Normal file
15
packages/tools/utils.js
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
async function importModule(url){
|
||||
try{
|
||||
return require(url)
|
||||
}catch{
|
||||
return await import(url)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
importModule
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@voerkai18n/scripts",
|
||||
"name": "@voerkai18n/vue",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
@ -7,5 +7,10 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"gulp": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
}
|
||||
}
|
1916
pnpm-lock.yaml
generated
1916
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
27
readme.md
27
readme.md
@ -151,7 +151,7 @@ t("第{}章",100) // == Chapter 100
|
||||
## 插值变量格式化
|
||||
|
||||
voerka-i18n支持对插值变量进行格式化
|
||||
|
||||
{{value | filter}} 过滤器语法类似管道操作符,它的上一个输出作为下一个输入。
|
||||
```javascript
|
||||
|
||||
new VoerkaI18n({
|
||||
@ -195,6 +195,31 @@ new VoerkaI18n({
|
||||
|
||||
t("今天是{date}",{date:new Date()})
|
||||
|
||||
```
|
||||
|
||||
### 字典
|
||||
|
||||
当翻译内容是一个{}时,启用字典插值模式。
|
||||
```javascript
|
||||
// 源文件
|
||||
// 假设网络状态取值:0=初始化,1=正在连接,2=已连接,3=正在断开.4=已断开,>4=未知
|
||||
|
||||
t("当前状态:{status}",{status})
|
||||
|
||||
// translates/default.json
|
||||
|
||||
{
|
||||
"当前状态:{status}":{
|
||||
cn:{0:"初始化",1:"正在连接",2:"已连接",3:"正在断开",4:"已断开",unknow:"未知"},
|
||||
en:{
|
||||
to:"Status:{}",
|
||||
vars:{"Init","Connecting","Connected","Disconnecting","Disconnected","unknow"}
|
||||
},
|
||||
en:"Status : {status | dict({0:"Init",1:"Connecting",2:"Connected",3:"Disconnecting",4:"Disconnected",5:"unknow"})}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 内置的格式化器
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
module.exports = {
|
||||
"*":{
|
||||
$types:{
|
||||
Date:(value)=>value.toLocaleString()
|
||||
},
|
||||
time:(value)=> value.toLocaleTimeString(),
|
||||
date: (value)=> value.toLocaleDateString(),
|
||||
},
|
||||
cn:{
|
||||
$types:{
|
||||
Date:(value)=> `${value.getFullYear()}年${value.getMonth()+1}月${value.getDate()}日 ${value.getHours()}点${value.getMinutes()}分${value.getSeconds()}秒`
|
||||
},
|
||||
time:(value)=>`${value.getHours()}点${value.getMinutes()}分${value.getSeconds()}秒`,
|
||||
date: (value)=> `${value.getFullYear()}年${value.getMonth()+1}月${value.getDate()}日`,
|
||||
currency:(value)=>`${value}元`,
|
||||
},
|
||||
en:{
|
||||
currency:(value)=>`$${value}`,
|
||||
}
|
||||
}
|
13
test/app.test.js
Normal file
13
test/app.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 测试demp app的语言运行环境
|
||||
*/
|
||||
const compile = require('../packages/tools/compile');
|
||||
const path = require("path")
|
||||
|
||||
|
||||
|
||||
test("导入多语言包",async ()=>{
|
||||
await compile(path.resolve(__dirname,'../packages/demo/apps/app/languages'),{moduleType:"commonjs"})
|
||||
const { t,scope,languages } = require(path.join(__dirname,'../packages/demo/apps/app/languages/index.js'));
|
||||
expect(t).toBeFunction()
|
||||
})
|
@ -1,7 +1,16 @@
|
||||
/**
|
||||
* 测试babel-plugin-voerkai18n插件
|
||||
*
|
||||
*
|
||||
* babel-plugin-voerkai18n插件可以根据idMap.js文件将源文件中的t函数翻译内容转化为对应的id
|
||||
* 并且可以自动导入t函数
|
||||
*
|
||||
*/
|
||||
|
||||
const babel = require("@babel/core");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const i18nPlugin = require("../src/babel-plugin-voerkai18n");
|
||||
const i18nPlugin = require("../packages/tools/babel-plugin-voerkai18n");
|
||||
|
||||
const code = `
|
||||
function test(a,b){
|
||||
@ -32,7 +41,8 @@ test("翻译函数转换",done=>{
|
||||
// 可以指定相对路径,也可以指定绝对路径
|
||||
autoImport:"#/languages",
|
||||
moduleType:"esm",
|
||||
// 此参数仅仅用于单元测试时使用,正常情况下,会读取location文件夹下的idMap", idMap:{
|
||||
// 此参数仅仅用于单元测试时使用,正常情况下,会读取location文件夹下的idMap",
|
||||
idMap:{
|
||||
"a":1,
|
||||
"b":2,
|
||||
"c{}{}":3,
|
||||
@ -53,7 +63,7 @@ test("读取esm格式的idMap后进行翻译函数转换",done=>{
|
||||
[
|
||||
i18nPlugin,
|
||||
{
|
||||
location:path.join(__dirname, "../demo/apps/lib1/languages"),
|
||||
location:path.join(__dirname, "../packages/demo/apps/lib1/languages"),
|
||||
autoImport:"#/languages",
|
||||
moduleType:"esm",
|
||||
}
|
||||
@ -70,7 +80,7 @@ test("读取commonjs格式的idMap后进行翻译函数转换",done=>{
|
||||
[
|
||||
i18nPlugin,
|
||||
{
|
||||
location:path.join(__dirname, "../demo/apps/lib2/languages"),
|
||||
location:path.join(__dirname, "../packages/demo/apps/lib2/languages"),
|
||||
autoImport:"#/languages",
|
||||
moduleType:"esm",
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
const extract = require("../src/extract.plugin");
|
||||
const extract = require("../packages/tools/extract.plugin");
|
||||
const gulp = require('gulp');
|
||||
const path = require('path');
|
||||
const Vinyl = require('vinyl');
|
||||
const { getTranslateTexts, normalizeLanguageOptions } = require("../src/extract.plugin");
|
||||
const { getTranslateTexts, normalizeLanguageOptions } = require("../packages/tools/extract.plugin");
|
||||
|
||||
|
||||
const languages = [{name:'en',title:"英文"},{name:'cn',title:"中文",default:true},{name:'de',title:"德语"},{name:'jp',title:"日本語"}]
|
||||
|
@ -1,23 +1,26 @@
|
||||
const dayjs = require('dayjs');
|
||||
const { getInterpolatedVars, replaceInterpolatedVars , translate} = require('../src/index.js')
|
||||
const { getInterpolatedVars, replaceInterpolatedVars , translate} = require('../packages/runtime/index.js')
|
||||
|
||||
const messages = {
|
||||
cn:{
|
||||
1:"你好",
|
||||
2:"现在是{}",
|
||||
3:"我出生于{year}年,今年{age}岁",
|
||||
4:"我有{}个朋友",
|
||||
},
|
||||
en :{
|
||||
1:"hello",
|
||||
2:"Now is {}",
|
||||
3:"I was born in {year}, now is {age} years old",
|
||||
4:["I have no friends","I have one friends","I have two friends","I have {} friends"],
|
||||
}
|
||||
}
|
||||
|
||||
const idMap = {
|
||||
"你好":1,
|
||||
"现在是{}":2,
|
||||
"我出生于{year}年,今年{age}岁":3
|
||||
"我出生于{year}年,今年{age}岁":3,
|
||||
"我有{}个朋友":4
|
||||
}
|
||||
|
||||
|
||||
@ -99,24 +102,48 @@ beforeEach(() => {
|
||||
|
||||
|
||||
test("获取翻译内容中的插值变量",done=>{
|
||||
const results = getInterpolatedVars("中华人民共和国成立于{date | year | time }年,首都是{city}市");
|
||||
expect(results.map(r=>r[0]).join(",")).toBe("date,city");
|
||||
expect(results[0][0]).toEqual("date");
|
||||
expect(results[0][1]).toEqual(["year","time"]);
|
||||
expect(results[1][0]).toEqual("city");
|
||||
expect(results[1][1]).toEqual([]);
|
||||
const results = getInterpolatedVars("中华人民共和国成立于{date | year(1,2) | time('a','b') | rel }年,首都是{city}市");
|
||||
expect(results.length).toEqual(2);
|
||||
//
|
||||
expect(results[0].name).toEqual("date");
|
||||
expect(results[0].formatters.length).toEqual(3);
|
||||
// year(1,2)
|
||||
expect(results[0].formatters[0].name).toEqual("year");
|
||||
expect(results[0].formatters[0].args).toEqual([1,2]);
|
||||
// time('a','b')
|
||||
expect(results[0].formatters[1].name).toEqual("time");
|
||||
expect(results[0].formatters[1].args).toEqual(["a","b"]);
|
||||
// rel
|
||||
expect(results[0].formatters[2].name).toEqual("rel");
|
||||
expect(results[0].formatters[2].args).toEqual([]);
|
||||
|
||||
|
||||
expect(results[1].name).toEqual("city");
|
||||
expect(results[1].formatters.length).toEqual(0);
|
||||
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
test("获取翻译内容中定义了重复的插值变量",done=>{
|
||||
const results = getInterpolatedVars("{a}{a}{a|x}{a|x}{a|x|y}{a|x|y}");
|
||||
expect(results.length).toEqual(3);
|
||||
expect(results[0][0]).toEqual("a");
|
||||
expect(results[0][1]).toEqual([]);
|
||||
expect(results[1][0]).toEqual("a");
|
||||
expect(results[1][1]).toEqual(["x"]);
|
||||
expect(results[2][0]).toEqual("a");
|
||||
expect(results[2][1]).toEqual(["x","y"]);
|
||||
expect(results[0].name).toEqual("a");
|
||||
expect(results[0].formatters.length).toEqual(0);
|
||||
|
||||
expect(results[1].name).toEqual("a");
|
||||
expect(results[1].formatters.length).toEqual(1);
|
||||
expect(results[1].formatters[0].name).toEqual("x");
|
||||
expect(results[1].formatters[0].args).toEqual([]);
|
||||
|
||||
|
||||
expect(results[2].name).toEqual("a");
|
||||
expect(results[2].formatters.length).toEqual(2);
|
||||
expect(results[2].formatters[0].name).toEqual("x");
|
||||
expect(results[2].formatters[0].args).toEqual([]);
|
||||
expect(results[2].formatters[1].name).toEqual("y");
|
||||
expect(results[2].formatters[1].args).toEqual([]);
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@ -223,3 +250,14 @@ test("切换到未知语言",done=>{
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
test("翻译复数支持",done=>{
|
||||
changeLanguage("en")
|
||||
expect(t("我有{}个朋友",0)).toBe("I have no friends");
|
||||
expect(t("我有{}个朋友",1)).toBe("I have one friends");
|
||||
expect(t("我有{}个朋友",2)).toBe("I have two friends");
|
||||
expect(t("我有{}个朋友",3)).toBe("I have 3 friends");
|
||||
expect(t("我有{}个朋友",4)).toBe("I have 4 friends");
|
||||
done()
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user