From 7494bad78bcdc80b8078172a871a212191f734dc Mon Sep 17 00:00:00 2001 From: wxzhang Date: Mon, 14 Mar 2022 18:16:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0tools?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/demo/apps/app/index.js | 71 ++++++++---- packages/demo/apps/app/package.json | 3 +- packages/demo/compile.demo.js | 2 +- packages/tools/.env | 2 + packages/tools/babel-plugin-voerkai18n.js | 2 +- .../tools/{compile.js => compile.command.js} | 19 ++-- packages/tools/extract.command.js | 25 +++-- packages/tools/extract.plugin.js | 49 +++++--- packages/tools/index.js | 58 ++++++++-- packages/tools/init.command.js | 20 ++-- packages/tools/languages/cn.js | 4 + packages/tools/languages/en.js | 4 + packages/tools/languages/formatters.js | 105 ++++++++++++++++++ packages/tools/languages/idMap.js | 4 + packages/tools/languages/index.js | 57 ++++++++++ packages/tools/languages/package.json | 3 + packages/tools/languages/settings.js | 15 +++ .../tools/languages/translates/default.json | 38 +++++++ packages/tools/package.json | 7 +- packages/tools/templates/entry.js | 8 +- packages/tools/utils.js | 24 +++- pnpm-lock.yaml | 15 ++- test/app.test.js | 2 +- 23 files changed, 437 insertions(+), 100 deletions(-) create mode 100644 packages/tools/.env rename packages/tools/{compile.js => compile.command.js} (91%) create mode 100644 packages/tools/languages/cn.js create mode 100644 packages/tools/languages/en.js create mode 100644 packages/tools/languages/formatters.js create mode 100644 packages/tools/languages/idMap.js create mode 100644 packages/tools/languages/index.js create mode 100644 packages/tools/languages/package.json create mode 100644 packages/tools/languages/settings.js create mode 100644 packages/tools/languages/translates/default.json diff --git a/packages/demo/apps/app/index.js b/packages/demo/apps/app/index.js index e3ab573..62b1770 100644 --- a/packages/demo/apps/app/index.js +++ b/packages/demo/apps/app/index.js @@ -1,26 +1,51 @@ -import { t,languages,scope } from "./languages/index.js" - -VoerkaI18n.on((language)=>{ - console.log("切换到语言:",language) -}) -async function output(){ - console.log(t("用户名或密码错误")) - console.log(t('请输入用户名:')) - console.log(t("请输入密码:")) - console.log(t("欢迎您: {}","张三丰")) - console.log("----------------") - await VoerkaI18n.change("en") - console.log(t("用户名或密码错误")) - console.log(t('请输入用户名:')) - console.log(t("请输入密码:")) - console.log(t("欢迎您: {}","tom")) - console.log("----------------") - await VoerkaI18n.change("cn") - console.log(t("用户名或密码错误")) - console.log(t('请输入用户名:')) - console.log(t("请输入密码:")) - console.log(t("欢迎您: {}","tom")) +import messageIds from "./idMap.js" +import { translate,I18nManager } from "@voerkai18n/runtime" +import defaultMessages from "./cn.js" +import scopeSettings from "./settings.js" + + +// 自动创建全局VoerkaI18n实例 +if(!globalThis.VoerkaI18n){ + globalThis.VoerkaI18n = new I18nManager(scopeSettings) } -output().then(()=>{}) \ No newline at end of file +let scope = { + defaultLanguage: "cn", // 默认语言名称 + default: defaultMessages, // 默认语言包 + messages : defaultMessages, // 当前语言包 + idMap:messageIds, // 消息id映射列表 + formatters:{}, // 当前作用域的格式化函数列表 + loaders:{}, // 异步加载语言文件的函数列表 + global:{}, // 引用全局VoerkaI18n配置,注册后自动引用 + // 主要用来缓存格式化器的引用,当使用格式化器时可以直接引用,避免检索 + $cache:{ + activeLanguage:null, + typedFormatters:{}, + formatters:{}, + } +} + +let supportedlanguages = {} + + +scope.loaders["en"] = ()=>import("./en.js") + + +const t = translate.bind(scope) +const languages = [ + { + "name": "cn", + "title": "cn" + }, + { + "name": "en", + "title": "en" + } +] +// 注册当前作用域到全局VoerkaI18n实例 +VoerkaI18n.register(scope) + + +export { t, languages,scope } + diff --git a/packages/demo/apps/app/package.json b/packages/demo/apps/app/package.json index 96ae6e5..9e84023 100644 --- a/packages/demo/apps/app/package.json +++ b/packages/demo/apps/app/package.json @@ -1,3 +1,4 @@ { - "type": "module" + "type": "commonjs", + "license": "MIT" } \ No newline at end of file diff --git a/packages/demo/compile.demo.js b/packages/demo/compile.demo.js index 72ce71a..f5d615d 100644 --- a/packages/demo/compile.demo.js +++ b/packages/demo/compile.demo.js @@ -1,6 +1,6 @@ -const compile = require('@voerkai18n/tools/compile'); +const compile = require('@voerkai18n/tools/compile.command'); const path = require("path") diff --git a/packages/tools/.env b/packages/tools/.env new file mode 100644 index 0000000..6ba1528 --- /dev/null +++ b/packages/tools/.env @@ -0,0 +1,2 @@ +# 命令行工具使用的语言 +language=en \ No newline at end of file diff --git a/packages/tools/babel-plugin-voerkai18n.js b/packages/tools/babel-plugin-voerkai18n.js index bf4ac5d..b739244 100644 --- a/packages/tools/babel-plugin-voerkai18n.js +++ b/packages/tools/babel-plugin-voerkai18n.js @@ -1,7 +1,7 @@ /** * 转译源码中的t翻译函数的翻译内容转换为唯一的id值 * - * - 将源文件中的t("xxxxx")转码成t(hash(xxxxx)) + * - 将源文件中的t("xxxxx")转码成t("id") * - 自动导入languages/index.js中的翻译函数t * * 使用方法: diff --git a/packages/tools/compile.js b/packages/tools/compile.command.js similarity index 91% rename from packages/tools/compile.js rename to packages/tools/compile.command.js index 7ebdc60..caebaa5 100644 --- a/packages/tools/compile.js +++ b/packages/tools/compile.command.js @@ -31,25 +31,20 @@ const { importModule,findModuleType } = require("./utils") const fs = require("fs") const logger = createLogger() const artTemplate = require("art-template") - +const { t } = require("./languages") function normalizeCompileOptions(opts={}) { let options = Object.assign({ - input:null, // 指定要编译的文件夹,即extract输出的语言文件夹 - output:null, // 指定编译后的语言文件夹,如果没有指定,则使用input目录 moduleType:"auto" // 指定编译后的语言文件的模块类型,取值common,cjs,esm,es }, 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" + if(!["auto","commonjs","cjs","esm","es"].includes(options.moduleType)) options.moduleType = "esm" return options; } - - - module.exports =async function compile(langFolder,opts={}){ const options = normalizeCompileOptions(opts); - const { output,moduleType } = options; + let { moduleType } = options; if(moduleType==="auto"){ moduleType = findModuleType(langFolder) @@ -63,7 +58,7 @@ module.exports =async function compile(langFolder,opts={}){ const langSettings = module.default; let { languages,defaultLanguage,activeLanguage,namespaces } = langSettings - logger.log("支持的语言\t: {}",languages.map(item=>`${item.title}(${item.name})`)) + logger.log(t("支持的语言\t: {}",languages.map(item=>`${item.title}(${item.name})`).join(","))) logger.log("默认语言\t: {}",defaultLanguage) logger.log("激活语言\t: {}",activeLanguage) logger.log("名称空间\t: {}",Object.keys(namespaces).join(",")) @@ -87,7 +82,7 @@ module.exports =async function compile(langFolder,opts={}){ logger.log("读取语言文件{}失败:{}",file,e.message) } }) - logger.log(" - 合成语言包文本,共{}条",Object.keys(messages).length) + logger.log(" - 共合成{}条语言包文本",Object.keys(messages).length) // 2. 为每一个文本内容生成一个唯一的id let messageIds = {} @@ -158,12 +153,12 @@ module.exports =async function compile(langFolder,opts={}){ let packageJson = {} if(moduleType==="esm"){ packageJson = { - version:"1.0.0", + license:"MIT", type:"module", } }else{ packageJson = { - version:"1.0.0", + license:"MIT", } } fs.writeFileSync(packageJsonFile,JSON.stringify(packageJson,null,4)) diff --git a/packages/tools/extract.command.js b/packages/tools/extract.command.js index a54aaac..c8e636c 100644 --- a/packages/tools/extract.command.js +++ b/packages/tools/extract.command.js @@ -2,6 +2,7 @@ const { findModuleType } = require("./utils") const path = require("path") const fs = require("fs") const gulp = require("gulp") +const extractor = require("./extract.plugin") const createLogger = require("logsets") const logger = createLogger() @@ -13,13 +14,11 @@ module.exports = function(targetPath,options={}){ const folders = filetypes.map(ftype=>{ if(ftype.startsWith(".")) ftype = "*"+ftype if(!ftype.startsWith("*.")) ftype = "*."+ftype - return path.join(targetPath,ftype) + return path.join(targetPath,"**",ftype) }) - folders.push(`!${path.join(targetPath,"languages")}`) - folders.push(`!${path.join(targetPath,"node_modules")}`) + folders.push(`!${path.join(targetPath,"languages","**")}`) + folders.push(`!${path.join(targetPath,"node_modules","**")}`) folders.push(`!${path.join(targetPath,"**","node_modules","**")}`) - // 排除文件夹 - console.log("exclude",exclude) if(!Array.isArray(exclude) && exclude){ exclude = exclude.split(",") } @@ -27,10 +26,18 @@ module.exports = function(targetPath,options={}){ exclude.forEach(folder=>{ folders.push(`!${path.join(targetPath,folder)}`) }) + } + if(!fs.existsSync(targetPath)){ + logger.log("目标文件夹<{}>不存在",targetPath) + return + } + if(options.debug){ + logger.log("扫描提取范围:") + logger.format(folders) } - console.log(folders) - - - //gulp.src(path.join(targetPath,"**/*.json")) + options.outputPath = path.join(targetPath,"languages") + gulp.src(folders) + .pipe(extractor(options)) + .pipe(gulp.dest(options.outputPath)) } \ No newline at end of file diff --git a/packages/tools/extract.plugin.js b/packages/tools/extract.plugin.js index 6521188..4286caf 100644 --- a/packages/tools/extract.plugin.js +++ b/packages/tools/extract.plugin.js @@ -12,8 +12,10 @@ const fs = require('fs') const readJson = require("readjson") const createLogger = require("logsets") const { replaceInterpolateVars,getDataTypeName } = require("@voerkai18n/runtime") -const { findModuleType } = require("./utils") +const { findModuleType,createPackageJsonFile } = require("./utils") const logger = createLogger() +const { t } = require("./languages") + // 捕获翻译文本的默认正则表达式 const DefaultTranslateExtractor = String.raw`\b{funcName}\(\s*("|'){1}(?:((?\w+)::))?(?.*?)(((\1\s*\)){1})|((\1){1}\s*(,(\w|\d|(?:\{.*\})|(?:\[.*\])|([\"\'\(].*[\"\'\)]))*)*\s*\)))` @@ -349,13 +351,14 @@ function updateLanguageFile(fromTexts,toLangFile,options){ module.exports = function(options={}){ options = normalizeLanguageOptions(options) - let {debug,output:{ path:outputPath, updateMode },languages} = options + let {debug,outputPath, updateMode,languages} = options - logger.log("Supported languages\t: {}",options.languages.map(item=>`${item.title}(${item.name})`)) - logger.log("Default language\t: {}",options.defaultLanguage) - logger.log("Active language\t\t: {}",options.activeLanguage) - logger.log("Language namespaces\t: {}",Object.keys(options.namespaces).join(",")) - + logger.log(t("支持的语言\t: {}"),options.languages.map(item=>`${item.title}(${item.name})`).join(",")) + logger.log("默认语言\t: {}",options.defaultLanguage) + logger.log("激活语言\t: {}",options.activeLanguage) + logger.log("名称空间\t: {}",Object.keys(options.namespaces).join(",")) + logger.log("") + logger // 保存提交提取的文本 = {} let results = {} let fileCount=0 // 文件总数 @@ -375,21 +378,24 @@ 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 {} messages.",file.relative,Object.keys(texts).join(),textCount) + logger.log("提取<{}>, 发现 [{}] 名称空间,{} 条信息。",file.relative,Object.keys(texts).join(),textCount) } }catch(err){ - logger.log("Error while extract messages from <{}> : {}",file.relative,err.message) + logger.log("从<{}>提取信息时出错 : {}",file.relative,err.message) } callback() },function(callback){ logger.log("") - logger.log("Extracting finished.") - logger.log(" - Total of files\t: {}",fileCount) - logger.log(" - Output location\t: {}",outputPath) + logger.log("翻译信息提取完成。") + logger.log(" - 文件总数\t: {}",fileCount) + logger.log(" - 输出路径\t: {}",outputPath) const translatesPath = path.join(outputPath,"translates") if(!fs.existsSync(outputPath)) fs.mkdirSync(outputPath) if(!fs.existsSync(translatesPath)) fs.mkdirSync(translatesPath) + if(!("default" in results)){ + results["default"] = {} + } // 每个名称空间对应一个文件 for(let [namespace,texts] of Object.entries(results)){ const langFile = path.join(outputPath,"translates",`${namespace}.json`) @@ -397,10 +403,10 @@ module.exports = function(options={}){ const langTexts = {} if(isExists){ updateLanguageFile(texts,langFile,options) - logger.log(" Update language file : {}",path.relative(outputPath,langFile)) + logger.log(" √ 更新语言文件 : {}",path.relative(outputPath,langFile)) }else{ fs.writeFileSync(langFile,JSON.stringify(texts,null,4)) - logger.log(" Save language file : {}",path.relative(outputPath,langFile)) + logger.log(" √ 保存语言文件 : {}",path.relative(outputPath,langFile)) } } // 生成语言配置文件 settings.js , 仅当不存在时才生成 @@ -413,13 +419,20 @@ module.exports = function(options={}){ namespaces : options.namespaces } fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`) - logger.log(" - Generate settings of language : {}",settingsFile) + logger.log(" - 生成语言配置文件: {}",settingsFile) }else{ - logger.log(" - Settings of language already exists : {}",settingsFile) + logger.log(" - 已更新语言配置文件: {}",settingsFile) } + + // 生成package.json - const packageJsonFile = path.join(outputPath,"package.json") - fs.writeFileSync(packageJsonFile,`${JSON.stringify({type:"commonjs"},null,4)}`) + createPackageJsonFile(outputPath) + + logger.log("下一步:") + logger.log(" - 运行<{}>编译语言包","voerkai18n compile") + logger.log(" - 在源码中导入编译后的语言包[{}]","import './languages'") + + callback() }); } diff --git a/packages/tools/index.js b/packages/tools/index.js index 7db69ae..2cb5c9c 100644 --- a/packages/tools/index.js +++ b/packages/tools/index.js @@ -5,18 +5,18 @@ const fs = require("fs"); const { importModule } = require('./utils'); const deepmerge = require('deepmerge'); const logger = createLogger() +require('dotenv').config() + +const { t, changeLanguage } = require("./languages") const program = new Command(); - - -program - .option('-d, --debug', '输出调试信息') - + program .command('init') - .argument('[location]', '工程项目所在目录') + .argument('[location]', t('工程项目所在目录')) .description('初始化项目国际化配置') + .option('-d, --debug', '输出调试信息') .option('-r, --reset', '重新生成当前项目的语言配置') - .option('-m, --moduleType [type]', '生成的js模块类型,默认esm',"esm") + .option('-m, --moduleType [type]', '生成的js模块类型,取值auto,esm,cjs',"auto") .option('-lngs, --languages ', '支持的语言列表', ['cn','en']) .action((location,options) => { if(!location) { @@ -25,8 +25,11 @@ program location = path.join(process.cwd(),location) } logger.log("工程目录:{}",location) + // + if(options.debug){ + logger.format(options,{compact:true}) + } const initializer = require("./init.command") - options.debug=true initializer(location,options) }); @@ -35,7 +38,7 @@ program .command('extract') .description('扫描并提取所有待翻译的字符串到文件夹中') .option('-d, --debug', '输出调试信息') - .option('-lngs, --languages', '支持的语言', 'cn,en,de,fr,es,it,jp') + .option('-lngs, --languages', '支持的语言', 'cn,en') .option('-d, --defaultLanguage', '默认语言', 'cn') .option('-a, --activeLanguage', '激活语言', 'cn') .option('-ns, --namespaces', '翻译名称空间') @@ -56,20 +59,51 @@ program logger.log("工程目录:{}",location) const langSettingsFile = path.join(location,"languages","settings.js") if(fs.existsSync(langSettingsFile)){ - logger.log("语言配置文件<{}>已存在.将优先使用此配置文件中参数来提取文本",langSettingsFile) + logger.log("语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本","./languages/settings.js") let lngOptions = (await importModule("file:///"+langSettingsFile)).default options.languages = lngOptions.languages options.defaultLanguage = lngOptions.defaultLanguage options.activeLanguage = lngOptions.activeLanguage options.namespaces = lngOptions.namespaces } - + // + if(options.debug){ + logger.format(options,{compact:true}) + } const extractor = require('./extract.command'); extractor(location,options) }); +program + .command('compile') + .description('编译语言包文件文件夹中') + .option('-d, --debug', '输出调试信息') + .option('-m, --moduleType [types]', '输出模块类型,取值auto,esm,cjs', 'auto') + .argument('[location]', t('工程项目所在目录'),"./") + .hook("preAction",async (location,options) => { + console.log("process.env.language",process.env.language) + await changeLanguage("en") + }) + .action(async (location,options) => { + if(!location) { + location = process.cwd() + }else{ + location = path.join(process.cwd(),location) + } + const langFolder = path.join(location,"languages") + if(!fs.existsSync(langFolder)){ + logger.error("语言包文件夹<{}>不存在",langFolder) + return + } + if(options.debug){ + logger.format(options,{compact:true}) + } + compile = require("./compile.command") + compile(langFolder,options) + }); + -program.parse(process.argv); +program.parseAsync(process.argv); const options = program.opts(); \ No newline at end of file diff --git a/packages/tools/init.command.js b/packages/tools/init.command.js index 8432e51..804a8ca 100644 --- a/packages/tools/init.command.js +++ b/packages/tools/init.command.js @@ -4,20 +4,20 @@ */ -const { findModuleType } = require("./utils") +const { findModuleType,createPackageJsonFile } = require("./utils") const path = require("path") const fs = require("fs") const createLogger = require("logsets") const logger = createLogger() + module.exports = function(targetPath,{debug = true,languages=["cn","en"],defaultLanguage="cn",activeLanguage="cn",moduleType = "auto",reset=false}={}){ // 语言文件夹名称 const langPath = "languages" // 查找当前项目的语言包类型路径 + const lngPath = path.join(targetPath,langPath) + moduleType = createPackageJsonFile(lngPath,moduleType) - if(moduleType==="auto"){ - moduleType = findModuleType(targetPath) - } if(moduleType==null) { if(debug){ logger.log("找不到{}文件,{}只能在js项目工程中使用","package.json","voerkai18n") @@ -26,7 +26,7 @@ module.exports = function(targetPath,{debug = true,languages=["cn","en"],default } } - const lngPath = path.join(targetPath,langPath) + if(!fs.existsSync(lngPath)){ fs.mkdirSync(lngPath) if(debug) logger.log("创建语言包文件夹: {}",lngPath) @@ -44,21 +44,19 @@ module.exports = function(targetPath,{debug = true,languages=["cn","en"],default activeLanguage, namespaces:{} } - const packageJsonFile = path.join(targetPath,"languages","package.json") + // 写入配置文件 if(["esm","es"].includes(moduleType)){ fs.writeFileSync(settingsFile,`export default ${JSON.stringify(settings,null,4)}`) - fs.writeFileSync(packageJsonFile,JSON.stringify({type:"module"},null,4)) }else{ fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`) - fs.writeFileSync(packageJsonFile,JSON.stringify({},null,4)) } if(debug) { logger.log("生成语言配置文件:{}","./languages/settings.js") logger.log("拟支持的语言:{}",settings.languages.map(l=>l.name).join(",")) - logger.log("下一步:") + logger.log("初始化成功,下一步:") logger.log(" - 编辑{}确定拟支持的语言种类等参数","languages/settings.js") logger.log(" - 运行<{}>扫描提取要翻译的文本","voerkai18n extract") logger.log(" - 运行<{}>编译语言包","voerkai18n compile") - } -} \ No newline at end of file + } +} \ No newline at end of file diff --git a/packages/tools/languages/cn.js b/packages/tools/languages/cn.js new file mode 100644 index 0000000..3fb3b4f --- /dev/null +++ b/packages/tools/languages/cn.js @@ -0,0 +1,4 @@ +module.exports = { + "1": "工程项目所在目录", + "2": "支持的语言\\t: {}" +} \ No newline at end of file diff --git a/packages/tools/languages/en.js b/packages/tools/languages/en.js new file mode 100644 index 0000000..5e5d2fa --- /dev/null +++ b/packages/tools/languages/en.js @@ -0,0 +1,4 @@ +module.exports = { + "1": "Project directory", + "2": "Supported Languages\\t: {}" +} \ No newline at end of file diff --git a/packages/tools/languages/formatters.js b/packages/tools/languages/formatters.js new file mode 100644 index 0000000..70631cb --- /dev/null +++ b/packages/tools/languages/formatters.js @@ -0,0 +1,105 @@ +/** + + 格式化器用来对翻译文本内容中的插值变量进行格式化, + 比如将一个数字格式化为货币格式,或者将一个日期格式化为友好的日期格式。 + + - 以下定义了一些格式化器,在中文场景下,会启用这些格式化器。 + import dayjs from "dayjs"; + const formatters = { + "*":{ // 在所有语言下生效的格式化器 + $types:{...}, // 只作用于特定数据类型的默认格式化器 + .... // 全局格式化器 + }, + cn:{ + // 只作用于特定数据类型的格式化器 + $types:{ + Date:(value)=>dayjs(value).format("YYYY年MM月DD日 HH:mm:ss"), + }, + date:(value)=>dayjs(value).format("YYYY年MM月DD日") + bjTime:(value)=>"北京时间"+ value, + [格式化器名称]:(value)=>{...}, + [格式化器名称]:(value)=>{...}, + [格式化器名称]:(value)=>{...}, + }, + en:{ + $types:{ + Date:(value)=>dayjs(value).format("YYYY/MM/DD HH:mm:ss"), // 默认的格式化器 + }, + date:(value)=>dayjs(value).format("YYYY/MM/DD") + bjTime:(value)=>"BeiJing "+ value, + [格式化器名称]:(value)=>{...}, + [格式化器名称]:(value)=>{...}, + [格式化器名称]:(value)=>{...}, + } + } + - 在翻译函数中使用格式化器的方法,示例如下: + + t("Now is { value | date | bjTime }",{value: new Date()}) + 其等效于: + t(`Now is ${bjTime(date(value))",{value: new Date()}) + 由于value分别经过两个管道符转换,上一个管道符的输出作为下一个管道符的输入,可以多次使用管道符。 + + 最终的输出结果: + 中文: "现在是北京时间2022年3月1日" + 英文: "Now is BeiJing 2022/03/01" + + + * + */ + +const formatters = { + "*":{ }, // 在所有语言下生效的格式化器 + $types:{ } // 在所有语言下只作用于特定数据类型的格式化器 + + cn:{ + $types:{ + "*":{ + + }, + Date:{ + + }, + Number:{ + + }, + String:{ + + }, + Array:{ + + }, + Object:{ + + } + } + } + + en:{ + $types:{ + "*":{ + + }, + Date:{ + + }, + Number:{ + + }, + String:{ + + }, + Array:{ + + }, + Object:{ + + } + } + } + +} + + +module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.module.exports.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s = formatters + + diff --git a/packages/tools/languages/idMap.js b/packages/tools/languages/idMap.js new file mode 100644 index 0000000..a665e68 --- /dev/null +++ b/packages/tools/languages/idMap.js @@ -0,0 +1,4 @@ +module.exports = { + "工程项目所在目录": 1, + "支持的语言\\t: {}": 2 +} \ No newline at end of file diff --git a/packages/tools/languages/index.js b/packages/tools/languages/index.js new file mode 100644 index 0000000..e877f34 --- /dev/null +++ b/packages/tools/languages/index.js @@ -0,0 +1,57 @@ + +const messageIds = require("./idMap") +const { translate,I18nManager } = require("@voerkai18n/runtime") +const defaultMessages = require("./cn.js") +const scopeSettings = require("./settings.js") + + +// 自动创建全局VoerkaI18n实例 +if(!globalThis.VoerkaI18n){ + globalThis.VoerkaI18n = new I18nManager(scopeSettings) +} + +let scope = { + defaultLanguage: "cn", // 默认语言名称 + default: defaultMessages, // 默认语言包 + messages : defaultMessages, // 当前语言包 + idMap:messageIds, // 消息id映射列表 + formatters:{}, // 当前作用域的格式化函数列表 + loaders:{}, // 异步加载语言文件的函数列表 + global:{}, // 引用全局VoerkaI18n配置,注册后自动引用 + // 主要用来缓存格式化器的引用,当使用格式化器时可以直接引用,避免检索 + $cache:{ + activeLanguage:null, + typedFormatters:{}, + formatters:{}, + } +} + +let supportedlanguages = {} + + +scope.loaders["en"] = ()=>import("./en.js") + + +const t = translate.bind(scope) +const languages = [ + { + "name": "cn", + "title": "中文" + }, + { + "name": "en", + "title": "英文" + } +] +// 注册当前作用域到全局VoerkaI18n实例 +VoerkaI18n.register(scope) + + +module.exports.languages = languages +module.exports.scope = scope +module.exports.t = t +module.exports.changeLanguage = VoerkaI18n.change.bind(VoerkaI18n) +module.exports.addLanguageListener = VoerkaI18n.on.bind(VoerkaI18n) +module.exports.removeLanguageListener = VoerkaI18n.off.bind(VoerkaI18n) +module.exports.i18nManager = VoerkaI18n + diff --git a/packages/tools/languages/package.json b/packages/tools/languages/package.json new file mode 100644 index 0000000..549951b --- /dev/null +++ b/packages/tools/languages/package.json @@ -0,0 +1,3 @@ +{ + "license": "MIT" +} \ No newline at end of file diff --git a/packages/tools/languages/settings.js b/packages/tools/languages/settings.js new file mode 100644 index 0000000..21f5da5 --- /dev/null +++ b/packages/tools/languages/settings.js @@ -0,0 +1,15 @@ +module.exports = { + "languages": [ + { + "name": "cn", + "title": "中文" + }, + { + "name": "en", + "title": "英文" + } + ], + "defaultLanguage": "cn", + "activeLanguage": "cn", + "namespaces": {} +} \ No newline at end of file diff --git a/packages/tools/languages/translates/default.json b/packages/tools/languages/translates/default.json new file mode 100644 index 0000000..67ec3a9 --- /dev/null +++ b/packages/tools/languages/translates/default.json @@ -0,0 +1,38 @@ +{ + "工程项目所在目录": { + "en": "Project directory", + "$file": [ + "index.js" + ] + }, + "支持的语言\\t: {}": { + "en": "Supported Languages\\t: {}", + "$file": [ + "extract.plugin.js" + ] + }, + "xxxxx": { + "en": "xxxxx", + "$file": [ + "babel-plugin-voerkai18n.js" + ] + }, + "id": { + "en": "id", + "$file": [ + "babel-plugin-voerkai18n.js" + ] + }, + "支持的语言\\t: {}\",languages.map(item=>`${item.title}(${item.name})`).join(": { + "en": "支持的语言\\t: {}\",languages.map(item=>`${item.title}(${item.name})`).join(", + "$file": [ + "compile.command.js" + ] + }, + "Now is { value | date | bjTime }": { + "en": "Now is { value | date | bjTime }", + "$file": [ + "templates\\formatters.js" + ] + } +} \ No newline at end of file diff --git a/packages/tools/package.json b/packages/tools/package.json index c0c34f4..9b9fe62 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -4,7 +4,9 @@ "description": "VoerkaI18n Tools", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "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" }, "author": "", "bin": { @@ -18,9 +20,10 @@ "art-template": "^4.13.2", "commander": "^9.0.0", "deepmerge": "^4.2.2", + "dotenv": "^16.0.0", "glob": "^7.2.0", "gulp": "^4.0.2", - "logsets": "^1.0.7", + "logsets": "^1.0.8", "readjson": "^2.2.2", "through2": "^4.0.2", "vinyl": "^2.2.1" diff --git a/packages/tools/templates/entry.js b/packages/tools/templates/entry.js index 59b952c..b2abba9 100644 --- a/packages/tools/templates/entry.js +++ b/packages/tools/templates/entry.js @@ -5,7 +5,7 @@ import defaultMessages from "./{{defaultLanguage}}.js" import scopeSettings from "./settings.js" {{else}} const messageIds = require("./idMap") -const { translate,i18n } = require("@voerkai18n/runtime") +const { translate,I18nManager } = require("@voerkai18n/runtime") const defaultMessages = require("./{{defaultLanguage}}.js") const scopeSettings = require("./settings.js") {{/if}} @@ -43,9 +43,13 @@ const languages = {{@ JSON.stringify(languages,null,4) }} VoerkaI18n.register(scope) {{if moduleType === "esm"}} -export { t, languages,scope } +export { t, languages,scope,i18nManager:VoerkaI18n, changeLanguage:VoerkaI18n.change.bind(VoerkaI18n),addLanguageListener:VoerkaI18n.on.bind(VoerkaI18n),removeLanguageListener:VoerkaI18n.off.bind(VoerkaI18n) } {{else}} module.exports.languages = languages module.exports.scope = scope module.exports.t = t +module.exports.changeLanguage = VoerkaI18n.change.bind(VoerkaI18n) +module.exports.addLanguageListener = VoerkaI18n.on.bind(VoerkaI18n) +module.exports.removeLanguageListener = VoerkaI18n.off.bind(VoerkaI18n) +module.exports.i18nManager = VoerkaI18n {{/if}} diff --git a/packages/tools/utils.js b/packages/tools/utils.js index 3a792f3..520e33f 100644 --- a/packages/tools/utils.js +++ b/packages/tools/utils.js @@ -1,4 +1,6 @@ - +const path = require("path") +const fs = require("fs") +const readJson = require("readjson") async function importModule(url){ try{ @@ -21,10 +23,25 @@ async function importModule(url){ let parent = path.dirname(folder) if(parent===folder) return null return findModuleType(parent) - }catch{ + }catch(e){ return "esm" } } + +function createPackageJsonFile(targetPath,moduleType="auto"){ + if(moduleType==="auto"){ + moduleType = findModuleType(targetPath) + } + const packageJsonFile = path.join(targetPath, "package.json") + if(["esm","es"].includes(moduleType)){ + fs.writeFileSync(packageJsonFile,JSON.stringify({type:"module",license:"MIT"},null,4)) + }else{ + fs.writeFileSync(packageJsonFile,JSON.stringify({license:"MIT"},null,4)) + } + return moduleType +} + + function isPlainObject(obj){ if (typeof obj !== 'object' || obj === null) return false; var proto = Object.getPrototypeOf(obj); @@ -95,7 +112,8 @@ function createJsModuleFile(filename,defaultExports={},namedExports={},moduleTyp } module.exports = { importModule, - findModuleType + findModuleType, + createPackageJsonFile } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 699b7e3..5400c22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,9 +92,10 @@ importers: art-template: ^4.13.2 commander: ^9.0.0 deepmerge: ^4.2.2 + dotenv: ^16.0.0 glob: ^7.2.0 gulp: ^4.0.2 - logsets: ^1.0.7 + logsets: ^1.0.8 readjson: ^2.2.2 through2: ^4.0.2 vinyl: ^2.2.1 @@ -105,9 +106,10 @@ importers: art-template: 4.13.2 commander: 9.0.0 deepmerge: 4.2.2 + dotenv: 16.0.0 glob: 7.2.0 gulp: 4.0.2 - logsets: 1.0.7 + logsets: 1.0.8 readjson: 2.2.2 through2: 4.0.2 vinyl: 2.2.1 @@ -2567,6 +2569,11 @@ packages: webidl-conversions: 5.0.0 dev: true + /dotenv/16.0.0: + resolution: {integrity: sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==} + engines: {node: '>=12'} + dev: false + /duplexify/3.7.1: resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} dependencies: @@ -4166,8 +4173,8 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /logsets/1.0.7: - resolution: {integrity: sha512-O9VFAcMEuUY1tyq81hdZada+77fLl2Ff602AhlKpEpVr3gMyPFM67tILlBZZgnCfcmoRUo6nwhl26xXURHPH0A==} + /logsets/1.0.8: + resolution: {integrity: sha512-9XuCtIjGvAWbi+JgF2+NI5Bb55uvzwgCFvlo/pafXdZjVC0DDri2k+Jzv7hWg0audTrw4FTnIBiSo3yOlEpmHQ==} dependencies: '@babel/runtime-corejs3': registry.npmmirror.com/@babel/runtime-corejs3/7.17.2 ansicolor: registry.npmmirror.com/ansicolor/1.1.100 diff --git a/test/app.test.js b/test/app.test.js index cccfece..e63dd06 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,7 +1,7 @@ /** * 测试demp app的语言运行环境 */ - const compile = require('../packages/tools/compile'); + const compile = require('../packages/tools/compile.command'); const path = require("path")