更新tools

This commit is contained in:
wxzhang 2022-03-14 18:16:09 +08:00
parent 54546276ab
commit 7494bad78b
23 changed files with 437 additions and 100 deletions

View File

@ -1,26 +1,51 @@
import { t,languages,scope } from "./languages/index.js"
VoerkaI18n.on((language)=>{
console.log("切换到语言:",language)
})
async function output(){ import messageIds from "./idMap.js"
console.log(t("用户名或密码错误")) import { translate,I18nManager } from "@voerkai18n/runtime"
console.log(t('请输入用户名:')) import defaultMessages from "./cn.js"
console.log(t("请输入密码:")) import scopeSettings from "./settings.js"
console.log(t("欢迎您: {}","张三丰"))
console.log("----------------")
await VoerkaI18n.change("en") // 自动创建全局VoerkaI18n实例
console.log(t("用户名或密码错误")) if(!globalThis.VoerkaI18n){
console.log(t('请输入用户名:')) globalThis.VoerkaI18n = new I18nManager(scopeSettings)
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"))
} }
output().then(()=>{}) 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 }

View File

@ -1,3 +1,4 @@
{ {
"type": "module" "type": "commonjs",
"license": "MIT"
} }

View File

@ -1,6 +1,6 @@
const compile = require('@voerkai18n/tools/compile'); const compile = require('@voerkai18n/tools/compile.command');
const path = require("path") const path = require("path")

2
packages/tools/.env Normal file
View File

@ -0,0 +1,2 @@
# 命令行工具使用的语言
language=en

View File

@ -1,7 +1,7 @@
/** /**
* 转译源码中的t翻译函数的翻译内容转换为唯一的id值 * 转译源码中的t翻译函数的翻译内容转换为唯一的id值
* *
* - 将源文件中的t("xxxxx")转码成t(hash(xxxxx)) * - 将源文件中的t("xxxxx")转码成t("id")
* - 自动导入languages/index.js中的翻译函数t * - 自动导入languages/index.js中的翻译函数t
* *
* 使用方法: * 使用方法:

View File

@ -31,25 +31,20 @@ const { importModule,findModuleType } = require("./utils")
const fs = require("fs") const fs = require("fs")
const logger = createLogger() const logger = createLogger()
const artTemplate = require("art-template") const artTemplate = require("art-template")
const { t } = require("./languages")
function normalizeCompileOptions(opts={}) { function normalizeCompileOptions(opts={}) {
let options = Object.assign({ let options = Object.assign({
input:null, // 指定要编译的文件夹即extract输出的语言文件夹
output:null, // 指定编译后的语言文件夹,如果没有指定则使用input目录
moduleType:"auto" // 指定编译后的语言文件的模块类型取值common,cjs,esm,es moduleType:"auto" // 指定编译后的语言文件的模块类型取值common,cjs,esm,es
}, opts) }, opts)
if(options.moduleType==="es") options.moduleType = "esm" if(options.moduleType==="es") options.moduleType = "esm"
if(options.moduleType==="cjs") options.moduleType = "commonjs" 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; return options;
} }
module.exports =async function compile(langFolder,opts={}){ module.exports =async function compile(langFolder,opts={}){
const options = normalizeCompileOptions(opts); const options = normalizeCompileOptions(opts);
const { output,moduleType } = options; let { moduleType } = options;
if(moduleType==="auto"){ if(moduleType==="auto"){
moduleType = findModuleType(langFolder) moduleType = findModuleType(langFolder)
@ -63,7 +58,7 @@ module.exports =async function compile(langFolder,opts={}){
const langSettings = module.default; const langSettings = module.default;
let { languages,defaultLanguage,activeLanguage,namespaces } = langSettings 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: {}",defaultLanguage)
logger.log("激活语言\t: {}",activeLanguage) logger.log("激活语言\t: {}",activeLanguage)
logger.log("名称空间\t: {}",Object.keys(namespaces).join(",")) 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("读取语言文件{}失败:{}",file,e.message)
} }
}) })
logger.log(" - 合成语言包文本,共{}条",Object.keys(messages).length) logger.log(" - 合成{}条语言包文本",Object.keys(messages).length)
// 2. 为每一个文本内容生成一个唯一的id // 2. 为每一个文本内容生成一个唯一的id
let messageIds = {} let messageIds = {}
@ -158,12 +153,12 @@ module.exports =async function compile(langFolder,opts={}){
let packageJson = {} let packageJson = {}
if(moduleType==="esm"){ if(moduleType==="esm"){
packageJson = { packageJson = {
version:"1.0.0", license:"MIT",
type:"module", type:"module",
} }
}else{ }else{
packageJson = { packageJson = {
version:"1.0.0", license:"MIT",
} }
} }
fs.writeFileSync(packageJsonFile,JSON.stringify(packageJson,null,4)) fs.writeFileSync(packageJsonFile,JSON.stringify(packageJson,null,4))

View File

@ -2,6 +2,7 @@ const { findModuleType } = require("./utils")
const path = require("path") const path = require("path")
const fs = require("fs") const fs = require("fs")
const gulp = require("gulp") const gulp = require("gulp")
const extractor = require("./extract.plugin")
const createLogger = require("logsets") const createLogger = require("logsets")
const logger = createLogger() const logger = createLogger()
@ -13,13 +14,11 @@ module.exports = function(targetPath,options={}){
const folders = filetypes.map(ftype=>{ const folders = filetypes.map(ftype=>{
if(ftype.startsWith(".")) ftype = "*"+ftype if(ftype.startsWith(".")) ftype = "*"+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,"languages","**")}`)
folders.push(`!${path.join(targetPath,"node_modules")}`) folders.push(`!${path.join(targetPath,"node_modules","**")}`)
folders.push(`!${path.join(targetPath,"**","node_modules","**")}`) folders.push(`!${path.join(targetPath,"**","node_modules","**")}`)
// 排除文件夹
console.log("exclude",exclude)
if(!Array.isArray(exclude) && exclude){ if(!Array.isArray(exclude) && exclude){
exclude = exclude.split(",") exclude = exclude.split(",")
} }
@ -27,10 +26,18 @@ module.exports = function(targetPath,options={}){
exclude.forEach(folder=>{ exclude.forEach(folder=>{
folders.push(`!${path.join(targetPath,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) options.outputPath = path.join(targetPath,"languages")
gulp.src(folders)
.pipe(extractor(options))
//gulp.src(path.join(targetPath,"**/*.json")) .pipe(gulp.dest(options.outputPath))
} }

