diff --git a/.gitignore b/.gitignore
index 7b7af42..fae58f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
/.vscode
/node_modules
node_modules
-/demo/apps/app/languages
-/demo/apps/*/languages
-/demo/*/node_modules
-/coverage
\ No newline at end of file
+/packages/**/node_modules
+/coverage
+/packages/apps/vueapp/src/languages
+/packages/apps/app/languages
\ No newline at end of file
diff --git a/package.json b/package.json
index 78cceef..8babf63 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,7 @@
"test:extract": "jest extract",
"test:translate": "jest translate",
"demo:extract": "node ./packages/demo/extract.demo.js",
- "demo:compile": "node ./packages/demo/compile.demo.js",
- "publish":""
+ "demo:compile": "node ./packages/demo/compile.demo.js"
},
"author": "",
"license": "ISC",
diff --git a/packages/apps/app/languages/index.js b/packages/apps/app/languages/index.js
index 9ca9114..1824519 100644
--- a/packages/apps/app/languages/index.js
+++ b/packages/apps/app/languages/index.js
@@ -1,12 +1,12 @@
const messageIds = require("./idMap")
-const { translate,I18nManager,i18nScope } = require("@voerkai18n/runtime")
+const { translate,i18nScope } = require("./runtime.js")
+
const formatters = require("./formatters.js")
const defaultMessages = require("./cn.js")
const activeMessages = defaultMessages
-
// 语言配置文件
const scopeSettings = {
"languages": [
@@ -40,6 +40,5 @@ const scope = new i18nScope({
const t = translate.bind(scope)
module.exports.t = t
-module.exports.scope = scope
-module.exports.i18nManager = VoerkaI18n
+module.exports.i18nScope = scope
diff --git a/packages/apps/app/package.json b/packages/apps/app/package.json
index d6b9f02..cc097dc 100644
--- a/packages/apps/app/package.json
+++ b/packages/apps/app/package.json
@@ -1 +1,6 @@
-{"type":"module","dependencies":{"@voerkai18n/cli":"workspace:^1.0.6","@voerkai18n/runtime":"^1.0.0"}}
\ No newline at end of file
+{
+ "dependencies": {
+ "@voerkai18n/cli": "workspace:^1.0.6",
+ "@voerkai18n/runtime": "^1.0.0"
+ }
+}
\ No newline at end of file
diff --git a/packages/apps/vueapp/src/App.vue b/packages/apps/vueapp/src/App.vue
index 7505026..9cfbc00 100644
--- a/packages/apps/vueapp/src/App.vue
+++ b/packages/apps/vueapp/src/App.vue
@@ -2,6 +2,8 @@
// This starter template is using Vue 3
diff --git a/packages/apps/vueapp/src/languages/index.js b/packages/apps/vueapp/src/languages/index.js
index 6d2e561..03127fa 100644
--- a/packages/apps/vueapp/src/languages/index.js
+++ b/packages/apps/vueapp/src/languages/index.js
@@ -1,6 +1,8 @@
import messageIds from "./idMap.js"
-import { translate,I18nManager,i18nScope } from "@voerkai18n/runtime"
+import runtime from "./runtime.js"
+const { translate,i18nScope } = runtime
+
import formatters from "./formatters.js"
import defaultMessages from "./cn.js"
const activeMessages = defaultMessages
@@ -40,7 +42,6 @@ const t = translate.bind(scope)
export {
t,
- i18nScope:scope,
- i18nManager:VoerkaI18n,
+ i18nScope as scope
}
diff --git a/packages/apps/vueapp/src/main.js b/packages/apps/vueapp/src/main.js
index 80e3bb2..03550a6 100644
--- a/packages/apps/vueapp/src/main.js
+++ b/packages/apps/vueapp/src/main.js
@@ -1,6 +1,5 @@
import { createApp } from 'vue'
import App from './App.vue'
-import { t, i18nScope } from './languages'
createApp(App).mount('#app')
diff --git a/packages/cli/compile.command.js b/packages/cli/compile.command.js
index 9a12715..a2997a1 100644
--- a/packages/cli/compile.command.js
+++ b/packages/cli/compile.command.js
@@ -43,7 +43,7 @@ function normalizeCompileOptions(opts={}) {
module.exports =async function compile(langFolder,opts={}){
const options = normalizeCompileOptions(opts);
- let { moduleType } = options;
+ let { moduleType,inlineRuntime } = options;
// 如果自动则会从当前项目读取,如果没有指定则会是esm
if(moduleType==="auto"){
moduleType = findModuleType(langFolder)
@@ -51,7 +51,11 @@ module.exports =async function compile(langFolder,opts={}){
const projectPackageJson = getCurrentPackageJson(langFolder)
// 加载多语言配置文件
const settingsFile = path.join(langFolder,"settings.json")
+
+
try{
+
+
// 读取多语言配置文件
const langSettings = fs.readJSONSync(settingsFile)
let { languages,defaultLanguage,activeLanguage,namespaces } = langSettings
@@ -80,8 +84,8 @@ module.exports =async function compile(langFolder,opts={}){
logger.log(t("读取语言文件{}失败:{}"),file,e.message)
}
})
- logger.log(t(" - 共合成{}条语言包文本"),Object.keys(messages).length)
-
+ logger.log(t(" - 共合成{}条文本"),Object.keys(messages).length)
+
// 2. 为每一个文本内容生成一个唯一的id
let messageIds = {}
Object.entries(messages).forEach(([msg,langs])=>{
@@ -113,8 +117,19 @@ module.exports =async function compile(langFolder,opts={}){
}
logger.log(t(" - idMap文件: {}"),path.basename(idMapFile))
+ // 嵌入运行时源码
+ if(inlineRuntime){
+ const runtimeSourceFolder = path.join(require.resolve("@voerkai18n/runtime"),"../..")
+ fs.copyFileSync(
+ path.join(runtimeSourceFolder,"dist",`runtime.${moduleType === 'esm' ? 'mjs' : 'cjs'}`),
+ path.join(langFolder,"runtime.js")
+ )
+ logger.log(t(" - 运行时: {}"),"runtime.js")
+ }
+
const templateContext = {
scopeId:projectPackageJson.name,
+ inlineRuntime,
languages,
defaultLanguage,
activeLanguage,
diff --git a/packages/cli/extract.plugin.js b/packages/cli/extract.plugin.js
index 66a1044..3b75cc5 100644
--- a/packages/cli/extract.plugin.js
+++ b/packages/cli/extract.plugin.js
@@ -10,7 +10,6 @@ const deepmerge = require("deepmerge")
const path = require('path')
const fs = require('fs-extra')
const createLogger = require("logsets")
-const { replaceInterpolateVars,getDataTypeName } = require("@voerkai18n/runtime")
const { findModuleType,createPackageJsonFile,t } = require("./utils")
const logger = createLogger()
diff --git a/packages/cli/index.js b/packages/cli/index.js
index 3bfc48b..351a0a9 100644
--- a/packages/cli/index.js
+++ b/packages/cli/index.js
@@ -46,7 +46,7 @@ program
.option('-r, --reset', t('重新生成当前项目的语言配置'))
.option('-lngs, --languages ', t('支持的语言列表'), ['cn','en'])
.option('-d, --defaultLanguage ', t('默认语言'), 'cn')
- .option('-i, --installRuntime', t('自动安装默认语言'),true)
+ // .option('-i, --installRuntime', t('自动安装默认语言'),true)
.option('-a, --activeLanguage ', t('激活语言'), 'cn')
.hook("preAction",async function(location){
const lang= process.env.LANGUAGE || "cn"
@@ -104,6 +104,7 @@ program
.command('compile')
.description(t('编译指定项目的语言包'))
.option('-d, --debug', t('输出调试信息'))
+ .option('--no-inline-runtime', t('不嵌入运行时源码'))
.option('-m, --moduleType [types]', t('输出模块类型,取值auto,esm,cjs'), 'esm')
.argument('[location]', t('工程项目所在目录'),"./")
.hook("preAction",async function(location){
diff --git a/packages/cli/init.command.js b/packages/cli/init.command.js
index d9d409d..afbbf53 100644
--- a/packages/cli/init.command.js
+++ b/packages/cli/init.command.js
@@ -56,10 +56,10 @@ module.exports = function(srcPath,{debug = true,languages=["cn","en"],defaultLan
fs.writeFileSync(settingsFile,JSON.stringify(settings,null,4))
// 自动安装运行时@voerkai18n/runtime
- if(installRuntime){
- logger.log(t("正在安装多语言运行时:{}"),"@voerkai18n/runtime")
- installVoerkai18nRuntim(srcPath)
- }
+ // if(installRuntime){
+ // logger.log(t("正在安装多语言运行时:{}"),"@voerkai18n/runtime")
+ // installVoerkai18nRuntim(srcPath)
+ // }
if(debug) {
logger.log(t("生成语言配置文件:{}"),"./languages/settings.json")
diff --git a/packages/cli/languages/cn.js b/packages/cli/languages/cn.js
index bea4eea..a70b30b 100644
--- a/packages/cli/languages/cn.js
+++ b/packages/cli/languages/cn.js
@@ -38,9 +38,12 @@ module.exports = {
"37": "模块类型\\t: {}",
"38": "编译结果输出至:{}",
"39": "读取语言文件{}失败:{}",
- "40": " - 共合成{}条语言包文本",
- "41": " - 语言包文件: {}",
- "42": " - idMap文件: {}",
- "43": " - 格式化器:{}",
- "44": "Now is { value | date | bjTime }"
+ "40": " - 语言包文件: {}",
+ "41": " - idMap文件: {}",
+ "42": " - 格式化器:{}",
+ "43": "Now is { value | date | bjTime }",
+ "44": " - 共合成{}条文本",
+ "45": " - 运行时: {}",
+ "46": "自动安装默认语言",
+ "47": "不嵌入运行时源码"
}
\ No newline at end of file
diff --git a/packages/cli/languages/en.js b/packages/cli/languages/en.js
index 1dfda9d..8ecdcc3 100644
--- a/packages/cli/languages/en.js
+++ b/packages/cli/languages/en.js
@@ -38,9 +38,12 @@ module.exports = {
"37": "Type of module\\t\\t: {}",
"38": "Compile to:{}",
"39": "Error while read language file{}: {}",
- "40": " - Total {} messages",
- "41": " - Language file: {}",
- "42": " - idMap file: {}",
- "43": " - Formatters: {}",
- "44": "Now is { value | date | bjTime }"
+ "40": " - Language file: {}",
+ "41": " - idMap file: {}",
+ "42": " - Formatters: {}",
+ "43": "Now is { value | date | bjTime }",
+ "44": " - Total{} messages",
+ "45": " - Runtime: {}",
+ "46": "Auto install default language",
+ "47": "Not inline runtime source"
}
\ No newline at end of file
diff --git a/packages/cli/languages/idMap.js b/packages/cli/languages/idMap.js
index 27275be..2b87550 100644
--- a/packages/cli/languages/idMap.js
+++ b/packages/cli/languages/idMap.js
@@ -38,9 +38,12 @@ module.exports = {
"模块类型\\t: {}": 37,
"编译结果输出至:{}": 38,
"读取语言文件{}失败:{}": 39,
- " - 共合成{}条语言包文本": 40,
- " - 语言包文件: {}": 41,
- " - idMap文件: {}": 42,
- " - 格式化器:{}": 43,
- "Now is { value | date | bjTime }": 44
+ " - 语言包文件: {}": 40,
+ " - idMap文件: {}": 41,
+ " - 格式化器:{}": 42,
+ "Now is { value | date | bjTime }": 43,
+ " - 共合成{}条文本": 44,
+ " - 运行时: {}": 45,
+ "自动安装默认语言": 46,
+ "不嵌入运行时源码": 47
}
\ No newline at end of file
diff --git a/packages/cli/languages/index.js b/packages/cli/languages/index.js
index 757d603..743e19b 100644
--- a/packages/cli/languages/index.js
+++ b/packages/cli/languages/index.js
@@ -1,6 +1,7 @@
const messageIds = require("./idMap")
-const { translate,I18nManager,i18nScope } = require("@voerkai18n/runtime")
+const { translate,i18nScope } = require("./runtime.js")
+
const formatters = require("./formatters.js")
const defaultMessages = require("./cn.js")
const activeMessages = defaultMessages
diff --git a/packages/cli/languages/runtime.js b/packages/cli/languages/runtime.js
new file mode 100644
index 0000000..cc4055d
--- /dev/null
+++ b/packages/cli/languages/runtime.js
@@ -0,0 +1,924 @@
+'use strict';
+
+/**
+*
+* 简单的事件触发器
+*
+*/
+
+var eventemitter = class EventEmitter{
+ constructor(){
+ this._callbacks = [];
+ }
+ on(callback){
+ if(this._callbacks.includes(callback)) return
+ this._callbacks.push(callback);
+ }
+ off(callback){
+ for(let i=0;icb(...args)));
+ }else {
+ await Promise.all(this._callbacks.map(cb=>cb(...args)));
+ }
+ }
+};
+
+var scope = class i18nScope {
+ constructor(options={},callback){
+ // 每个作用域都有一个唯一的id
+ this._id = options.id || (new Date().getTime().toString()+parseInt(Math.random()*1000));
+ this._languages = options.languages; // 当前作用域的语言列表
+ this._defaultLanguage = options.defaultLanguage || "cn"; // 默认语言名称
+ this._activeLanguage = options.activeLanguage; // 当前语言名称
+ this._default = options.default; // 默认语言包
+ this._messages = options.messages; // 当前语言包
+ this._idMap = options.idMap; // 消息id映射列表
+ this._formatters = options.formatters; // 当前作用域的格式化函数列表
+ this._loaders = options.loaders; // 异步加载语言文件的函数列表
+ this._global = null; // 引用全局VoerkaI18n配置,注册后自动引用
+ // 主要用来缓存格式化器的引用,当使用格式化器时可以直接引用,避免检索
+ this.$cache={
+ activeLanguage : null,
+ typedFormatters: {},
+ formatters : {},
+ };
+ // 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,
+ // 则使用当前作用域来初始化全局VoerkaI18n实例
+ if(!globalThis.VoerkaI18n){
+ const { I18nManager } = runtime;
+ globalThis.VoerkaI18n = new I18nManager({
+ defaultLanguage: this.defaultLanguage,
+ activeLanguage : this.activeLanguage,
+ languages: options.languages,
+ });
+ }
+ this.global = globalThis.VoerkaI18n;
+ // 正在加载语言包标识
+ this._loading=false;
+ // 在全局注册作用域
+ this.register(callback);
+ }
+ // 作用域
+ get id(){return this._id}
+ // 默认语言名称
+ get defaultLanguage(){return this._defaultLanguage}
+ // 默认语言名称
+ get activeLanguage(){return this._activeLanguage}
+ // 默认语言包
+ get default(){return this._default}
+ // 当前语言包
+ get messages(){return this._messages}
+ // 消息id映射列表
+ get idMap(){return this._idMap}
+ // 当前作用域的格式化函数列表
+ get formatters(){return this._formatters}
+ // 异步加载语言文件的函数列表
+ get loaders(){return this._loaders}
+ // 引用全局VoerkaI18n配置,注册后自动引用
+ get global(){return this._global}
+ set global(value){this._global = value;}
+ /**
+ * 在全局注册作用域
+ * @param {*} callback 当注册
+ */
+ register(callback){
+ if(!typeof(callback)==="function") callback = ()=>{};
+ this.global.register(this).then(callback).catch(callback);
+ }
+ registerFormatter(name,formatter,{language="*"}={}){
+ if(!typeof(formatter)==="function" || typeof(name)!=="string"){
+ throw new TypeError("Formatter must be a function")
+ }
+ if(DataTypes.includes(name)){
+ this.formatters[language].$types[name] = formatter;
+ }else {
+ this.formatters[language][name] = formatter;
+ }
+ }
+ /**
+ * 回退到默认语言
+ */
+ _fallback(){
+ this._messages = this._default;
+ this._activeLanguage = this.defaultLanguage;
+ }
+ /**
+ * 刷新当前语言包
+ * @param {*} newLanguage
+ */
+ async refresh(newLanguage){
+ this._loading = Promise.resolve();
+ if(!newLanguage) newLanguage = this.activeLanguage;
+ // 默认语言,默认语言采用静态加载方式,只需要简单的替换即可
+ if(newLanguage === this.defaultLanguage){
+ this._messages = this._default;
+ return
+ }
+ // 非默认语言需要异步加载语言包文件,加载器是一个异步函数
+ // 如果没有加载器,则无法加载语言包,因此回退到默认语言
+ const loader = this.loaders[newLanguage];
+ if(typeof(loader) === "function"){
+ try{
+ this._messages = (await loader()).default;
+ this._activeLanguage = newLanguage;
+ }catch(e){
+ console.warn(`Error while loading language <${newLanguage}> on i18nScope(${this.id}): ${e.message}`);
+ this._fallback();
+ }
+ }else {
+ this._fallback();
+ }
+ }
+ // 以下方法引用全局VoerkaI18n实例的方法
+ get on(){return this.global.on.bind(this.global)}
+ get off(){return this.global.off.bind(this.global)}
+ get offAll(){return this.global.offAll.bind(this.global)}
+ get change(){
+ return this.global.change.bind(this.global)
+ }
+};
+
+/**
+ * 内置的格式化器
+ *
+ */
+
+/**
+ * 字典格式化器
+ * 根据输入data的值,返回后续参数匹配的结果
+ * dict(data,,,,,,,...)
+ *
+ *
+ * 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;i0 && (args.length % 2!==0)) return args[args.length-1]
+ return value
+}
+
+var formatters$1 = {
+ "*":{
+ $types:{
+ Date:(value)=>value.toLocaleString()
+ },
+ time:(value)=> value.toLocaleTimeString(),
+ shorttime:(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()}秒`
+ },
+ shortime:(value)=> value.toLocaleTimeString(),
+ time:(value)=>`${value.getHours()}点${value.getMinutes()}分${value.getSeconds()}秒`,
+ date: (value)=> `${value.getFullYear()}年${value.getMonth()+1}月${value.getDate()}日`,
+ shortdate: (value)=> `${value.getFullYear()}-${value.getMonth()+1}-${value.getDate()}`,
+ currency:(value)=>`${value}元`,
+ },
+ en:{
+ currency:(value)=>{
+ return `$${value}`
+ }
+ }
+};
+
+const EventEmitter = eventemitter;
+const i18nScope = scope;
+let formatters = formatters$1;
+
+
+// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
+// 不支持参数: let varWithPipeRegexp = /\{\s*(?\w+)?(?(\s*\|\s*\w*\s*)*)\s*\}/g
+
+// 支持参数: { var | formatter(x,x,..) | formatter }
+let varWithPipeRegexp = /\{\s*(?\w+)?(?(\s*\|\s*\w*(\(.*\)){0,1}\s*)*)\s*\}/g;
+
+// 有效的语言名称列表
+const languages = ["af","am","ar-dz","ar-iq","ar-kw","ar-ly","ar-ma","ar-sa","ar-tn","ar","az","be","bg","bi","bm","bn","bo","br","bs","ca","cs","cv","cy","da","de-at","de-ch","de","dv","el","en-au","en-ca","en-gb","en-ie","en-il","en-in","en-nz","en-sg","en-tt","en","eo","es-do","es-mx","es-pr","es-us","es","et","eu","fa","fi","fo","fr-ca","fr-ch","fr","fy","ga","gd","gl","gom-latn","gu","he","hi","hr","ht","hu","hy-am","id","is","it-ch","it","ja","jv","ka","kk","km","kn","ko","ku","ky","lb","lo","lt","lv","me","mi","mk","ml","mn","mr","ms-my","ms","mt","my","nb","ne","nl-be","nl","nn","oc-lnc","pa-in","pl","pt-br","pt","ro","ru","rw","sd","se","si","sk","sl","sq","sr-cyrl","sr","ss","sv-fi","sv","sw","ta","te","tet","tg","th","tk","tl-ph","tlh","tr","tzl","tzm-latn","tzm","ug-cn","uk","ur","uz-latn","uz","vi","x-pseudo","yo","zh-cn","zh-hk","zh-tw","zh"];
+
+/**
+ * 考虑到通过正则表达式进行插件的替换可能较慢,因此提供一个简单方法来过滤掉那些
+ * 不需要进行插值处理的字符串
+ * 原理很简单,就是判断一下是否同时具有{和}字符,如果有则认为可能有插值变量,如果没有则一定没有插件变量,则就不需要进行正则匹配
+ * 从而可以减少不要的正则匹配
+ * 注意:该方法只能快速判断一个字符串不包括插值变量
+ * @param {*} str
+ * @returns {boolean} true=可能包含插值变量,
+ */
+function hasInterpolation(str){
+ return str.includes("{") && str.includes("}")
+}
+const DataTypes$1 = ["String","Number","Boolean","Object","Array","Function","Error","Symbol","RegExp","Date","Null","Undefined","Set","Map","WeakSet","WeakMap"];
+
+/**
+ * 获取指定变量类型名称
+ * getDataTypeName(1) == Number
+ * getDataTypeName("") == String
+ * getDataTypeName(null) == Null
+ * getDataTypeName(undefined) == Undefined
+ * getDataTypeName(new Date()) == Date
+ * getDataTypeName(new Error()) == Error
+ *
+ * @param {*} v
+ * @returns
+ */
+ function getDataTypeName(v){
+ if (v === null) return 'Null'
+ if (v === undefined) return 'Undefined'
+ if(typeof(v)==="function") return "Function"
+ return v.constructor && v.constructor.name;
+}function isPlainObject(obj){
+ if (typeof obj !== 'object' || obj === null) return false;
+ var proto = Object.getPrototypeOf(obj);
+ if (proto === null) return true;
+ var baseProto = proto;
+
+ while (Object.getPrototypeOf(baseProto) !== null) {
+ baseProto = Object.getPrototypeOf(baseProto);
+ }
+ return proto === baseProto;
+}
+function isNumber(value){
+ return !isNaN(parseInt(value))
+}
+
+
+
+/**
+ * 简单进行对象合并
+ *
+ * options={
+ * array:0 , // 数组合并策略,0-替换,1-合并,2-去重合并
+ * object:0, // 对象合并策略,0-替换,1-合并,2-去重合并
+ * }
+ *
+ * @param {*} toObj
+ * @param {*} formObj
+ * @returns 合并后的对象
+ */
+function deepMerge(toObj,formObj,options={}){
+ let results = Object.assign({},toObj);
+ Object.entries(formObj).forEach(([key,value])=>{
+ if(key in results){
+ if(typeof value === "object" && value !== null){
+ if(Array.isArray(value)){
+ if(options.array === 0){
+ results[key] = value;
+ }else if(options.array === 1){
+ results[key] = [...results[key],...value];
+ }else if(options.array === 2){
+ results[key] = [...new Set([...results[key],...value])];
+ }
+ }else {
+ results[key] = deepMerge(results[key],value,options);
+ }
+ }else {
+ results[key] = value;
+ }
+ }else {
+ results[key] = value;
+ }
+ });
+ return results
+}
+
+
+/**
+ 通过正则表达式对原始文本内容进行解析匹配后得到的
+ formatters="| aaa(1,1) | bbb "
+
+ 需要统一解析为
+
+ [
+ [aaa,[1,1]], // [formatter'name,[args,...]]
+ [bbb,[]],
+ ]
+
+ formatters="| aaa(1,1,"dddd") | bbb "
+
+ 目前对参数采用简单的split(",")来解析,因为无法正确解析aaa(1,1,"dd,,dd")形式的参数
+ 在此场景下基本够用了,如果需要支持更复杂的参数解析,可以后续考虑使用正则表达式来解析
+
+ @returns [[,[,,...]]]
+ */
+function parseFormatters(formatters){
+ if(!formatters) return []
+ // 1. 先解析为 ["aaa()","bbb"]形式
+ let result = formatters.trim().substr(1).trim().split("|").map(r=>r.trim());
+
+ // 2. 解析格式化器参数
+ return result.map(formatter=>{
+ let firstIndex = formatter.indexOf("(");
+ let lastIndex = formatter.lastIndexOf(")");
+ if(firstIndex!==-1 && lastIndex!==-1){ // 带参数的格式化器
+ const argsContent = formatter.substr(firstIndex+1,lastIndex-firstIndex-1).trim();
+ let args = argsContent=="" ? [] : argsContent.split(",").map(arg=>{
+ arg = arg.trim();
+ if(!isNaN(parseInt(arg))){
+ return parseInt(arg) // 数字
+ }else if((arg.startsWith('\"') && arg.endsWith('\"')) || (arg.startsWith('\'') && arg.endsWith('\'')) ){
+ return arg.substr(1,arg.length-2) // 字符串
+ }else if(arg.toLowerCase()==="true" || arg.toLowerCase()==="false"){
+ return arg.toLowerCase()==="true" // 布尔值
+ }else if((arg.startsWith('{') && arg.endsWith('}')) || (arg.startsWith('[') && arg.endsWith(']'))){
+ try{
+ return JSON.parse(arg)
+ }catch(e){
+ return String(arg)
+ }
+ }else {
+ return String(arg)
+ }
+ });
+ return [formatter.substr(0,firstIndex),args]
+ }else {// 不带参数的格式化器
+ return [formatter,[]]
+ }
+ })
+}
+
+/**
+ * 提取字符串中的插值变量
+ * // [
+ // {
+ name:<变量名称>,formatters:[{name:<格式化器名称>,args:[<参数>,<参数>,....]]}],<匹配字符串>],
+ // ....
+ //
+ * @param {*} str
+ * @param {*} isFull =true 保留所有插值变量 =false 进行去重
+ * @returns {Array}
+ * [
+ * {
+ * name:"<变量名称>",
+ * formatters:[
+ * {name:"<格式化器名称>",args:[<参数>,<参数>,....]},
+ * {name:"<格式化器名称>",args:[<参数>,<参数>,....]},
+ * ],
+ * match:"<匹配字符串>"
+ * },
+ * ...
+ * ]
+ */
+function getInterpolatedVars(str){
+ 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 {Function(<变量名称>,[formatters],match[0])} callback
+ * @returns 返回替换后的字符串
+ */
+function forEachInterpolatedVars(str,callback,options={}){
+ let result=str, match;
+ let opts = Object.assign({
+ replaceAll:true, // 是否替换所有插值变量,当使用命名插值时应置为true,当使用位置插值时应置为false
+ },options);
+ varWithPipeRegexp.lastIndex=0;
+ while ((match = varWithPipeRegexp.exec(result)) !== null) {
+ const varname = match.groups.varname || "";
+ // 解析格式化器和参数 = [,[,[,,...]]]
+ const formatters = parseFormatters(match.groups.formatters);
+ if(typeof(callback)==="function"){
+ try{
+ if(opts.replaceAll){
+ result=result.replaceAll(match[0],callback(varname,formatters,match[0]));
+ }else {
+ result=result.replace(match[0],callback(varname,formatters,match[0]));
+ }
+ }catch{// callback函数可能会抛出异常,如果抛出异常,则中断匹配过程
+ break
+ }
+ }
+ varWithPipeRegexp.lastIndex=0;
+ }
+ return result
+}
+
+function resetScopeCache(scope,activeLanguage=null){
+ scope.$cache = {activeLanguage,typedFormatters:{},formatters:{}};
+}
+/**
+ * 取得指定数据类型的默认格式化器
+ *
+ * 可以为每一个数据类型指定一个默认的格式化器,当传入插值变量时,
+ * 会自动调用该格式化器来对值进行格式化转换
+
+ const formatters = {
+ "*":{
+ $types:{...} // 在所有语言下只作用于特定数据类型的格式化器
+ }, // 在所有语言下生效的格式化器
+ cn:{
+ $types:{
+ [数据类型]:(value)=>{...},
+ },
+ [格式化器名称]:(value)=>{...},
+ [格式化器名称]:(value)=>{...},
+ [格式化器名称]:(value)=>{...},
+ },
+ }
+ * @param {*} scope
+ * @param {*} activeLanguage
+ * @param {*} dataType 数字类型
+ * @returns {Function} 格式化函数
+ */
+function getDataTypeDefaultFormatter(scope,activeLanguage,dataType){
+ if(!scope.$cache) resetScopeCache(scope);
+ if(scope.$cache.activeLanguage === activeLanguage) {
+ if(dataType in scope.$cache.typedFormatters) return scope.$cache.typedFormatters[dataType]
+ }else {// 当语言切换时清空缓存
+ resetScopeCache(scope,activeLanguage);
+ }
+
+ // 先在当前作用域中查找,再在全局查找
+ const targets = [scope.formatters,scope.global.formatters];
+ for(const target of targets){
+ if(!target) continue
+ // 优先在当前语言的$types中查找
+ if((activeLanguage in target) && isPlainObject(target[activeLanguage].$types)){
+ let formatters = target[activeLanguage].$types;
+ if(dataType in formatters && typeof(formatters[dataType])==="function"){
+ return scope.$cache.typedFormatters[dataType] = formatters[dataType]
+ }
+ }
+ // 在所有语言的$types中查找
+ if(("*" in target) && isPlainObject(target["*"].$types)){
+ let formatters = target["*"].$types;
+ if(dataType in formatters && typeof(formatters[dataType])==="function"){
+ return scope.$cache.typedFormatters[dataType] = formatters[dataType]
+ }
+ }
+ }
+}
+
+/**
+ * 获取指定名称的格式化器函数
+ * @param {*} scope
+ * @param {*} activeLanguage
+ * @param {*} name 格式化器名称
+ * @returns {Function} 格式化函数
+ */
+function getFormatter(scope,activeLanguage,name){
+ // 缓存格式化器引用,避免重复检索
+ if(!scope.$cache) resetScopeCache(scope);
+ if(scope.$cache.activeLanguage === activeLanguage) {
+ if(name in scope.$cache.formatters) return scope.$cache.formatters[name]
+ }else {// 当语言切换时清空缓存
+ resetScopeCache(scope,activeLanguage);
+ }
+ // 先在当前作用域中查找,再在全局查找
+ const targets = [scope.formatters,scope.global.formatters];
+ for(const target of targets){
+ // 优先在当前语言查找
+ if(activeLanguage in target){
+ let formatters = target[activeLanguage] || {};
+ if((name in formatters) && typeof(formatters[name])==="function") return scope.$cache.formatters[name] = formatters[name]
+ }
+ // 在所有语言的$types中查找
+ let formatters = target["*"] || {};
+ if((name in formatters) && typeof(formatters[name])==="function") return scope.$cache.formatters[name] = formatters[name]
+ }
+}
+
+/**
+ * 执行格式化器并返回结果
+ * @param {*} value
+ * @param {*} formatters 多个格式化器顺序执行,前一个输出作为下一个格式化器的输入
+ */
+function executeFormatter(value,formatters){
+ if(formatters.length===0) return value
+ let result = value;
+ try{
+ for(let formatter of formatters){
+ if(typeof(formatter) === "function") {
+ result = formatter(result);
+ }else {// 如果碰到无效的格式化器,则跳过过续的格式化器
+ return result
+ }
+ }
+ }catch(e){
+ console.error(`Error while execute i18n formatter for ${value}: ${e.message} ` );
+ }
+ return result
+}
+/**
+ * 将 [[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器转化为
+ *
+ *
+ *
+ * @param {*} scope
+ * @param {*} activeLanguage
+ * @param {*} formatters
+ */
+function buildFormatters(scope,activeLanguage,formatters){
+ let results = [];
+ for(let formatter of formatters){
+ if(formatter[0]){
+ const func = getFormatter(scope,activeLanguage,formatter[0]);
+ if(typeof(func)==="function"){
+ results.push((v)=>{
+ return func(v,...formatter[1])
+ });
+ }else {
+ // 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
+ // 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
+ results.push((v)=>{
+ if(typeof(v[formatter[0]])==="function"){
+ return v[formatter[0]].call(v,...formatter[1])
+ }else {
+ return v
+ }
+ });
+ }
+ }
+ }
+ return results
+}
+
+/**
+ * 将value经过格式化器处理后返回
+ * @param {*} scope
+ * @param {*} activeLanguage
+ * @param {*} formatters
+ * @param {*} value
+ * @returns
+ */
+function getFormattedValue(scope,activeLanguage,formatters,value){
+ // 1. 取得格式化器函数列表
+ const formatterFuncs = buildFormatters(scope,activeLanguage,formatters);
+ // 2. 查找每种数据类型默认格式化器,并添加到formatters最前面,默认数据类型格式化器优先级最高
+ const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value));
+ if(defaultFormatter){
+ formatterFuncs.splice(0,0,defaultFormatter);
+ }
+ // 3. 执行格式化器
+ value = executeFormatter(value,formatterFuncs);
+ return value
+}
+
+/**
+ * 字符串可以进行变量插值替换,
+ * replaceInterpolatedVars("<模板字符串>",{变量名称:变量值,变量名称:变量值,...})
+ * replaceInterpolatedVars("<模板字符串>",[变量值,变量值,...])
+ * replaceInterpolatedVars("<模板字符串>",变量值,变量值,...])
+ *
+- 当只有两个参数并且第2个参数是{}时,将第2个参数视为命名变量的字典
+ replaceInterpolatedVars("this is {a}+{b},{a:1,b:2}) --> this is 1+2
+- 当只有两个参数并且第2个参数是[]时,将第2个参数视为位置参数
+ replaceInterpolatedVars"this is {}+{}",[1,2]) --> this is 1+2
+- 普通位置参数替换
+ replaceInterpolatedVars("this is {a}+{b}",1,2) --> this is 1+2
+-
+this == scope == { formatters: {}, ... }
+* @param {*} template
+* @returns
+*/
+function replaceInterpolatedVars(template,...args) {
+ const scope = this;
+ // 当前激活语言
+ const activeLanguage = scope.global.activeLanguage;
+
+ // 没有变量插值则的返回原字符串
+ if(args.length===0 || !hasInterpolation(template)) return template
+
+ // ****************************变量插值****************************
+ if(args.length===1 && isPlainObject(args[0])){
+ // 读取模板字符串中的插值变量列表
+ // [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
+ let varValues = args[0];
+ return forEachInterpolatedVars(template,(varname,formatters)=>{
+ let value = (varname in varValues) ? varValues[varname] : '';
+ return getFormattedValue(scope,activeLanguage,formatters,value)
+ })
+ }else {
+ // ****************************位置插值****************************
+ // 如果只有一个Array参数,则认为是位置变量列表,进行展开
+ const params=(args.length===1 && Array.isArray(args[0])) ? [...args[0]] : args;
+ if(params.length===0) return template // 没有变量则不需要进行插值处理,返回原字符串
+ let i = 0;
+ return forEachInterpolatedVars(template,(varname,formatters)=>{
+ if(params.length>i){
+ return getFormattedValue(scope,activeLanguage,formatters,params[i++])
+ }else {
+ throw new Error() // 抛出异常,停止插值处理
+ }
+ },{replaceAll:false})
+
+ }
+}
+
+// 默认语言配置
+const defaultLanguageSettings = {
+ defaultLanguage: "cn",
+ activeLanguage: "cn",
+ languages:[
+ {name:"cn",title:"中文",default:true},
+ {name:"en",title:"英文"}
+ ],
+ formatters
+};
+
+function isMessageId(content){
+ return parseInt(content)>0
+}
+/**
+ * 根据值的单数和复数形式,从messages中取得相应的消息
+ *
+ * @param {*} messages 复数形式的文本内容 = [<=0时的内容>,<=1时的内容>,<=2时的内容>,...]
+ * @param {*} value
+ */
+function getPluraMessage(messages,value){
+ try{
+ if(Array.isArray(messages)){
+ return messages.length > value ? messages[value] : messages[messages.length-1]
+ }else {
+ return messages
+ }
+ }catch{
+ return Array.isArray(messages) ? messages[0] : messages
+ }
+}
+function escape(str){
+ return str.replaceAll(/\\(?![trnbvf'"]{1})/g,"\\\\")
+ .replaceAll("\t","\\t")
+ .replaceAll("\n","\\n")
+ .replaceAll("\b","\\b")
+ .replaceAll("\r","\\r")
+ .replaceAll("\f","\\f")
+ .replaceAll("\'","\\'")
+ .replaceAll('\"','\\"')
+ .replaceAll('\v','\\v')
+}
+function unescape(str){
+ return str
+ .replaceAll("\\t","\t")
+ .replaceAll("\\n","\n")
+ .replaceAll("\\b","\b")
+ .replaceAll("\\r","\r")
+ .replaceAll("\\f","\f")
+ .replaceAll("\\'","\'")
+ .replaceAll('\\"','\"')
+ .replaceAll('\\v','\v')
+ .replaceAll(/\\\\(?![trnbvf'"]{1})/g,"\\")
+}
+/**
+ * 翻译函数
+ *
+* translate("要翻译的文本内容") 如果默认语言是中文,则不会进行翻译直接返回
+* translate("I am {} {}","man") == I am man 位置插值
+* translate("I am {p}",{p:"man"}) 字典插值
+* translate("total {$count} items", {$count:1}) //复数形式
+* translate("total {} {} {} items",a,b,c) // 位置变量插值
+ *
+ * this===scope 当前绑定的scope
+ *
+ */
+function translate(message) {
+ const scope = this;
+ const activeLanguage = scope.global.activeLanguage;
+ let content = message;
+ let vars=[]; // 插值变量列表
+ let pluralVars= []; // 复数变量
+ let pluraValue = null; // 复数值
+ if(!typeof(message)==="string") return message
+ try{
+ // 1. 预处理变量: 复数变量保存至pluralVars中 , 变量如果是Function则调用
+ if(arguments.length === 2 && isPlainObject(arguments[1])){
+ Object.entries(arguments[1]).forEach(([name,value])=>{
+ if(typeof(value)==="function"){
+ try{
+ vars[name] = value();
+ }catch(e){
+ vars[name] = value;
+ }
+ }
+ // 以$开头的视为复数变量
+ if(name.startsWith("$") && typeof(vars[name])==="number") pluralVars.push(name);
+ });
+ vars = [arguments[1]];
+ }else if(arguments.length >= 2){
+ vars = [...arguments].splice(1).map((arg,index)=>{
+ try{
+ arg = typeof(arg)==="function" ? arg() : arg;
+ // 位置参数中以第一个数值变量为复数变量
+ if(isNumber(arg)) pluraValue = parseInt(arg);
+ }catch(e){ }
+ return arg
+ });
+
+ }
+
+
+
+
+ // 3. 取得翻译文本模板字符串
+ if(activeLanguage === scope.defaultLanguage){
+ // 2.1 从默认语言中取得翻译文本模板字符串
+ // 如果当前语言就是默认语言,不需要查询加载,只需要做插值变换即可
+ // 当源文件运用了babel插件后会将原始文本内容转换为msgId
+ // 如果是msgId则从scope.default中读取,scope.default=默认语言包={:}
+ if(isMessageId(content)){
+ content = scope.default[content] || message;
+ }
+ }else {
+ // 2.2 从当前语言包中取得翻译文本模板字符串
+ // 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
+ // JSON.stringify在进行转换时会将\t\n\r转换为\\t\\n\\r,这样在进行匹配时就出错
+ let msgId = isMessageId(content) ? content : scope.idMap[escape(content)];
+ content = scope.messages[msgId] || content;
+ content = Array.isArray(content) ? content.map(v=>unescape(v)) : unescape(content);
+ }
+ // 2. 处理复数
+ // 经过上面的处理,content可能是字符串或者数组
+ // content = "原始文本内容" || 复数形式["原始文本内容","原始文本内容"....]
+ // 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式
+ if(Array.isArray(content) && content.length>0){
+ // 如果存在复数命名变量,只取第一个复数变量
+ if(pluraValue!==null){ // 启用的是位置插值,pluraIndex=第一个数字变量的位置
+ content = getPluraMessage(content,pluraValue);
+ }else if(pluralVar.length>0){
+ content = getPluraMessage(content,parseInt(vars(pluralVar[0])));
+ }else { // 如果找不到复数变量,则使用第一个内容
+ content = content[0];
+ }
+ }
+
+ // 进行插值处理
+ if(vars.length==0){
+ return content
+ }else {
+ return replaceInterpolatedVars.call(scope,content,...vars)
+ }
+ }catch(e){
+ return content // 出错则返回原始文本
+ }
+}
+
+/**
+ * 多语言管理类
+ *
+ * 当导入编译后的多语言文件时(import("./languages")),会自动生成全局实例VoerkaI18n
+ *
+ * VoerkaI18n.languages // 返回支持的语言列表
+ * VoerkaI18n.defaultLanguage // 默认语言
+ * VoerkaI18n.language // 当前语言
+ * VoerkaI18n.change(language) // 切换到新的语言
+ *
+ *
+ * VoerkaI18n.on("change",(language)=>{}) // 注册语言切换事件
+ * VoerkaI18n.off("change",(language)=>{})
+ *
+ * */
+ class I18nManager extends EventEmitter{
+ constructor(settings={}){
+ super();
+ if(I18nManager.instance!=null){
+ return I18nManager.instance;
+ }
+ I18nManager.instance = this;
+ this._settings = deepMerge(defaultLanguageSettings,settings);
+ this._scopes=[];
+ return I18nManager.instance;
+ }
+ get settings(){ return this._settings }
+ get scopes(){ return this._scopes }
+ // 当前激活语言
+ get activeLanguage(){ return this._settings.activeLanguage}
+ // 默认语言
+ get defaultLanguage(){ return this.this._settings.defaultLanguage}
+ // 支持的语言列表
+ get languages(){ return this._settings.languages}
+ // 全局格式化器
+ get formatters(){ return formatters }
+ /**
+ * 切换语言
+ */
+ async change(value){
+ value=value.trim();
+ if(this.languages.findIndex(lang=>lang.name === value)!==-1){
+ // 通知所有作用域刷新到对应的语言包
+ await this._refreshScopes(value);
+ this._settings.activeLanguage = value;
+ /// 触发语言切换事件
+ await this.emit(value);
+ }else {
+ throw new Error("Not supported language:"+value)
+ }
+ }
+ /**
+ * 当切换语言时调用此方法来加载更新语言包
+ * @param {*} newLanguage
+ */
+ async _refreshScopes(newLanguage){
+ // 并发执行所有作用域语言包的加载
+ try{
+ const scopeRefreshers = this._scopes.map(scope=>{
+ return scope.refresh(newLanguage)
+ });
+ if(Promise.allSettled){
+ await Promise.allSettled(scopeRefreshers);
+ }else {
+ await Promise.all(scopeRefreshers);
+ }
+ }catch(e){
+ console.warn("Error while refreshing i18n scopes:",e.message);
+ }
+ }
+ /**
+ *
+ * 注册一个新的作用域
+ *
+ * 每一个库均对应一个作用域,每个作用域可以有多个语言包,且对应一个翻译函数
+ * 除了默认语言外,其他语言采用动态加载的方式
+ *
+ * @param {*} scope
+ */
+ async register(scope){
+ if(!(scope instanceof i18nScope)){
+ throw new TypeError("Scope must be an instance of I18nScope")
+ }
+ this._scopes.push(scope);
+ await scope.refresh(this.activeLanguage);
+ }
+ /**
+ * 注册全局格式化器
+ * 格式化器是一个简单的同步函数value=>{...},用来对输入进行格式化后返回结果
+ *
+ * registerFormatters(name,value=>{...}) // 适用于所有语言
+ * registerFormatters(name,value=>{...},{langauge:"cn"}) // 适用于cn语言
+ * registerFormatters(name,value=>{...},{langauge:"en"}) // 适用于en语言
+
+ * @param {*} formatters
+ */
+ registerFormatter(name,formatter,{language="*"}={}){
+ if(!typeof(formatter)==="function" || typeof(name)!=="string"){
+ throw new TypeError("Formatter must be a function")
+ }
+ if(DataTypes$1.includes(name)){
+ this.formatters[language].$types[name] = formatter;
+ }else {
+ this.formatters[language][name] = formatter;
+ }
+ }
+}
+
+var runtime ={
+ getInterpolatedVars,
+ replaceInterpolatedVars,
+ I18nManager,
+ translate,
+ languages,
+ i18nScope,
+ defaultLanguageSettings,
+ getDataTypeName,
+ isNumber,
+ isPlainObject
+};
+
+module.exports = runtime;
diff --git a/packages/cli/languages/translates/default.json b/packages/cli/languages/translates/default.json
index 2347110..781d9e0 100644
--- a/packages/cli/languages/translates/default.json
+++ b/packages/cli/languages/translates/default.json
@@ -237,12 +237,6 @@
"compile.command.js"
]
},
- " - 共合成{}条语言包文本": {
- "en": " - Total {} messages",
- "$file": [
- "compile.command.js"
- ]
- },
" - 语言包文件: {}": {
"en": " - Language file: {}",
"$file": [
@@ -266,5 +260,29 @@
"$file": [
"templates\\formatters.js"
]
+ },
+ " - 共合成{}条文本": {
+ "en": " - Total{} messages",
+ "$file": [
+ "compile.command.js"
+ ]
+ },
+ " - 运行时: {}": {
+ "en": " - Runtime: {}",
+ "$file": [
+ "compile.command.js"
+ ]
+ },
+ "自动安装默认语言": {
+ "en": "Auto install default language",
+ "$file": [
+ "index.js"
+ ]
+ },
+ "不嵌入运行时源码": {
+ "en": "Not inline runtime source",
+ "$file": [
+ "index.js"
+ ]
}
}
\ No newline at end of file
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 03f84d7..4f48a64 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@voerkai18n/cli",
- "version": "1.0.7",
+ "version": "1.0.10",
"description": "VoerkaI18n command line interactive tools",
"main": "index.js",
"homepage": "https://gitee.com/zhangfisher/voerka-i18n",
@@ -18,13 +18,13 @@
"test": "echo \"Error: no test specified\" && exit 1",
"extract": "node ./index.js extract -d -e babel-plugin-voerkai18n.js,templates/**",
"compile": "node ./index.js compile -d",
- "compile:en": "cross-env LANGUAGE=en node ./index.js compile -d"
+ "compile:en": "cross-env LANGUAGE=en node ./index.js compile -d",
+ "release": "npm version patch && pnpm publish --no-git-checks --access public"
},
"author": "wxzhang",
"license": "MIT",
"bin": {
- "voerkai18n": "./index.js",
- "publish": "npm publish -access public"
+ "voerkai18n": "./index.js"
},
"dependencies": {
"@babel/cli": "^7.17.6",
diff --git a/packages/cli/readme.md b/packages/cli/readme.md
index 42c3de4..8ab058a 100644
--- a/packages/cli/readme.md
+++ b/packages/cli/readme.md
@@ -11,12 +11,11 @@
Arguments:
location 工程项目所在目录
Options:
- -d, --debug 输出调试信息
+ -D, --debug 输出调试信息
-r, --reset 重新生成当前项目的语言配置
- -m, --moduleType [type] 生成的js模块类型,取值auto,esm,cjs (default: "auto")
-lngs, --languages 支持的语言列表 (default: ["cn","en"])
- -default, --defaultLanguage 默认语言
- -active, --activeLanguage 激活语言
+ -d, --defaultLanguage 默认语言
+ -a, --activeLanguage 激活语言
-h, --help display help for command
@@ -33,10 +32,10 @@ Arguments:
location 工程项目所在目录 (default: "./")
Options:
- -d, --debug 输出调试信息
+ -D, --debug 输出调试信息
-lngs, --languages 支持的语言
- -default, --defaultLanguage 默认语言
- -active, --activeLanguage 激活语言
+ -d, --defaultLanguage 默认语言
+ -a, --activeLanguage 激活语言
-ns, --namespaces 翻译名称空间
-e, --exclude 排除要扫描的文件夹,多个用逗号分隔
-u, --updateMode 本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并
@@ -58,7 +57,7 @@ Arguments:
Options:
-d, --debug 输出调试信息
- -m, --moduleType [types] 输出模块类型,取值auto,esm,cjs (default: "auto")
+ -m, --moduleType [types] 输出模块类型,取值auto,esm,cjs (default: "esm")
-h, --help display help for command
```
diff --git a/packages/cli/templates/entry.js b/packages/cli/templates/entry.js
index 6d63eb6..34170f4 100644
--- a/packages/cli/templates/entry.js
+++ b/packages/cli/templates/entry.js
@@ -1,13 +1,16 @@
{{if moduleType === "esm"}}
import messageIds from "./idMap.js"
-import { translate,I18nManager,i18nScope } from "@voerkai18n/runtime"
+{{if inlineRuntime }}import runtime from "./runtime.js"
+const { translate,i18nScope } = runtime
+{{else}}import { translate,i18nScope } from "@voerkai18n/runtime"{{/if}}
import formatters from "./formatters.js"
import defaultMessages from "./{{defaultLanguage}}.js"
{{if defaultLanguage === activeLanguage}}const activeMessages = defaultMessages
{{else}}import activeMessages from "./{{activeLanguage}}.js"{{/if}}
{{else}}
const messageIds = require("./idMap")
-const { translate,I18nManager,i18nScope } = require("@voerkai18n/runtime")
+{{if inlineRuntime }}const { translate,i18nScope } = require("./runtime.js")
+{{else}}const { translate,i18nScope } = require("@voerkai18n/runtime"){{/if}}
const formatters = require("./formatters.js")
const defaultMessages = require("./{{defaultLanguage}}.js")
{{if defaultLanguage === activeLanguage}}const activeMessages = defaultMessages
@@ -33,11 +36,9 @@ const t = translate.bind(scope)
{{if moduleType === "esm"}}
export {
t,
- i18nScope:scope,
- i18nManager:VoerkaI18n,
+ i18nScope as scope
}
{{else}}
module.exports.t = t
-module.exports.i18nScope = scope
-module.exports.i18nManager = VoerkaI18n
+module.exports.i18nScope = scope
{{/if}}
diff --git a/packages/runtime/babel.config.js b/packages/runtime/babel.config.js
new file mode 100644
index 0000000..1c3d3ee
--- /dev/null
+++ b/packages/runtime/babel.config.js
@@ -0,0 +1,25 @@
+module.exports = {
+ "presets": [
+ [
+ "@babel/preset-env",
+ {
+ "useBuiltIns": "usage",
+ "debug": false,
+ "modules": false,
+ "corejs":{
+ "version":"3.21",
+ "proposals": true
+ }
+ }
+ ]
+ ],
+ "plugins": [
+ [
+ "@babel/plugin-transform-runtime",
+ {
+ "corejs":3,
+ "proposals": true
+ }
+ ]
+ ]
+ }
\ No newline at end of file
diff --git a/packages/runtime/dist/index.cjs b/packages/runtime/dist/index.cjs
index e4473c2..0023309 100644
--- a/packages/runtime/dist/index.cjs
+++ b/packages/runtime/dist/index.cjs
@@ -1 +1,2 @@
-"use strict";var t=function(t){return function(t){return!!t&&"object"==typeof t}(t)&&!function(t){var r=Object.prototype.toString.call(t);return"[object RegExp]"===r||"[object Date]"===r||function(t){return t.$$typeof===e}(t)}(t)};var e="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function r(t,e){return!1!==e.clone&&e.isMergeableObject(t)?l((r=t,Array.isArray(r)?[]:{}),t,e):t;var r}function a(t,e,a){return t.concat(e).map((function(t){return r(t,a)}))}function n(t){return Object.keys(t).concat(function(t){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(t).filter((function(e){return t.propertyIsEnumerable(e)})):[]}(t))}function s(t,e){try{return e in t}catch(t){return!1}}function i(t,e,a){var i={};return a.isMergeableObject(t)&&n(t).forEach((function(e){i[e]=r(t[e],a)})),n(e).forEach((function(n){(function(t,e){return s(t,e)&&!(Object.hasOwnProperty.call(t,e)&&Object.propertyIsEnumerable.call(t,e))})(t,n)||(s(t,n)&&a.isMergeableObject(e[n])?i[n]=function(t,e){if(!e.customMerge)return l;var r=e.customMerge(t);return"function"==typeof r?r:l}(n,a)(t[n],e[n],a):i[n]=r(e[n],a))})),i}function l(e,n,s){(s=s||{}).arrayMerge=s.arrayMerge||a,s.isMergeableObject=s.isMergeableObject||t,s.cloneUnlessOtherwiseSpecified=r;var l=Array.isArray(n);return l===Array.isArray(e)?l?s.arrayMerge(e,n,s):i(e,n,s):r(n,s)}l.all=function(t,e){if(!Array.isArray(t))throw new Error("first argument should be an array");return t.reduce((function(t,r){return l(t,r,e)}),{})};var o=l;var c={"*":{$types:{Date:t=>t.toLocaleString()},time:t=>t.toLocaleTimeString(),shorttime:t=>t.toLocaleTimeString(),date:t=>t.toLocaleDateString(),dict:function(t,...e){for(let r=0;r0&&e.length%2!=0?e[e.length-1]:t}},cn:{$types:{Date:t=>`${t.getFullYear()}年${t.getMonth()+1}月${t.getDate()}日 ${t.getHours()}点${t.getMinutes()}分${t.getSeconds()}秒`},shortime:t=>t.toLocaleTimeString(),time:t=>`${t.getHours()}点${t.getMinutes()}分${t.getSeconds()}秒`,date:t=>`${t.getFullYear()}年${t.getMonth()+1}月${t.getDate()}日`,shortdate:t=>`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`,currency:t=>`${t}元`},en:{currency:t=>`$${t}`}};const u=o,g=class{constructor(){this._callbacks=[]}on(t){this._callbacks.includes(t)||this._callbacks.push(t)}off(t){for(let e=0;ee(...t)))):await Promise.all(this._callbacks.map((e=>e(...t))))}},f=class{constructor(t={},e){if(this._id=t.id||(new Date).getTime().toString()+parseInt(1e3*Math.random()),this._languages=t.languages,this._defaultLanguage=t.defaultLanguage||"cn",this._activeLanguage=t.activeLanguage,this._default=t.default,this._messages=t.messages,this._idMap=t.idMap,this._formatters=t.formatters,this._loaders=t.loaders,this._global=null,this.$cache={activeLanguage:null,typedFormatters:{},formatters:{}},!globalThis.VoerkaI18n){const{I18nManager:e}=E;globalThis.VoerkaI18n=new e({defaultLanguage:this.defaultLanguage,activeLanguage:this.activeLanguage,languages:t.languages})}this.global=globalThis.VoerkaI18n,this._loading=!1,this.register(e)}get id(){return this._id}get defaultLanguage(){return this._defaultLanguage}get activeLanguage(){return this._activeLanguage}get default(){return this._default}get messages(){return this._messages}get idMap(){return this._idMap}get formatters(){return this._formatters}get loaders(){return this._loaders}get global(){return this._global}set global(t){this._global=t}register(t){this.global.register(this).then(t).catch(t)}registerFormatter(t,e,{language:r="*"}={}){if("string"!=typeof t)throw new TypeError("Formatter must be a function");DataTypes.includes(t)?this.formatters[r].$types[t]=e:this.formatters[r][t]=e}_fallback(){this._messages=this._default,this._activeLanguage=this.defaultLanguage}async refresh(t){if(this._loading=Promise.resolve(),t||(t=this.activeLanguage),t===this.defaultLanguage)return void(this._messages=this._default);const e=this.loaders[t];if("function"==typeof e)try{this._messages=(await e()).default,this._activeLanguage=t}catch(e){console.warn(`Error while loading language <${t}> on i18nScope(${this.id}): ${e.message}`),this._fallback()}else this._fallback()}get on(){return this.global.on.bind(this.global)}get off(){return this.global.off.bind(this.global)}get offAll(){return this.global.offAll.bind(this.global)}get change(){return this.global.change.bind(this.global)}};let h=c,p=/\{\s*(?\w+)?(?(\s*\|\s*\w*(\(.*\)){0,1}\s*)*)\s*\}/g;const m=["String","Number","Boolean","Object","Array","Function","Error","Symbol","RegExp","Date","Null","Undefined","Set","Map","WeakSet","WeakMap"];function b(t){return null===t?"Null":void 0===t?"Undefined":"function"==typeof t?"Function":t.constructor&&t.constructor.name}function y(t){if("object"!=typeof t||null===t)return!1;var e=Object.getPrototypeOf(t);if(null===e)return!0;for(var r=e;null!==Object.getPrototypeOf(r);)r=Object.getPrototypeOf(r);return e===r}function d(t){return!isNaN(parseInt(t))}function _(t){if(!t)return[];return t.trim().substr(1).trim().split("|").map((t=>t.trim())).map((t=>{let e=t.indexOf("("),r=t.lastIndexOf(")");if(-1!==e&&-1!==r){const a=t.substr(e+1,r-e-1).trim();let n=""==a?[]:a.split(",").map((t=>{if(t=t.trim(),!isNaN(parseInt(t)))return parseInt(t);if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.substr(1,t.length-2);if("true"===t.toLowerCase()||"false"===t.toLowerCase())return"true"===t.toLowerCase();if(!(t.startsWith("{")&&t.endsWith("}")||t.startsWith("[")&&t.endsWith("]")))return String(t);try{return JSON.parse(t)}catch(e){return String(t)}}));return[t.substr(0,e),n]}return[t,[]]}))}function v(t,e,r={}){let a,n=t,s=Object.assign({replaceAll:!0},r);for(p.lastIndex=0;null!==(a=p.exec(n));){const t=a.groups.varname||"",r=_(a.groups.formatters);if("function"==typeof e)try{n=s.replaceAll?n.replaceAll(a[0],e(t,r,a[0])):n.replace(a[0],e(t,r,a[0]))}catch{break}p.lastIndex=0}return n}function $(t,e=null){t.$cache={activeLanguage:e,typedFormatters:{},formatters:{}}}function A(t,e,r){if(t.$cache||$(t),t.$cache.activeLanguage===e){if(r in t.$cache.formatters)return t.$cache.formatters[r]}else $(t,e);const a=[t.formatters,t.global.formatters];for(const n of a){if(e in n){let a=n[e]||{};if(r in a&&"function"==typeof a[r])return t.$cache.formatters[r]=a[r]}let a=n["*"]||{};if(r in a&&"function"==typeof a[r])return t.$cache.formatters[r]=a[r]}}function L(t,e,r,a){const n=function(t,e,r){let a=[];for(let n of r)if(n[0]){const r=A(t,e,n[0]);"function"==typeof r?a.push((t=>r(t,...n[1]))):a.push((t=>"function"==typeof t[n[0]]?t[n[0]].call(t,...n[1]):t))}return a}(t,e,r),s=function(t,e,r){if(t.$cache||$(t),t.$cache.activeLanguage===e){if(r in t.$cache.typedFormatters)return t.$cache.typedFormatters[r]}else $(t,e);const a=[t.formatters,t.global.formatters];for(const n of a)if(n){if(e in n&&y(n[e].$types)){let a=n[e].$types;if(r in a&&"function"==typeof a[r])return t.$cache.typedFormatters[r]=a[r]}if("*"in n&&y(n["*"].$types)){let e=n["*"].$types;if(r in e&&"function"==typeof e[r])return t.$cache.typedFormatters[r]=e[r]}}}(t,e,b(a));return s&&n.splice(0,0,s),a=function(t,e){if(0===e.length)return t;let r=t;try{for(let t of e){if("function"!=typeof t)return r;r=t(r)}}catch(e){console.error(`Error while execute i18n formatter for ${t}: ${e.message} `)}return r}(a,n),a}function w(t,...e){const r=this,a=r.global.activeLanguage;if(0===e.length||(!(n=t).includes("{")||!n.includes("}")))return t;var n;if(1===e.length&&y(e[0])){let n=e[0];return v(t,((t,e)=>{let s=t in n?n[t]:"";return L(r,a,e,s)}))}{const n=1===e.length&&Array.isArray(e[0])?[...e[0]]:e;if(0===n.length)return t;let s=0;return v(t,((t,e)=>{if(n.length>s)return L(r,a,e,n[s++]);throw new Error}),{replaceAll:!1})}}const S={defaultLanguage:"cn",activeLanguage:"cn",languages:{cn:{name:"cn",title:"中文",default:!0},en:{name:"en",title:"英文"}},formatters:h};function k(t){return parseInt(t)>0}function O(t,e){try{return Array.isArray(t)?t.length>e?t[e]:t[t.length-1]:t}catch{return Array.isArray(t)?t[0]:t}}function M(t){return t.replaceAll(/\\(?![trnbvf'"]{1})/g,"\\\\").replaceAll("\t","\\t").replaceAll("\n","\\n").replaceAll("\b","\\b").replaceAll("\r","\\r").replaceAll("\f","\\f").replaceAll("'","\\'").replaceAll('"','\\"').replaceAll("\v","\\v")}function j(t){return t.replaceAll("\\t","\t").replaceAll("\\n","\n").replaceAll("\\b","\b").replaceAll("\\r","\r").replaceAll("\\f","\f").replaceAll("\\'","'").replaceAll('\\"','"').replaceAll("\\v","\v").replaceAll(/\\\\(?![trnbvf'"]{1})/g,"\\")}class I extends g{static instance=null;callbacks=[];constructor(t={}){return super(),null!=I.instance||(I.instance=this,this._settings=u(S,t),this._scopes=[]),I.instance}get settings(){return this._settings}get scopes(){return this._scopes}get activeLanguage(){return this._settings.activeLanguage}get defaultLanguage(){return this.this._settings.defaultLanguage}get languages(){return this._settings.languages}get formatters(){return h}async change(t){if(t=t.trim(),-1===this.languages.findIndex((e=>e.name===t)))throw new Error("Not supported language:"+t);await this._refreshScopes(t),this._settings.activeLanguage=t,await this.emit(t)}async _refreshScopes(t){try{const e=this._scopes.map((e=>e.refresh(t)));Promise.allSettled?await Promise.allSettled(e):await Promise.all(e)}catch(t){console.warn("Error while refreshing i18n scopes:",t.message)}}async register(t){if(!(t instanceof f))throw new TypeError("Scope must be an instance of I18nScope");this._scopes.push(t),await t.refresh(this.activeLanguage)}registerFormatter(t,e,{language:r="*"}={}){if("string"!=typeof t)throw new TypeError("Formatter must be a function");m.includes(t)?this.formatters[r].$types[t]=e:this.formatters[r][t]=e}}var E={getInterpolatedVars:function(t){let e=[];return v(t,((t,r,a)=>{let n={name:t,formatters:r.map((([t,e])=>({name:t,args:e}))),match:a};return-1===e.findIndex((t=>t.name===n.name&&n.formatters.toString()==t.formatters.toString()))&&e.push(n),""})),e},replaceInterpolatedVars:w,I18nManager:I,translate:function(t){const e=this,r=e.global.activeLanguage;let a=t,n=[],s=[],i=null;try{if(2===arguments.length&&y(arguments[1])?(Object.entries(arguments[1]).forEach((([t,e])=>{if("function"==typeof e)try{n[t]=e()}catch(r){n[t]=e}t.startsWith("$")&&"number"==typeof n[t]&&s.push(t)})),n=[arguments[1]]):arguments.length>=2&&(n=[...arguments].splice(1).map(((t,e)=>{try{d(t="function"==typeof t?t():t)&&(i=parseInt(t))}catch(t){}return t}))),r===e.defaultLanguage)k(a)&&(a=e.default[a]||t);else{let t=k(a)?a:e.idMap[M(a)];a=e.messages[t]||a,a=Array.isArray(a)?a.map((t=>j(t))):j(a)}return Array.isArray(a)&&a.length>0&&(a=null!==i?O(a,i):pluralVar.length>0?O(a,parseInt(n(pluralVar[0]))):a[0]),0==n.length?a:w.call(e,a,...n)}catch(t){return a}},languages:["af","am","ar-dz","ar-iq","ar-kw","ar-ly","ar-ma","ar-sa","ar-tn","ar","az","be","bg","bi","bm","bn","bo","br","bs","ca","cs","cv","cy","da","de-at","de-ch","de","dv","el","en-au","en-ca","en-gb","en-ie","en-il","en-in","en-nz","en-sg","en-tt","en","eo","es-do","es-mx","es-pr","es-us","es","et","eu","fa","fi","fo","fr-ca","fr-ch","fr","fy","ga","gd","gl","gom-latn","gu","he","hi","hr","ht","hu","hy-am","id","is","it-ch","it","ja","jv","ka","kk","km","kn","ko","ku","ky","lb","lo","lt","lv","me","mi","mk","ml","mn","mr","ms-my","ms","mt","my","nb","ne","nl-be","nl","nn","oc-lnc","pa-in","pl","pt-br","pt","ro","ru","rw","sd","se","si","sk","sl","sq","sr-cyrl","sr","ss","sv-fi","sv","sw","ta","te","tet","tg","th","tk","tl-ph","tlh","tr","tzl","tzm-latn","tzm","ug-cn","uk","ur","uz-latn","uz","vi","x-pseudo","yo","zh-cn","zh-hk","zh-tw","zh"],i18nScope:f,defaultLanguageSettings:S,getDataTypeName:b,isNumber:d,isPlainObject:y};module.exports=E;
+"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};zE({global:!0},{globalThis:n});var r=n,e=function(t){return t&&t.Math==Math&&t},n=e("object"==(void 0===r?"undefined":NO(r))&&r)||e("object"==("undefined"==typeof window?"undefined":NO(window))&&window)||e("object"==("undefined"==typeof self?"undefined":NO(self))&&self)||e("object"==NO(t)&&t)||function(){return this}()||Function("return this")(),o=function(t){return"function"==typeof t},i=n.String,a=function(t){try{return i(t)}catch(t){return"Object"}},u=o,c=a,f=n.TypeError,s=function(t){if(u(t))return t;throw f(c(t)+" is not a function")},l=o,v=function(t){return"object"==NO(t)?null!==t:l(t)},h=n,p=v,g=h.String,d=h.TypeError,y=function(t){if(p(t))return t;throw d(g(t)+" is not an object")},m=function(t){try{return!!t()}catch(t){return!0}},b=mE,w=s,x=y;zE({target:"Reflect",stat:!0,forced:!m((function(){E((function(){}))}))},{apply:function(t,r,e){return b(w(t),r,x(e))}});var S={},E=S.Reflect.apply,O=function(t){return t&&t.Math==Math&&t},A=O("object"==(void 0===r?"undefined":NO(r))&&r)||O("object"==("undefined"==typeof window?"undefined":NO(window))&&window)||O("object"==("undefined"==typeof self?"undefined":NO(self))&&self)||O("object"==NO(t)&&t)||function(){return this}()||Function("return this")(),j={exports:{}},k={exports:{}},L=!m((function(){return 7!=US({},1,{get:function(){return 7}})[1]})),I={exports:{}},T=Math.ceil,_=Math.floor,P=function(t){var r=+t;return r!=r||0===r?0:(r>0?_:T)(r)},R=P,M=Math.max,F=Math.min,N=function(t,r){var e=R(t);return e<0?M(e+r,0):F(e,r)},C=P,D=Math.min,z=function(t){return t>0?D(C(t),9007199254740991):0},$=z,G=function(t){return $(t.length)},B=mS,U=N,W=G,V=function(t){return function(r,e,n){var o,i=B(r),a=W(i),u=U(n,a);if(t&&e!=e){for(;a>u;)if((o=i[u++])!=o)return!0}else for(;a>u;u++)if((t||u in i)&&i[u]===e)return t||u||0;return!t&&-1}},Y={includes:V(!0),indexOf:V(!1)},H=m,J=function(t,r){var e=[][t];return!!e&&H((function(){e.call(null,r||function(){return 1},1)}))},q=zE,K=fO,X=ct(Y),Q=J,Z=K(ct([])),tt=!!Z&&1/Z([1],1,-0)<0,rt=Q("indexOf");q({target:"Array",proto:!0,forced:tt||!rt},{indexOf:function(t){var r=arguments.length>1?arguments[1]:void 0;return tt?Z(this,t,r)||0:X(this,t,r)}});var et=S,nt=function(t){return et[t+"Prototype"]},ot=ct(nt("Array")),it=sO,at=ot,ut=Array.prototype,ct=function(t){var r=ct(t);return t===ut||it(ut,t)&&r===ct(ut)?at:r};zE({target:"Array",stat:!0},{isArray:dt});var ft=S.Array.isArray,st=ft,lt=fO,vt=lt({}.toString),ht=lt(Bx("")),pt=function(t){return ht(vt(t),8,-1)},gt=pt,dt=st||function(t){return"Array"==gt(t)},yt={exports:{}},mt=n,bt=US,wt=function(t,r){try{bt(mt,t,{value:r,configurable:!0,writable:!0})}catch(e){mt[t]=r}return r},xt=n["__core-js_shared__"]||wt("__core-js_shared__",{}),St=xt;(yt.exports=function(t,r){return St[t]||(St[t]=void 0!==r?r:{})})("versions",[]).push({version:"3.21.1",mode:"pure",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.21.1/LICENSE",source:"https://github.com/zloirock/core-js"});var Et=function(t){try{return!!t()}catch(t){return!0}},Ot=!Et((function(){var t,r=nO(t=function(){}).call(t);return"function"!=typeof r||r.hasOwnProperty("prototype")})),At=Ot,jt=Function.prototype,kt=nO(jt),Lt=jt.call,It=At&&nO(kt).call(kt,Lt,Lt),Tt=At?function(t){return t&&It(t)}:function(t){return t&&function(){return Lt.apply(t,arguments)}},_t=A.TypeError,Pt=function(t){if(null==t)throw _t("Can't call method on "+t);return t},Rt=Pt,Mt=A.Object,Ft=function(t){return Mt(Rt(t))},Nt=Ft,Ct=Tt({}.hasOwnProperty),Dt=Object.hasOwn||function(t,r){return Ct(Nt(t),r)};WO({target:"Object",stat:!0},{hasOwn:Dt});var zt=n.TypeError,$t=function(t){if(null==t)throw zt("Can't call method on "+t);return t},Gt=$t,Bt=n.Object,Ut=function(t){return Bt(Gt(t))},Wt=Ut,Vt=fO({}.hasOwnProperty),Yt=Object.hasOwn||function(t,r){return Vt(Wt(t),r)},Ht=fO,Jt=0,qt=Math.random(),Kt=Ht(1..toString),Xt=function(t){return"Symbol("+(void 0===t?"":t)+")_"+Kt(++Jt+qt,36)},Qt=!Et((function(){return 7!=US({},1,{get:function(){return 7}})[1]})),Zt={exports:{}},tr=zE,rr=L,er=ur.f;tr({target:"Object",stat:!0,forced:ar!==er,sham:!rr},{defineProperties:er});var nr=S.Object,or=Zt.exports=function(t,r){return nr.defineProperties(t,r)};nr.defineProperties.sham&&(or.sham=!0);var ir=Zt.exports,ar=ir,ur={},cr=L&&m((function(){return 42!=US((function(){}),"prototype",{value:42,writable:!1}).prototype})),fr=function(t,r){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:r}},sr=LS,lr=fr,vr=L?function(t,r,e){return sr.f(t,r,lr(1,e))}:function(t,r,e){return t[r]=e,t},hr={},pr={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0},gr=n,dr=nx,yr=vr,mr=hr,br=Hw("toStringTag");for(var wr in pr){var xr=gr[wr],Sr=xr&&xr.prototype;Sr&&dr(Sr)!==br&&yr(Sr,br,wr),mr[wr]=mr.Array}var Er=Tr(nt("Array")),Or=nx,Ar=Yt,jr=sO,kr=Er,Lr=Array.prototype,Ir={DOMTokenList:!0,NodeList:!0},Tr=function(t){var r=Tr(t);return t===Lr||jr(Lr,t)&&r===Tr(Lr)||Ar(Ir,Or(t))?kr:r},_r=Ut,Pr=Vr;zE({target:"Object",stat:!0,forced:m((function(){Pr(1)}))},{keys:function(t){return Pr(_r(t))}});var Rr=Tr(S.Object),Mr={},Fr=fO,Nr=Yt,Cr=mS,Dr=ct(Y),zr=Mr,$r=Fr([].push),Gr=function(t,r){var e,n=Cr(t),o=0,i=[];for(e in n)!Nr(zr,e)&&Nr(n,e)&&$r(i,e);for(;r.length>o;)Nr(n,e=r[o++])&&(~Dr(i,e)||$r(i,e));return i},Br=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Ur=Gr,Wr=Br,Vr=Rr||function(t){return Ur(t,Wr)},Yr=L,Hr=cr,Jr=LS,qr=y,Kr=mS,Xr=Vr;ur.f=Yr&&!Hr?ar:function(t,r){qr(t);for(var e,n=Kr(r),o=Xr(r),i=o.length,a=0;i>a;)Jr.f(t,e=o[a++],n[e]);return t};var Qr,Zr=S,te=n,re=o,ee=function(t){return re(t)?t:void 0},ne=function(t,r){return arguments.length<2?ee(Zr[t])||ee(te[t]):Zr[t]&&Zr[t][r]||te[t]&&te[t][r]},oe=ne("document","documentElement"),ie=v,ae=n.document,ue=ie(ae)&&ie(ae.createElement),ce=function(t){return ue?ae.createElement(t):{}},fe=yt.exports,se=Xt,le=fe("keys"),ve=function(t){return le[t]||(le[t]=se(t))},he=y,pe=ur,ge=Br,de=Mr,ye=oe,me=ce,be=ve("IE_PROTO"),we=function(){},xe=function(t){return"