View File

@ -12,8 +12,10 @@ const fs = require('fs')
const readJson = require("readjson") const readJson = require("readjson")
const createLogger = require("logsets") const createLogger = require("logsets")
const { replaceInterpolateVars,getDataTypeName } = require("@voerkai18n/runtime") const { replaceInterpolateVars,getDataTypeName } = require("@voerkai18n/runtime")
const { findModuleType } = require("./utils") const { findModuleType,createPackageJsonFile } = require("./utils")
const logger = createLogger() const logger = createLogger()
const { t } = require("./languages")
// 捕获翻译文本的默认正则表达式 // 捕获翻译文本的默认正则表达式
const DefaultTranslateExtractor = String.raw`\b{funcName}\(\s*("|'){1}(?:((?<namespace>\w+)::))?(?<text>.*?)(((\1\s*\)){1})|((\1){1}\s*(,(\w|\d|(?:\{.*\})|(?:\[.*\])|([\"\'\(].*[\"\'\)]))*)*\s*\)))` const DefaultTranslateExtractor = String.raw`\b{funcName}\(\s*("|'){1}(?:((?<namespace>\w+)::))?(?<text>.*?)(((\1\s*\)){1})|((\1){1}\s*(,(\w|\d|(?:\{.*\})|(?:\[.*\])|([\"\'\(].*[\"\'\)]))*)*\s*\)))`
@ -349,13 +351,14 @@ function updateLanguageFile(fromTexts,toLangFile,options){
module.exports = function(options={}){ module.exports = function(options={}){
options = normalizeLanguageOptions(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(t("支持的语言\t: {}"),options.languages.map(item=>`${item.title}(${item.name})`).join(","))
logger.log("Default language\t: {}",options.defaultLanguage) logger.log("默认语言\t: {}",options.defaultLanguage)
logger.log("Active language\t\t: {}",options.activeLanguage) logger.log("激活语言\t: {}",options.activeLanguage)
logger.log("Language namespaces\t: {}",Object.keys(options.namespaces).join(",")) logger.log("名称空间\t: {}",Object.keys(options.namespaces).join(","))
logger.log("")
logger
// 保存提交提取的文本 = {} // 保存提交提取的文本 = {}
let results = {} let results = {}
let fileCount=0 // 文件总数 let fileCount=0 // 文件总数
@ -375,21 +378,24 @@ module.exports = function(options={}){
fileCount++ fileCount++
if(debug){ if(debug){
const textCount = Object.values(texts).reduce((sum,item)=>sum+Object.keys(item).length,0) 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){ }catch(err){
logger.log("Error while extract messages from <{}> : {}",file.relative,err.message) logger.log("从<{}>提取信息时出错 : {}",file.relative,err.message)
} }
callback() callback()
},function(callback){ },function(callback){
logger.log("") logger.log("")
logger.log("Extracting finished.") logger.log("翻译信息提取完成。")
logger.log(" - Total of files\t: {}",fileCount) logger.log(" - 文件总数\t: {}",fileCount)
logger.log(" - Output location\t: {}",outputPath) logger.log(" - 输出路径\t: {}",outputPath)
const translatesPath = path.join(outputPath,"translates") const translatesPath = path.join(outputPath,"translates")
if(!fs.existsSync(outputPath)) fs.mkdirSync(outputPath) if(!fs.existsSync(outputPath)) fs.mkdirSync(outputPath)
if(!fs.existsSync(translatesPath)) fs.mkdirSync(translatesPath) if(!fs.existsSync(translatesPath)) fs.mkdirSync(translatesPath)
if(!("default" in results)){
results["default"] = {}
}
// 每个名称空间对应一个文件 // 每个名称空间对应一个文件
for(let [namespace,texts] of Object.entries(results)){ for(let [namespace,texts] of Object.entries(results)){
const langFile = path.join(outputPath,"translates",`${namespace}.json`) const langFile = path.join(outputPath,"translates",`${namespace}.json`)
@ -397,10 +403,10 @@ module.exports = function(options={}){
const langTexts = {} const langTexts = {}
if(isExists){ if(isExists){
updateLanguageFile(texts,langFile,options) updateLanguageFile(texts,langFile,options)
logger.log(" Update language file : {}",path.relative(outputPath,langFile)) logger.log(" √ 更新语言文件 : {}",path.relative(outputPath,langFile))
}else{ }else{
fs.writeFileSync(langFile,JSON.stringify(texts,null,4)) 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 , 仅当不存在时才生成 // 生成语言配置文件 settings.js , 仅当不存在时才生成
@ -413,13 +419,20 @@ module.exports = function(options={}){
namespaces : options.namespaces namespaces : options.namespaces
} }
fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`) fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`)
logger.log(" - Generate settings of language : {}",settingsFile) logger.log(" - 生成语言配置文件: {}",settingsFile)
}else{ }else{
logger.log(" - Settings of language already exists : {}",settingsFile) logger.log(" - 已更新语言配置文件: {}",settingsFile)
} }
// 生成package.json // 生成package.json
const packageJsonFile = path.join(outputPath,"package.json") createPackageJsonFile(outputPath)
fs.writeFileSync(packageJsonFile,`${JSON.stringify({type:"commonjs"},null,4)}`)
logger.log("下一步:")
logger.log(" - 运行<{}>编译语言包","voerkai18n compile")
logger.log(" - 在源码中导入编译后的语言包[{}]","import './languages'")
callback() callback()
}); });
} }

View File

@ -5,18 +5,18 @@ const fs = require("fs");
const { importModule } = require('./utils'); const { importModule } = require('./utils');
const deepmerge = require('deepmerge'); const deepmerge = require('deepmerge');
const logger = createLogger() const logger = createLogger()
require('dotenv').config()
const { t, changeLanguage } = require("./languages")
const program = new Command(); const program = new Command();
program
.option('-d, --debug', '输出调试信息')
program program
.command('init') .command('init')
.argument('[location]', '工程项目所在目录') .argument('[location]', t('工程项目所在目录'))
.description('初始化项目国际化配置') .description('初始化项目国际化配置')
.option('-d, --debug', '输出调试信息')
.option('-r, --reset', '重新生成当前项目的语言配置') .option('-r, --reset', '重新生成当前项目的语言配置')
.option('-m, --moduleType [type]', '生成的js模块类型,默认esm',"esm") .option('-m, --moduleType [type]', '生成的js模块类型,取值auto,esm,cjs',"auto")
.option('-lngs, --languages <languages...>', '支持的语言列表', ['cn','en']) .option('-lngs, --languages <languages...>', '支持的语言列表', ['cn','en'])
.action((location,options) => { .action((location,options) => {
if(!location) { if(!location) {
@ -25,8 +25,11 @@ program
location = path.join(process.cwd(),location) location = path.join(process.cwd(),location)
} }
logger.log("工程目录:{}",location) logger.log("工程目录:{}",location)
//
if(options.debug){
logger.format(options,{compact:true})
}
const initializer = require("./init.command") const initializer = require("./init.command")
options.debug=true
initializer(location,options) initializer(location,options)
}); });
@ -35,7 +38,7 @@ program
.command('extract') .command('extract')
.description('扫描并提取所有待翻译的字符串到<languages/translates>文件夹中') .description('扫描并提取所有待翻译的字符串到<languages/translates>文件夹中')
.option('-d, --debug', '输出调试信息') .option('-d, --debug', '输出调试信息')
.option('-lngs, --languages', '支持的语言', 'cn,en,de,fr,es,it,jp') .option('-lngs, --languages', '支持的语言', 'cn,en')
.option('-d, --defaultLanguage', '默认语言', 'cn') .option('-d, --defaultLanguage', '默认语言', 'cn')
.option('-a, --activeLanguage', '激活语言', 'cn') .option('-a, --activeLanguage', '激活语言', 'cn')
.option('-ns, --namespaces', '翻译名称空间') .option('-ns, --namespaces', '翻译名称空间')
@ -56,20 +59,51 @@ program
logger.log("工程目录:{}",location) logger.log("工程目录:{}",location)
const langSettingsFile = path.join(location,"languages","settings.js") const langSettingsFile = path.join(location,"languages","settings.js")
if(fs.existsSync(langSettingsFile)){ if(fs.existsSync(langSettingsFile)){
logger.log("语言配置文件<{}>已存在.将优先使用此配置文件中参数来提取文本",langSettingsFile) logger.log("语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本","./languages/settings.js")
let lngOptions = (await importModule("file:///"+langSettingsFile)).default let lngOptions = (await importModule("file:///"+langSettingsFile)).default
options.languages = lngOptions.languages options.languages = lngOptions.languages
options.defaultLanguage = lngOptions.defaultLanguage options.defaultLanguage = lngOptions.defaultLanguage
options.activeLanguage = lngOptions.activeLanguage options.activeLanguage = lngOptions.activeLanguage
options.namespaces = lngOptions.namespaces options.namespaces = lngOptions.namespaces
} }
//
if(options.debug){
logger.format(options,{compact:true})
}
const extractor = require('./extract.command'); const extractor = require('./extract.command');
extractor(location,options) extractor(location,options)
}); });
program
.command('compile')
.description('编译语言包文件<languages>文件夹中')
.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(); const options = program.opts();

View File

@ -4,20 +4,20 @@
*/ */
const { findModuleType } = require("./utils") const { findModuleType,createPackageJsonFile } = require("./utils")
const path = require("path") const path = require("path")
const fs = require("fs") const fs = require("fs")
const createLogger = require("logsets") const createLogger = require("logsets")
const logger = createLogger() const logger = createLogger()
module.exports = function(targetPath,{debug = true,languages=["cn","en"],defaultLanguage="cn",activeLanguage="cn",moduleType = "auto",reset=false}={}){ module.exports = function(targetPath,{debug = true,languages=["cn","en"],defaultLanguage="cn",activeLanguage="cn",moduleType = "auto",reset=false}={}){
// 语言文件夹名称 // 语言文件夹名称
const langPath = "languages" const langPath = "languages"
// 查找当前项目的语言包类型路径 // 查找当前项目的语言包类型路径
const lngPath = path.join(targetPath,langPath)
moduleType = createPackageJsonFile(lngPath,moduleType)
if(moduleType==="auto"){
moduleType = findModuleType(targetPath)
}
if(moduleType==null) { if(moduleType==null) {
if(debug){ if(debug){
logger.log("找不到{}文件,{}只能在js项目工程中使用","package.json","voerkai18n") 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)){ if(!fs.existsSync(lngPath)){
fs.mkdirSync(lngPath) fs.mkdirSync(lngPath)
if(debug) logger.log("创建语言包文件夹: {}",lngPath) if(debug) logger.log("创建语言包文件夹: {}",lngPath)
@ -44,21 +44,19 @@ module.exports = function(targetPath,{debug = true,languages=["cn","en"],default
activeLanguage, activeLanguage,
namespaces:{} namespaces:{}
} }
const packageJsonFile = path.join(targetPath,"languages","package.json") // 写入配置文件
if(["esm","es"].includes(moduleType)){ if(["esm","es"].includes(moduleType)){
fs.writeFileSync(settingsFile,`export default ${JSON.stringify(settings,null,4)}`) fs.writeFileSync(settingsFile,`export default ${JSON.stringify(settings,null,4)}`)
fs.writeFileSync(packageJsonFile,JSON.stringify({type:"module"},null,4))
}else{ }else{
fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`) fs.writeFileSync(settingsFile,`module.exports = ${JSON.stringify(settings,null,4)}`)
fs.writeFileSync(packageJsonFile,JSON.stringify({},null,4))
} }
if(debug) { if(debug) {
logger.log("生成语言配置文件:{}","./languages/settings.js") logger.log("生成语言配置文件:{}","./languages/settings.js")
logger.log("拟支持的语言:{}",settings.languages.map(l=>l.name).join(",")) logger.log("拟支持的语言:{}",settings.languages.map(l=>l.name).join(","))
logger.log("下一步:") logger.log("初始化成功,下一步:")
logger.log(" - 编辑{}确定拟支持的语言种类等参数","languages/settings.js") logger.log(" - 编辑{}确定拟支持的语言种类等参数","languages/settings.js")
logger.log(" - 运行<{}>扫描提取要翻译的文本","voerkai18n extract") logger.log(" - 运行<{}>扫描提取要翻译的文本","voerkai18n extract")
logger.log(" - 运行<{}>编译语言包","voerkai18n compile") logger.log(" - 运行<{}>编译语言包","voerkai18n compile")
} }
} }

View File

@ -0,0 +1,4 @@
module.exports = {
"1": "工程项目所在目录",
"2": "支持的语言\\t: {}"
}

View File

@ -0,0 +1,4 @@
module.exports = {
"1": "Project directory",
"2": "Supported Languages\\t: {}"
}

View File

@ -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

View File

@ -0,0 +1,4 @@
module.exports = {
"工程项目所在目录": 1,
"支持的语言\\t: {}": 2
}

View File

@ -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

View File

@ -0,0 +1,3 @@
{
"license": "MIT"
}

View File

@ -0,0 +1,15 @@
module.exports = {
"languages": [
{
"name": "cn",
"title": "中文"
},
{
"name": "en",
"title": "英文"
}
],
"defaultLanguage": "cn",
"activeLanguage": "cn",
"namespaces": {}
}

View File

@ -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"
]
}
}

View File

@ -4,7 +4,9 @@
"description": "VoerkaI18n Tools", "description": "VoerkaI18n Tools",
"main": "index.js", "main": "index.js",
"scripts": { "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": "", "author": "",
"bin": { "bin": {
@ -18,9 +20,10 @@
"art-template": "^4.13.2", "art-template": "^4.13.2",
"commander": "^9.0.0", "commander": "^9.0.0",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"dotenv": "^16.0.0",
"glob": "^7.2.0", "glob": "^7.2.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"logsets": "^1.0.7", "logsets": "^1.0.8",
"readjson": "^2.2.2", "readjson": "^2.2.2",
"through2": "^4.0.2", "through2": "^4.0.2",
"vinyl": "^2.2.1" "vinyl": "^2.2.1"

View File

@ -5,7 +5,7 @@ import defaultMessages from "./{{defaultLanguage}}.js"
import scopeSettings from "./settings.js" import scopeSettings from "./settings.js"
{{else}} {{else}}
const messageIds = require("./idMap") const messageIds = require("./idMap")
const { translate,i18n } = require("@voerkai18n/runtime") const { translate,I18nManager } = require("@voerkai18n/runtime")
const defaultMessages = require("./{{defaultLanguage}}.js") const defaultMessages = require("./{{defaultLanguage}}.js")
const scopeSettings = require("./settings.js") const scopeSettings = require("./settings.js")
{{/if}} {{/if}}
@ -43,9 +43,13 @@ const languages = {{@ JSON.stringify(languages,null,4) }}
VoerkaI18n.register(scope) VoerkaI18n.register(scope)
{{if moduleType === "esm"}} {{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}} {{else}}
module.exports.languages = languages module.exports.languages = languages
module.exports.scope = scope module.exports.scope = scope
module.exports.t = t 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}} {{/if}}

View File

@ -1,4 +1,6 @@
const path = require("path")
const fs = require("fs")
const readJson = require("readjson")
async function importModule(url){ async function importModule(url){
try{ try{
@ -21,10 +23,25 @@ async function importModule(url){
let parent = path.dirname(folder) let parent = path.dirname(folder)
if(parent===folder) return null if(parent===folder) return null
return findModuleType(parent) return findModuleType(parent)
}catch{ }catch(e){
return "esm" 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){ function isPlainObject(obj){
if (typeof obj !== 'object' || obj === null) return false; if (typeof obj !== 'object' || obj === null) return false;
var proto = Object.getPrototypeOf(obj); var proto = Object.getPrototypeOf(obj);
@ -95,7 +112,8 @@ function createJsModuleFile(filename,defaultExports={},namedExports={},moduleTyp
} }
module.exports = { module.exports = {
importModule, importModule,
findModuleType findModuleType,
createPackageJsonFile
} }

15
pnpm-lock.yaml generated
View File

@ -92,9 +92,10 @@ importers:
art-template: ^4.13.2 art-template: ^4.13.2
commander: ^9.0.0 commander: ^9.0.0
deepmerge: ^4.2.2 deepmerge: ^4.2.2
dotenv: ^16.0.0
glob: ^7.2.0 glob: ^7.2.0
gulp: ^4.0.2 gulp: ^4.0.2
logsets: ^1.0.7 logsets: ^1.0.8
readjson: ^2.2.2 readjson: ^2.2.2
through2: ^4.0.2 through2: ^4.0.2
vinyl: ^2.2.1 vinyl: ^2.2.1
@ -105,9 +106,10 @@ importers:
art-template: 4.13.2 art-template: 4.13.2
commander: 9.0.0 commander: 9.0.0
deepmerge: 4.2.2 deepmerge: 4.2.2
dotenv: 16.0.0
glob: 7.2.0 glob: 7.2.0
gulp: 4.0.2 gulp: 4.0.2
logsets: 1.0.7 logsets: 1.0.8
readjson: 2.2.2 readjson: 2.2.2
through2: 4.0.2 through2: 4.0.2
vinyl: 2.2.1 vinyl: 2.2.1
@ -2567,6 +2569,11 @@ packages:
webidl-conversions: 5.0.0 webidl-conversions: 5.0.0
dev: true dev: true
/dotenv/16.0.0:
resolution: {integrity: sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==}
engines: {node: '>=12'}
dev: false
/duplexify/3.7.1: /duplexify/3.7.1:
resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
dependencies: dependencies:
@ -4166,8 +4173,8 @@ packages:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true dev: true
/logsets/1.0.7: /logsets/1.0.8:
resolution: {integrity: sha512-O9VFAcMEuUY1tyq81hdZada+77fLl2Ff602AhlKpEpVr3gMyPFM67tILlBZZgnCfcmoRUo6nwhl26xXURHPH0A==} resolution: {integrity: sha512-9XuCtIjGvAWbi+JgF2+NI5Bb55uvzwgCFvlo/pafXdZjVC0DDri2k+Jzv7hWg0audTrw4FTnIBiSo3yOlEpmHQ==}
dependencies: dependencies:
'@babel/runtime-corejs3': registry.npmmirror.com/@babel/runtime-corejs3/7.17.2 '@babel/runtime-corejs3': registry.npmmirror.com/@babel/runtime-corejs3/7.17.2
ansicolor: registry.npmmirror.com/ansicolor/1.1.100 ansicolor: registry.npmmirror.com/ansicolor/1.1.100

View File

@ -1,7 +1,7 @@
/** /**
* 测试demp app的语言运行环境 * 测试demp app的语言运行环境
*/ */
const compile = require('../packages/tools/compile'); const compile = require('../packages/tools/compile.command');
const path = require("path") const path = require("path")