Alpha版本发布,并完成文档
This commit is contained in:
parent
353234cd01
commit
b7340a9989
BIN
images/arch.png
Normal file
BIN
images/arch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
@ -1,4 +1,7 @@
|
||||
{
|
||||
"type": "commonjs",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@voerkai18n/tools": "workspace:^1.0.0"
|
||||
}
|
||||
}
|
@ -10,13 +10,10 @@ module.exports = {
|
||||
"title" : "标题",
|
||||
"content": "内容"
|
||||
},
|
||||
"产品清单\t{}": [
|
||||
"手机",
|
||||
"电脑"
|
||||
],
|
||||
"产品清单\t{}": 2,
|
||||
"产品价格" : {
|
||||
"手机" : 1299,
|
||||
"电脑": 3999
|
||||
"电脑\t:": 3999
|
||||
},
|
||||
"产品\\清单" : 1
|
||||
"产品\\清\\单": 1
|
||||
}
|
@ -1,10 +1,16 @@
|
||||
const objectToString = require("../tools/stringify")
|
||||
const { objectStringify } = require("../tools/stringify")
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const k1 = "产品清单\t{}"
|
||||
const k2 = "产品\\清单"
|
||||
const data = {
|
||||
const k2 = "产品\\清\\单"
|
||||
|
||||
const newTexts = {
|
||||
[k1]:1,
|
||||
[k2]:2
|
||||
}
|
||||
|
||||
let data = {
|
||||
name:"张三丰",
|
||||
age:12,
|
||||
active:true,
|
||||
@ -21,14 +27,16 @@ const data = {
|
||||
"手机",
|
||||
"电脑"
|
||||
],
|
||||
"产品清单\t{}":2,
|
||||
"产品价格":{
|
||||
"手机":1299,
|
||||
"电脑":3999
|
||||
"电脑\t:":3999
|
||||
},
|
||||
[k2]:1
|
||||
"产品\\清\\单":1
|
||||
}
|
||||
|
||||
const result = objectToString(data)
|
||||
|
||||
const result = objectStringify(data)
|
||||
|
||||
console.log(result)
|
||||
|
||||
@ -36,6 +44,15 @@ fs.writeFileSync(path.join(__dirname,"./data.js"),`module.exports = ${result}`)
|
||||
|
||||
const loaded = require("./data.js")
|
||||
|
||||
Object.entries(loaded).forEach(([key,value])=>{
|
||||
if(key===k1){
|
||||
console.log("k1=",value)
|
||||
}else if(key===k2){
|
||||
console.log("k2=",value)
|
||||
console.log("k2 in data",k2 in data)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(loaded[k1])
|
||||
console.log(loaded[k2])
|
||||
|
||||
|
31
packages/runtime/eventemitter.js
Normal file
31
packages/runtime/eventemitter.js
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
*
|
||||
* 简单的事件触发器
|
||||
*
|
||||
*/
|
||||
module.exports = class EventEmitter{
|
||||
constructor(){
|
||||
this._callbacks = []
|
||||
}
|
||||
on(callback){
|
||||
if(this._callbacks.includes(callback)) return
|
||||
this._callbacks.push(callback)
|
||||
}
|
||||
off(callback){
|
||||
for(let i=0;i<this._callbacks.length;i++){
|
||||
if(this._callbacks[i]===callback ){
|
||||
this._callbacks.splice(i,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
offAll(){
|
||||
this._callbacks = []
|
||||
}
|
||||
async emit(...args){
|
||||
if(Promise.allSettled){
|
||||
await Promise.allSettled(this._callbacks.map(cb=>cb(...args)))
|
||||
}else{
|
||||
await Promise.all(this._callbacks.map(cb=>cb(...args)))
|
||||
}
|
||||
}
|
||||
}
|
@ -27,17 +27,18 @@
|
||||
* @returns
|
||||
*/
|
||||
function dict(value,...args){
|
||||
try{
|
||||
for(let i=0;i<args.length;i+=2){
|
||||
if(args[i]===value){
|
||||
return args[i+1]
|
||||
}
|
||||
}
|
||||
if(args.length >0 && (args.length % 2!==0)) return args[args.length-1]
|
||||
}catch{}
|
||||
return value
|
||||
}
|
||||
|
||||
function formatCurrency(value,symbol,retainDots){
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
"*":{
|
||||
@ -45,6 +46,7 @@ module.exports = {
|
||||
Date:(value)=>value.toLocaleString()
|
||||
},
|
||||
time:(value)=> value.toLocaleTimeString(),
|
||||
shorttime:(value)=> value.toLocaleTimeString(),
|
||||
date: (value)=> value.toLocaleDateString(),
|
||||
dict, //字典格式化器
|
||||
},
|
||||
@ -52,11 +54,15 @@ module.exports = {
|
||||
$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)=>`$${value}`,
|
||||
currency:(value)=>{
|
||||
return `$${value}`
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
const deepMerge = require("deepmerge")
|
||||
const formatters = require("./formatters")
|
||||
const EventEmitter = require("./eventemitter")
|
||||
const i18nScope = require("./scope.js")
|
||||
let formatters = require("./formatters")
|
||||
|
||||
|
||||
// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
|
||||
// 不支持参数: let varWithPipeRegexp = /\{\s*(?<varname>\w+)?(?<formatters>(\s*\|\s*\w*\s*)*)\s*\}/g
|
||||
@ -30,6 +33,8 @@ let varReplaceRegexp =String.raw`\{\s*{varname}\s*\}`
|
||||
function hasInterpolation(str){
|
||||
return str.includes("{") && str.includes("}")
|
||||
}
|
||||
const DataTypes = ["String","Number","Boolean","Object","Array","Function","Error","Symbol","RegExp","Date","Null","Undefined","Set","Map","WeakSet","WeakMap"]
|
||||
|
||||
/**
|
||||
* 获取指定变量类型名称
|
||||
* getDataTypeName(1) == Number
|
||||
@ -251,6 +256,7 @@ function getDataTypeDefaultFormatter(scope,activeLanguage,dataType){
|
||||
// 先在当前作用域中查找,再在全局查找
|
||||
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
|
||||
@ -457,7 +463,29 @@ function getPluraMessage(messages,value){
|
||||
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,"\\")
|
||||
}
|
||||
/**
|
||||
* 翻译函数
|
||||
*
|
||||
@ -517,8 +545,9 @@ function translate(message) {
|
||||
// 2.2 从当前语言包中取得翻译文本模板字符串
|
||||
// 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
|
||||
// JSON.stringify在进行转换时会将\t\n\r转换为\\t\\n\\r,这样在进行匹配时就出错
|
||||
let msgId = isMessageId(content) ? content : scope.idMap[content]
|
||||
let msgId = isMessageId(content) ? content : scope.idMap[escape(content)]
|
||||
content = scope.messages[msgId] || content
|
||||
content = unescape(content)
|
||||
}
|
||||
|
||||
// 3. 处理复数
|
||||
@ -561,10 +590,11 @@ function translate(message) {
|
||||
* VoerkaI18n.off("change",(language)=>{})
|
||||
*
|
||||
* */
|
||||
class I18nManager{
|
||||
class I18nManager extends EventEmitter{
|
||||
static instance = null; // 单例引用
|
||||
callbacks = [] // 当切换语言时的回调事件
|
||||
constructor(settings={}){
|
||||
super()
|
||||
if(I18nManager.instance!=null){
|
||||
return I18nManager.instance;
|
||||
}
|
||||
@ -581,96 +611,39 @@ function translate(message) {
|
||||
get defaultLanguage(){ return this.this._settings.defaultLanguage}
|
||||
// 支持的语言列表
|
||||
get languages(){ return this._settings.languages}
|
||||
// 订阅语言切换事件
|
||||
on(callback){
|
||||
this.callbacks.push(callback)
|
||||
}
|
||||
off(callback){
|
||||
for(let i=0;i<this.callbacks.length;i++){
|
||||
if(this.callbacks[i]===callback ){
|
||||
this.callbacks.splice(i,1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
offAll(){
|
||||
this.callbacks=[]
|
||||
}
|
||||
/**
|
||||
* 切换语言时触发语言切换事件回调
|
||||
*/
|
||||
async _triggerChangeEvents(newLanguage){
|
||||
try{
|
||||
await this._updateScopes(newLanguage)
|
||||
if(Promise.allSettled){
|
||||
await Promise.allSettled(this.callbacks.map(cb=>cb(newLanguage)))
|
||||
}else{
|
||||
await Promise.all(this.callbacks.map(cb=>cb(newLanguage)))
|
||||
}
|
||||
}catch(e){
|
||||
console.warn("Error while executing language change events:",e.message)
|
||||
}
|
||||
}
|
||||
// 全局格式化器
|
||||
get formatters(){ return formatters }
|
||||
/**
|
||||
* 切换语言
|
||||
*/
|
||||
async change(value){
|
||||
if(this.languages.findIndex(lang=>lang.name === value)!==-1){
|
||||
await this._triggerChangeEvents(value)
|
||||
// 通知所有作用域刷新到对应的语言包
|
||||
await this._refreshScopes(value)
|
||||
this._settings.activeLanguage = value
|
||||
/// 触发语言切换事件
|
||||
await this.emit(value)
|
||||
}else{
|
||||
throw new Error("Not supported language:"+value)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取指定作用域的下的语言包加载器
|
||||
*
|
||||
* 同时会进行语言兼容性处理
|
||||
*
|
||||
* 如scope里面定义了一个cn的语言包,当切换到zh-cn时,会自动加载cn语言包
|
||||
*
|
||||
*
|
||||
* @param {*} scope
|
||||
* @param {*} lang
|
||||
*/
|
||||
_getScopeLoader(scope,lang){
|
||||
|
||||
}
|
||||
/**
|
||||
* 当切换语言时调用此方法来加载更新语言包
|
||||
* @param {*} newLanguage
|
||||
*/
|
||||
async _updateScopes(newLanguage){
|
||||
async _refreshScopes(newLanguage){
|
||||
// 并发执行所有作用域语言包的加载
|
||||
try{
|
||||
let scopeLoders = this._scopes.map(scope=>{
|
||||
return async ()=>{
|
||||
// 默认语言,所有均默认语言均采用静态加载方式,只需要简单的替换即可
|
||||
if(newLanguage === scope.defaultLanguage){
|
||||
scope.messages = scope.default
|
||||
return
|
||||
}
|
||||
// 异步加载语言文件
|
||||
const loader = scope.loaders[newLanguage]
|
||||
if(typeof(loader) === "function"){
|
||||
try{
|
||||
scope.messages = (await loader() ).default
|
||||
}catch(e){
|
||||
console.warn(`Error loading language ${newLanguage} : ${e.message}`)
|
||||
scope.messages = defaultMessages // 出错时回退到默认语言
|
||||
}
|
||||
}else{
|
||||
scope.messages = defaultMessages
|
||||
}
|
||||
}
|
||||
const scopeRefreshers = this._scopes.map(scope=>{
|
||||
return scope.refresh(newLanguage)
|
||||
})
|
||||
if(Promise.allSettled){
|
||||
await Promise.allSettled(scopeLoders.map((f)=>f()))
|
||||
await Promise.allSettled(scopeRefreshers)
|
||||
}else{
|
||||
await Promise.all(scopeLoders.map((f)=>f()))
|
||||
await Promise.all(scopeRefreshers)
|
||||
}
|
||||
}catch(e){
|
||||
console.warn("Error while refreshing scope:",e.message)
|
||||
console.warn("Error while refreshing i18n scopes:",e.message)
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -678,33 +651,36 @@ function translate(message) {
|
||||
* 注册一个新的作用域
|
||||
*
|
||||
* 每一个库均对应一个作用域,每个作用域可以有多个语言包,且对应一个翻译函数
|
||||
* scope={
|
||||
* defaultLanguage:"cn",
|
||||
default: defaultMessages, // 转换文本信息
|
||||
messages : defaultMessages, // 当前语言的消息
|
||||
idMap:messageIds,
|
||||
formatters:{
|
||||
...formatters,
|
||||
...i18nSettings.formatters || {}
|
||||
},
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
settings:{} // 引用全局VoerkaI18n实例的配置
|
||||
* }
|
||||
*
|
||||
* 除了默认语言外,其他语言采用动态加载的方式
|
||||
*
|
||||
* @param {*} scope
|
||||
*/
|
||||
register(scope){
|
||||
scope.global = this._settings
|
||||
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
|
||||
*/
|
||||
registerFormatters(formatters){
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,6 +690,7 @@ module.exports ={
|
||||
I18nManager,
|
||||
translate,
|
||||
languages,
|
||||
i18nScope,
|
||||
defaultLanguageSettings,
|
||||
getDataTypeName,
|
||||
isNumber,
|
||||
|
115
packages/runtime/scope.js
Normal file
115
packages/runtime/scope.js
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
module.exports = 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 } = require("./")
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
# 命令行工具使用的语言
|
||||
language=en
|
||||
LANGUAGE=en
|
@ -27,11 +27,11 @@ const readJson = require("readjson")
|
||||
const glob = require("glob")
|
||||
const createLogger = require("logsets")
|
||||
const path = require("path")
|
||||
const { importModule,findModuleType } = require("./utils")
|
||||
const { t,importModule,findModuleType,getCurrentPackageJson} = 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({
|
||||
moduleType:"auto" // 指定编译后的语言文件的模块类型,取值common,cjs,esm,es
|
||||
@ -49,7 +49,7 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
if(moduleType==="auto"){
|
||||
moduleType = findModuleType(langFolder)
|
||||
}
|
||||
|
||||
const projectPackageJson = getCurrentPackageJson(langFolder)
|
||||
// 加载多语言配置文件
|
||||
const settingsFile = path.join(langFolder,"settings.js")
|
||||
try{
|
||||
@ -58,13 +58,13 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
const langSettings = module.default;
|
||||
let { languages,defaultLanguage,activeLanguage,namespaces } = langSettings
|
||||
|
||||
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(","))
|
||||
logger.log("模块类型\t: {}",moduleType)
|
||||
logger.log(t("支持的语言\t: {}"),languages.map(item=>`${item.title}(${item.name})`).join(","))
|
||||
logger.log(t("默认语言\t: {}"),defaultLanguage)
|
||||
logger.log(t("激活语言\t: {}"),activeLanguage)
|
||||
logger.log(t("名称空间\t: {}"),Object.keys(namespaces).join(","))
|
||||
logger.log(t("模块类型\t: {}"),moduleType)
|
||||
logger.log("")
|
||||
logger.log("编译结果输出至:{}",langFolder)
|
||||
logger.log(t("编译结果输出至:{}"),langFolder)
|
||||
|
||||
// 1. 合并生成最终的语言文件
|
||||
let messages = {} ,msgId =1
|
||||
@ -79,10 +79,10 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
}
|
||||
})
|
||||
}catch(e){
|
||||
logger.log("读取语言文件{}失败:{}",file,e.message)
|
||||
logger.log(t("读取语言文件{}失败:{}"),file,e.message)
|
||||
}
|
||||
})
|
||||
logger.log(" - 共合成{}条语言包文本",Object.keys(messages).length)
|
||||
logger.log(t(" - 共合成{}条语言包文本"),Object.keys(messages).length)
|
||||
|
||||
// 2. 为每一个文本内容生成一个唯一的id
|
||||
let messageIds = {}
|
||||
@ -103,7 +103,7 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
}else{
|
||||
fs.writeFileSync(langFile,`module.exports = ${JSON.stringify(langMessages,null,4)}`)
|
||||
}
|
||||
logger.log(" - 语言包文件: {}",path.basename(langFile))
|
||||
logger.log(t(" - 语言包文件: {}"),path.basename(langFile))
|
||||
})
|
||||
|
||||
// 4. 生成id映射文件
|
||||
@ -113,34 +113,46 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
}else{
|
||||
fs.writeFileSync(idMapFile,`module.exports = ${JSON.stringify(messageIds,null,4)}`)
|
||||
}
|
||||
logger.log(" - idMap文件: {}",path.basename(idMapFile))
|
||||
logger.log(t(" - idMap文件: {}"),path.basename(idMapFile))
|
||||
|
||||
// 5. 生成编译后的访问入口文件
|
||||
const entryFile = path.join(langFolder,"index.js")
|
||||
const entryContent = artTemplate(path.join(__dirname,"templates","entry.js"), {languages,defaultLanguage,activeLanguage,namespaces,moduleType,JSON } )
|
||||
fs.writeFileSync(entryFile,entryContent)
|
||||
logger.log(" - 访问入口文件: {}",path.basename(entryFile))
|
||||
const templateContext = {
|
||||
scopeId:projectPackageJson.name,
|
||||
languages,
|
||||
defaultLanguage,
|
||||
activeLanguage,
|
||||
namespaces,
|
||||
moduleType,
|
||||
JSON
|
||||
}
|
||||
|
||||
|
||||
// 6 . 生成编译后的格式化函数文件
|
||||
// 5 . 生成编译后的格式化函数文件
|
||||
const formattersFile = path.join(langFolder,"formatters.js")
|
||||
if(!fs.existsSync(formattersFile)){
|
||||
const formattersContent = artTemplate(path.join(__dirname,"templates","formatters.js"), {languages,defaultLanguage,activeLanguage,namespaces,moduleType } )
|
||||
const formattersContent = artTemplate(path.join(__dirname,"templates","formatters.js"), templateContext )
|
||||
fs.writeFileSync(formattersFile,formattersContent)
|
||||
logger.log(" - 格式化器:{}",path.basename(formattersFile))
|
||||
logger.log(t(" - 格式化器:{}"),path.basename(formattersFile))
|
||||
}else{ // 格式化器如果存在,则需要更改对应的模块类型
|
||||
let formattersContent = fs.readFileSync(formattersFile,"utf8").toString()
|
||||
if(moduleType == "esm"){
|
||||
formattersContent = formattersContent.replaceAll(/\s*module.exports\s*\=/g,"export default ")
|
||||
formattersContent = formattersContent.replaceAll(/\s*module.exports\./g,"export ")
|
||||
}else{
|
||||
formattersContent = formattersContent.replaceAll(/\s*export\s*default\s*/g,"module.exports = ")
|
||||
formattersContent = formattersContent.replaceAll(/\s*export\s*/g,"module.exports.")
|
||||
formattersContent = formattersContent.replaceAll(/^\s*export\s*default\s*/g,"module.exports = ")
|
||||
formattersContent = formattersContent.replaceAll(/^\s*export\s*/g,"module.exports.")
|
||||
}
|
||||
fs.writeFileSync(formattersFile,formattersContent)
|
||||
logger.log(" - 更新格式化器:{}",path.basename(formattersFile))
|
||||
logger.log(t(" - 更新格式化器:{}"),path.basename(formattersFile))
|
||||
}
|
||||
|
||||
// 6. 生成编译后的访问入口文件
|
||||
const entryFile = path.join(langFolder,"index.js")
|
||||
const entryContent = artTemplate(path.join(__dirname,"templates","entry.js"), templateContext )
|
||||
fs.writeFileSync(entryFile,entryContent)
|
||||
logger.log(t(" - 访问入口文件: {}"),path.basename(entryFile))
|
||||
|
||||
|
||||
|
||||
|
||||
// 7. 重新生成settings ,需要确保settings.js匹配模块类型
|
||||
if(moduleType==="esm"){
|
||||
fs.writeFileSync(settingsFile,`export default ${JSON.stringify(langSettings,null,4)}`)
|
||||
@ -163,6 +175,6 @@ module.exports =async function compile(langFolder,opts={}){
|
||||
}
|
||||
fs.writeFileSync(packageJsonFile,JSON.stringify(packageJson,null,4))
|
||||
}catch(e){
|
||||
logger.log("加载多语言配置文件<{}>失败: {} ",settingsFile,e.message)
|
||||
logger.log(t("加载多语言配置文件<{}>失败: {} "),settingsFile,e.message)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
const { findModuleType } = require("./utils")
|
||||
const { findModuleType,t } = require("./utils")
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const gulp = require("gulp")
|
||||
@ -9,7 +9,7 @@ const logger = createLogger()
|
||||
|
||||
module.exports = function(targetPath,options={}){
|
||||
let { filetypes,exclude} = options
|
||||
if(!filetypes) filetypes = ["*.js","*.json","*.jsx","*.ts","*.tsx","*.vue","*.html"]
|
||||
if(!filetypes) filetypes = ["*.js","*.jsx","*.ts","*.tsx","*.vue","*.html"]
|
||||
if(!Array.isArray(filetypes)) filetypes = filetypes.split(",")
|
||||
const folders = filetypes.map(ftype=>{
|
||||
if(ftype.startsWith(".")) ftype = "*"+ftype
|
||||
@ -19,6 +19,12 @@ module.exports = function(targetPath,options={}){
|
||||
folders.push(`!${path.join(targetPath,"languages","**")}`)
|
||||
folders.push(`!${path.join(targetPath,"node_modules","**")}`)
|
||||
folders.push(`!${path.join(targetPath,"**","node_modules","**")}`)
|
||||
folders.push("!**/babel.config.js")
|
||||
folders.push("!**/gulpfile.js")
|
||||
folders.push("!**/*.test.js")
|
||||
folders.push("!__test__/**/*.js")
|
||||
|
||||
|
||||
if(!Array.isArray(exclude) && exclude){
|
||||
exclude = exclude.split(",")
|
||||
}
|
||||
@ -28,11 +34,11 @@ module.exports = function(targetPath,options={}){
|
||||
})
|
||||
}
|
||||
if(!fs.existsSync(targetPath)){
|
||||
logger.log("目标文件夹<{}>不存在",targetPath)
|
||||
logger.log(t("目标文件夹<{}>不存在"),targetPath)
|
||||
return
|
||||
}
|
||||
if(options.debug){
|
||||
logger.log("扫描提取范围:")
|
||||
logger.log(t("扫描提取范围:"))
|
||||
logger.format(folders)
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,8 @@ const fs = require('fs')
|
||||
const readJson = require("readjson")
|
||||
const createLogger = require("logsets")
|
||||
const { replaceInterpolateVars,getDataTypeName } = require("@voerkai18n/runtime")
|
||||
const { findModuleType,createPackageJsonFile } = require("./utils")
|
||||
const stringify = require("./stringify")
|
||||
const { findModuleType,createPackageJsonFile,t } = require("./utils")
|
||||
const logger = createLogger()
|
||||
const { t } = require("./languages")
|
||||
|
||||
|
||||
// 捕获翻译文本正则表达式一: 能匹配完整的t(xx,...)函数调用,如果t函数调用不完整,则不能匹配到
|
||||
@ -311,35 +309,35 @@ function normalizeLanguageOptions(options){
|
||||
|
||||
|
||||
*/
|
||||
function updateLanguageFile(fromTexts,toLangFile,options){
|
||||
function updateLanguageFile(newTexts,toLangFile,options){
|
||||
const { output:{ updateMode } } = options
|
||||
|
||||
// 默认的overwrite
|
||||
if(!["merge","sync"].includes(updateMode)){
|
||||
fs.writeFileSync(toLangFile,stringify(targetTexts))
|
||||
fs.writeFileSync(toLangFile,JSON.stringify(oldTexts,null,4))
|
||||
return
|
||||
}
|
||||
let targetTexts = {}
|
||||
let oldTexts = {}
|
||||
// 读取原始翻译文件
|
||||
try{
|
||||
targetTexts = readJson.sync(toLangFile)
|
||||
oldTexts =JSON.parse(fs.readFileSync(toLangFile))// readJson.sync(toLangFile)
|
||||
}catch(e){
|
||||
logger.log("Error while read language file <{}>: {}",toLangFile,e.message)
|
||||
// 如果读取出错,可能是语言文件不是有效的json文件,则备份一下
|
||||
}
|
||||
// 同步模式下,如果原始文本在新扫描的内容中,则需要删除
|
||||
if(updateMode==="sync"){
|
||||
Object.keys(targetTexts).forEach((text)=>{
|
||||
if(!(text in fromTexts)){
|
||||
delete targetTexts[text]
|
||||
Object.keys(oldTexts).forEach((text)=>{
|
||||
if(!(text in newTexts)){
|
||||
delete oldTexts[text]
|
||||
}
|
||||
})
|
||||
}
|
||||
Object.entries(fromTexts).forEach(([text,sourceLangs])=>{
|
||||
if(text in targetTexts){ // 合并
|
||||
let targetLangs = targetTexts[text] //{cn:'',en:''}
|
||||
Object.entries(newTexts).forEach(([text,sourceLangs])=>{
|
||||
if(text in oldTexts){ // 合并
|
||||
let targetLangs = oldTexts[text] //{cn:'',en:''}
|
||||
Object.entries(sourceLangs).forEach(([langName,sourceText])=>{
|
||||
if(langName.startsWith("$")) return //
|
||||
if(langName.startsWith("$")) return // 以$开头的为保留字段,不是翻译内容
|
||||
const langExists = langName in targetLangs
|
||||
const targetText = targetLangs[langName]
|
||||
// 如果目标语言已经存在并且内容不为空,则不需要更新
|
||||
@ -348,10 +346,10 @@ function updateLanguageFile(fromTexts,toLangFile,options){
|
||||
}
|
||||
})
|
||||
}else{
|
||||
targetTexts[text] = sourceLangs
|
||||
oldTexts[text] = sourceLangs
|
||||
}
|
||||
})
|
||||
fs.writeFileSync(toLangFile,stringify(targetTexts))
|
||||
fs.writeFileSync(toLangFile,JSON.stringify(oldTexts,null,4))
|
||||
}
|
||||
|
||||
|
||||
@ -359,10 +357,10 @@ module.exports = function(options={}){
|
||||
options = normalizeLanguageOptions(options)
|
||||
let {debug,outputPath, updateMode,languages} = options
|
||||
|
||||
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(t("支持的语言\t: {}"),options.languages.map(item=>`${item.title}(${item.name})`).join(","))
|
||||
logger.log(t("默认语言\t: {}"),options.defaultLanguage)
|
||||
logger.log(t("激活语言\t: {}"),options.activeLanguage)
|
||||
logger.log(t("名称空间\t: {}"),Object.keys(options.namespaces).join(","))
|
||||
logger.log("")
|
||||
logger
|
||||
// 保存提交提取的文本 = {}
|
||||
@ -384,8 +382,10 @@ module.exports = function(options={}){
|
||||
fileCount++
|
||||
if(debug){
|
||||
const textCount = Object.values(texts).reduce((sum,item)=>sum+Object.keys(item).length,0)
|
||||
if(textCount>0){
|
||||
logger.log("提取<{}>, 发现 [{}] 名称空间,{} 条信息。",file.relative,Object.keys(texts).join(),textCount)
|
||||
}
|
||||
}
|
||||
}catch(err){
|
||||
logger.log("从<{}>提取信息时出错 : {}",file.relative,err.message)
|
||||
}
|
||||
@ -411,7 +411,7 @@ module.exports = function(options={}){
|
||||
updateLanguageFile(texts,langFile,options)
|
||||
logger.log(" √ 更新语言文件 : {}",path.relative(outputPath,langFile))
|
||||
}else{
|
||||
fs.writeFileSync(langFile,stringify(texts))
|
||||
fs.writeFileSync(langFile,JSON.stringify(texts,null,4))
|
||||
logger.log(" √ 保存语言文件 : {}",path.relative(outputPath,langFile))
|
||||
}
|
||||
}
|
||||
@ -430,7 +430,6 @@ module.exports = function(options={}){
|
||||
logger.log(" - 已更新语言配置文件: {}",settingsFile)
|
||||
}
|
||||
|
||||
|
||||
// 生成package.json
|
||||
createPackageJsonFile(outputPath)
|
||||
|
||||
|
@ -2,29 +2,31 @@ const { Command } = require('commander');
|
||||
const createLogger = require("logsets")
|
||||
const path = require("path")
|
||||
const fs = require("fs");
|
||||
const { importModule } = require('./utils');
|
||||
const { importModule,t } = require('./utils');
|
||||
const deepmerge = require('deepmerge');
|
||||
const logger = createLogger()
|
||||
require('dotenv').config()
|
||||
const { scope } = require('./languages');
|
||||
|
||||
const { t, changeLanguage } = require("./languages")
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.argument('[location]', t('工程项目所在目录'))
|
||||
.description('初始化项目国际化配置')
|
||||
.option('-d, --debug', '输出调试信息')
|
||||
.option('-r, --reset', '重新生成当前项目的语言配置')
|
||||
.option('-m, --moduleType [type]', '生成的js模块类型,取值auto,esm,cjs',"auto")
|
||||
.option('-lngs, --languages <languages...>', '支持的语言列表', ['cn','en'])
|
||||
.description(t('初始化项目国际化配置'))
|
||||
.option('-d, --debug', t('输出调试信息'))
|
||||
.option('-r, --reset', t('重新生成当前项目的语言配置'))
|
||||
.option('-m, --moduleType [type]', t('生成的js模块类型,取值auto,esm,cjs'),"auto")
|
||||
.option('-lngs, --languages <languages...>', t('支持的语言列表'), ['cn','en'])
|
||||
.hook("preAction",async function(location){
|
||||
|
||||
})
|
||||
.action((location,options) => {
|
||||
if(!location) {
|
||||
location = process.cwd()
|
||||
}else{
|
||||
location = path.join(process.cwd(),location)
|
||||
}
|
||||
logger.log("工程目录:{}",location)
|
||||
logger.log(t("工程目录:{}"),location)
|
||||
//
|
||||
if(options.debug){
|
||||
logger.format(options,{compact:true})
|
||||
@ -36,16 +38,16 @@ program
|
||||
|
||||
program
|
||||
.command('extract')
|
||||
.description('扫描并提取所有待翻译的字符串到<languages/translates>文件夹中')
|
||||
.option('-d, --debug', '输出调试信息')
|
||||
.option('-lngs, --languages', '支持的语言', 'cn,en')
|
||||
.option('-d, --defaultLanguage', '默认语言', 'cn')
|
||||
.option('-a, --activeLanguage', '激活语言', 'cn')
|
||||
.option('-ns, --namespaces', '翻译名称空间')
|
||||
.option('-e, --exclude <folders>', '排除要扫描的文件夹,多个用逗号分隔')
|
||||
.option('-u, --updateMode', '本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并', 'sync')
|
||||
.option('-f, --filetypes', '要扫描的文件类型', 'js,vue,html,jsx,ts')
|
||||
.argument('[location]', '工程所在目录',"./")
|
||||
.description(t('扫描并提取所有待翻译的字符串到<languages/translates>文件夹中'))
|
||||
.option('-d, --debug', t('输出调试信息'))
|
||||
.option('-lngs, --languages', t('支持的语言'), 'cn,en')
|
||||
.option('-default, --defaultLanguage', t('默认语言'), 'cn')
|
||||
.option('-active, --activeLanguage', t('激活语言'), 'cn')
|
||||
.option('-ns, --namespaces', t('翻译名称空间'))
|
||||
.option('-e, --exclude <folders>', t('排除要扫描的文件夹,多个用逗号分隔'))
|
||||
.option('-u, --updateMode', t('本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并'), 'sync')
|
||||
.option('-f, --filetypes', t('要扫描的文件类型'), 'js,vue,html,jsx,ts')
|
||||
.argument('[location]', t('工程项目所在目录'),"./")
|
||||
.action(async (location,options) => {
|
||||
if(!location) {
|
||||
location = process.cwd()
|
||||
@ -56,10 +58,10 @@ program
|
||||
options.languages = options.languages.split(",").map(l=>({name:l,title:l}))
|
||||
}
|
||||
//options = Object.assign({},options)
|
||||
logger.log("工程目录:{}",location)
|
||||
logger.log(t("工程目录:{}"),location)
|
||||
const langSettingsFile = path.join(location,"languages","settings.js")
|
||||
if(fs.existsSync(langSettingsFile)){
|
||||
logger.log("语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本","./languages/settings.js")
|
||||
logger.log(t("语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本","./languages/settings.js"))
|
||||
let lngOptions = (await importModule("file:///"+langSettingsFile)).default
|
||||
options.languages = lngOptions.languages
|
||||
options.defaultLanguage = lngOptions.defaultLanguage
|
||||
@ -67,9 +69,9 @@ program
|
||||
options.namespaces = lngOptions.namespaces
|
||||
}
|
||||
//
|
||||
if(options.debug){
|
||||
logger.format(options,{compact:true})
|
||||
}
|
||||
// if(options.debug){
|
||||
// logger.format(options,{compact:true})
|
||||
// }
|
||||
const extractor = require('./extract.command');
|
||||
extractor(location,options)
|
||||
});
|
||||
@ -77,13 +79,15 @@ program
|
||||
|
||||
program
|
||||
.command('compile')
|
||||
.description('编译语言包文件<languages>文件夹中')
|
||||
.option('-d, --debug', '输出调试信息')
|
||||
.option('-m, --moduleType [types]', '输出模块类型,取值auto,esm,cjs', 'auto')
|
||||
.description(t('编译指定项目的语言包'))
|
||||
.option('-d, --debug', t('输出调试信息'))
|
||||
.option('-m, --moduleType [types]', t('输出模块类型,取值auto,esm,cjs'), 'auto')
|
||||
.argument('[location]', t('工程项目所在目录'),"./")
|
||||
.hook("preAction",async (location,options) => {
|
||||
console.log("process.env.language",process.env.language)
|
||||
await changeLanguage("en")
|
||||
.hook("preAction",async function(location){
|
||||
const lang= process.env.LANGUAGE
|
||||
if(lang){
|
||||
await scope.change(lang)
|
||||
}
|
||||
})
|
||||
.action(async (location,options) => {
|
||||
if(!location) {
|
||||
@ -93,12 +97,12 @@ program
|
||||
}
|
||||
const langFolder = path.join(location,"languages")
|
||||
if(!fs.existsSync(langFolder)){
|
||||
logger.error("语言包文件夹<{}>不存在",langFolder)
|
||||
logger.error(t("语言包文件夹<{}>不存在",langFolder))
|
||||
return
|
||||
}
|
||||
if(options.debug){
|
||||
logger.format(options,{compact:true})
|
||||
}
|
||||
// if(options.debug){
|
||||
// logger.format(options,{compact:true})
|
||||
// }
|
||||
compile = require("./compile.command")
|
||||
compile(langFolder,options)
|
||||
});
|
||||
|
@ -4,38 +4,40 @@
|
||||
*/
|
||||
|
||||
|
||||
const { findModuleType,createPackageJsonFile } = require("./utils")
|
||||
const { findModuleType,createPackageJsonFile,t } = 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}={}){
|
||||
module.exports = function(srcPath,{debug = true,languages=["cn","en"],defaultLanguage="cn",activeLanguage="cn",moduleType = "auto",reset=false}={}){
|
||||
// 语言文件夹名称
|
||||
const langPath = "languages"
|
||||
// 查找当前项目的语言包类型路径
|
||||
const lngPath = path.join(targetPath,langPath)
|
||||
const lngPath = path.join(srcPath,langPath)
|
||||
if(!fs.existsSync(lngPath)){
|
||||
fs.mkdirSync(lngPath)
|
||||
if(debug) logger.log(t("创建语言包文件夹: {}"),lngPath)
|
||||
}
|
||||
|
||||
moduleType = createPackageJsonFile(lngPath,moduleType)
|
||||
|
||||
if(moduleType==null) {
|
||||
if(debug){
|
||||
logger.log("找不到{}文件,{}只能在js项目工程中使用","package.json","voerkai18n")
|
||||
logger.log(t("找不到{}文件,{}只能在js项目工程中使用"),"package.json","voerkai18n")
|
||||
}else{
|
||||
throw new Error("找不到package.json文件,voerkai18n只能在js项目工程中使用")
|
||||
throw new Error(t("找不到package.json文件,voerkai18n只能在js项目工程中使用"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!fs.existsSync(lngPath)){
|
||||
fs.mkdirSync(lngPath)
|
||||
if(debug) logger.log("创建语言包文件夹: {}",lngPath)
|
||||
}
|
||||
|
||||
|
||||
// 创建settings.js文件
|
||||
const settingsFile = path.join(lngPath,"settings.js")
|
||||
if(fs.existsSync(settingsFile) && !reset){
|
||||
if(debug) logger.log("语言配置文件{}文件已存在,跳过创建。\n使用{}可以重新覆盖创建",settingsFile,"-r")
|
||||
if(debug) logger.log(t("语言配置文件{}文件已存在,跳过创建。\n使用{}可以重新覆盖创建"),settingsFile,"-r")
|
||||
return
|
||||
}
|
||||
const settings = {
|
||||
@ -52,11 +54,11 @@ module.exports = function(targetPath,{debug = true,languages=["cn","en"],default
|
||||
}
|
||||
|
||||
if(debug) {
|
||||
logger.log("生成语言配置文件:{}","./languages/settings.js")
|
||||
logger.log("拟支持的语言:{}",settings.languages.map(l=>l.name).join(","))
|
||||
logger.log("初始化成功,下一步:")
|
||||
logger.log(" - 编辑{}确定拟支持的语言种类等参数","languages/settings.js")
|
||||
logger.log(" - 运行<{}>扫描提取要翻译的文本","voerkai18n extract")
|
||||
logger.log(" - 运行<{}>编译语言包","voerkai18n compile")
|
||||
logger.log(t("生成语言配置文件:{}"),"./languages/settings.js")
|
||||
logger.log(t("拟支持的语言:{}"),settings.languages.map(l=>l.name).join(","))
|
||||
logger.log(t("初始化成功,下一步:"))
|
||||
logger.log(t(" - 编辑{}确定拟支持的语言种类等参数"),"languages/settings.js")
|
||||
logger.log(t(" - 运行<{}>扫描提取要翻译的文本"),"voerkai18n extract")
|
||||
logger.log(t(" - 运行<{}>编译语言包"),"voerkai18n compile")
|
||||
}
|
||||
}
|
@ -1,4 +1,46 @@
|
||||
module.exports = {
|
||||
"1": "支持的语言\t: {}",
|
||||
"2": "工程项目所在目录"
|
||||
"1": "支持的语言\\t: {}",
|
||||
"2": "默认语言\\t: {}",
|
||||
"3": "激活语言\\t: {}",
|
||||
"4": "名称空间\\t: {}",
|
||||
"5": "模块类型\\t: {}",
|
||||
"6": "编译结果输出至:{}",
|
||||
"7": "读取语言文件{}失败:{}",
|
||||
"8": " - 共合成{}条语言包文本",
|
||||
"9": " - 语言包文件: {}",
|
||||
"10": " - idMap文件: {}",
|
||||
"11": " - 格式化器:{}",
|
||||
"12": " - 更新格式化器:{}",
|
||||
"13": " - 访问入口文件: {}",
|
||||
"14": "加载多语言配置文件<{}>失败: {} ",
|
||||
"15": "目标文件夹<{}>不存在",
|
||||
"16": "扫描提取范围:",
|
||||
"17": "工程项目所在目录",
|
||||
"18": "初始化项目国际化配置",
|
||||
"19": "输出调试信息",
|
||||
"20": "重新生成当前项目的语言配置",
|
||||
"21": "生成的js模块类型,取值auto,esm,cjs",
|
||||
"22": "支持的语言列表",
|
||||
"23": "工程目录:{}",
|
||||
"24": "扫描并提取所有待翻译的字符串到<languages/translates>文件夹中",
|
||||
"25": "支持的语言",
|
||||
"26": "默认语言",
|
||||
"27": "激活语言",
|
||||
"28": "翻译名称空间",
|
||||
"29": "排除要扫描的文件夹,多个用逗号分隔",
|
||||
"30": "本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并",
|
||||
"31": "要扫描的文件类型",
|
||||
"32": "语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本",
|
||||
"33": "编译指定项目的语言包",
|
||||
"34": "输出模块类型,取值auto,esm,cjs",
|
||||
"35": "语言包文件夹<{}>不存在",
|
||||
"36": "找不到{}文件,{}只能在js项目工程中使用",
|
||||
"37": "找不到package.json文件,voerkai18n只能在js项目工程中使用",
|
||||
"38": "语言配置文件{}文件已存在,跳过创建。\\n使用{}可以重新覆盖创建",
|
||||
"39": "生成语言配置文件:{}",
|
||||
"40": "拟支持的语言:{}",
|
||||
"41": "初始化成功,下一步:",
|
||||
"42": " - 编辑{}确定拟支持的语言种类等参数",
|
||||
"43": " - 运行<{}>扫描提取要翻译的文本",
|
||||
"44": " - 运行<{}>编译语言包"
|
||||
}
|
@ -1,4 +1,46 @@
|
||||
module.exports = {
|
||||
"1": "Supported Languages\t: {}",
|
||||
"2": "Project Directory"
|
||||
"1": "Supported languages\\t: {}",
|
||||
"2": "Default language\\t: {}",
|
||||
"3": "Active language\\t\\t: {}",
|
||||
"4": "Namespaces\\t\\t: {}",
|
||||
"5": "Module type\\t\\t: {}",
|
||||
"6": "Compile to:{}",
|
||||
"7": "Error while read language of file<{}>:{}",
|
||||
"8": " - Total {} messages",
|
||||
"9": " - language messages: {}",
|
||||
"10": " - idMap file: {}",
|
||||
"11": " - Formatters:{}",
|
||||
"12": " - Update formatters:{}",
|
||||
"13": " - Entry of language: {}",
|
||||
"14": "Failed to load multilingual configuration file <{}>: {}",
|
||||
"15": "The destination folder < {} > does not exist",
|
||||
"16": "Scan for:",
|
||||
"17": "Project directory",
|
||||
"18": "Initialize project i18n configuration",
|
||||
"19": "Output debug information",
|
||||
"20": "Regenerate the language configuration of the current project",
|
||||
"21": "Generated JS module type, with values of auto, esm, cjs",
|
||||
"22": "Supported languages",
|
||||
"23": "Folder of project:{}",
|
||||
"24": "Scan and extract all strings to be translated into the <languages/translations> folder",
|
||||
"25": "Supported languages",
|
||||
"26": "Default language",
|
||||
"27": "Active language",
|
||||
"28": "Namespaces",
|
||||
"29": "Exclude folders to scan, multiple separated by commas",
|
||||
"30": " strategy of messages merge,with value of sync(default),overwrite,merge",
|
||||
"31": "Type of file to scan",
|
||||
"32": "The language configuration file <{}> already exists. It will be used preferentially to extract messages",
|
||||
"33": "Compiles the language messages for project",
|
||||
"34": "Output module type, values: auto, esm, cjs",
|
||||
"35": "The language messages folder <{}> does not exist",
|
||||
"36": "Cannot find {} file, {} can only be used in JS project",
|
||||
"37": "Cannot find <package.json> file, voerkai18n can only be used in JS project",
|
||||
"38": "Language configuration {} file already exists, skipping creation\\n use {} to overwrite the creation",
|
||||
"39": "Generate language configuration: {}",
|
||||
"40": "Languages to be supported:{}",
|
||||
"41": "Initialization succeeded, next step:",
|
||||
"42": " - Edit language parameters in {}",
|
||||
"43": " - Run <{}> scan to extract the messages to be translated",
|
||||
"44": " - Run <{}> compile language messages"
|
||||
}
|
@ -47,59 +47,83 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const formatters = {
|
||||
"*":{ }, // 在所有语言下生效的格式化器
|
||||
$types:{ } // 在所有语言下只作用于特定数据类型的格式化器
|
||||
module.exports = {
|
||||
// 在所有语言下生效的格式化器
|
||||
"*":{
|
||||
//[格式化名称]:(value)=>{...},
|
||||
//[格式化名称]:(value,arg)=>{...},
|
||||
},
|
||||
// 在所有语言下只作用于特定数据类型的格式化器
|
||||
$types:{
|
||||
|
||||
},
|
||||
cn:{
|
||||
$types:{
|
||||
"*":{
|
||||
// 所有类型的默认格式化器
|
||||
// "*":{
|
||||
|
||||
},
|
||||
Date:{
|
||||
// },
|
||||
// Date:{
|
||||
|
||||
},
|
||||
Number:{
|
||||
// },
|
||||
// Number:{
|
||||
|
||||
},
|
||||
String:{
|
||||
// },
|
||||
// String:{
|
||||
|
||||
},
|
||||
Array:{
|
||||
// },
|
||||
// Array:{
|
||||
|
||||
},
|
||||
Object:{
|
||||
// },
|
||||
// Object:{
|
||||
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
en:{
|
||||
$types:{
|
||||
"*":{
|
||||
// 所有类型的默认格式化器
|
||||
// "*":{
|
||||
|
||||
// },
|
||||
// Date:{
|
||||
|
||||
// },
|
||||
// Number:{
|
||||
|
||||
// },
|
||||
// String:{
|
||||
|
||||
// },
|
||||
// Array:{
|
||||
|
||||
// },
|
||||
// Object:{
|
||||
|
||||
// }
|
||||
}
|
||||
},
|
||||
Date:{
|
||||
jp:{
|
||||
$types:{
|
||||
// 所有类型的默认格式化器
|
||||
// "*":{
|
||||
|
||||
},
|
||||
Number:{
|
||||
// },
|
||||
// Date:{
|
||||
|
||||
},
|
||||
String:{
|
||||
// },
|
||||
// Number:{
|
||||
|
||||
},
|
||||
Array:{
|
||||
// },
|
||||
// String:{
|
||||
|
||||
},
|
||||
Object:{
|
||||
// },
|
||||
// Array:{
|
||||
|
||||
// },
|
||||
// Object:{
|
||||
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.module.module.module.module.module.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.s.s.s.s.s.s = formatters
|
||||
|
||||
|
||||
|
@ -1,4 +1,46 @@
|
||||
module.exports = {
|
||||
"支持的语言\t: {}": 1,
|
||||
"工程项目所在目录": 2
|
||||
"支持的语言\\t: {}": 1,
|
||||
"默认语言\\t: {}": 2,
|
||||
"激活语言\\t: {}": 3,
|
||||
"名称空间\\t: {}": 4,
|
||||
"模块类型\\t: {}": 5,
|
||||
"编译结果输出至:{}": 6,
|
||||
"读取语言文件{}失败:{}": 7,
|
||||
" - 共合成{}条语言包文本": 8,
|
||||
" - 语言包文件: {}": 9,
|
||||
" - idMap文件: {}": 10,
|
||||
" - 格式化器:{}": 11,
|
||||
" - 更新格式化器:{}": 12,
|
||||
" - 访问入口文件: {}": 13,
|
||||
"加载多语言配置文件<{}>失败: {} ": 14,
|
||||
"目标文件夹<{}>不存在": 15,
|
||||
"扫描提取范围:": 16,
|
||||
"工程项目所在目录": 17,
|
||||
"初始化项目国际化配置": 18,
|
||||
"输出调试信息": 19,
|
||||
"重新生成当前项目的语言配置": 20,
|
||||
"生成的js模块类型,取值auto,esm,cjs": 21,
|
||||
"支持的语言列表": 22,
|
||||
"工程目录:{}": 23,
|
||||
"扫描并提取所有待翻译的字符串到<languages/translates>文件夹中": 24,
|
||||
"支持的语言": 25,
|
||||
"默认语言": 26,
|
||||
"激活语言": 27,
|
||||
"翻译名称空间": 28,
|
||||
"排除要扫描的文件夹,多个用逗号分隔": 29,
|
||||
"本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并": 30,
|
||||
"要扫描的文件类型": 31,
|
||||
"语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本": 32,
|
||||
"编译指定项目的语言包": 33,
|
||||
"输出模块类型,取值auto,esm,cjs": 34,
|
||||
"语言包文件夹<{}>不存在": 35,
|
||||
"找不到{}文件,{}只能在js项目工程中使用": 36,
|
||||
"找不到package.json文件,voerkai18n只能在js项目工程中使用": 37,
|
||||
"语言配置文件{}文件已存在,跳过创建。\\n使用{}可以重新覆盖创建": 38,
|
||||
"生成语言配置文件:{}": 39,
|
||||
"拟支持的语言:{}": 40,
|
||||
"初始化成功,下一步:": 41,
|
||||
" - 编辑{}确定拟支持的语言种类等参数": 42,
|
||||
" - 运行<{}>扫描提取要翻译的文本": 43,
|
||||
" - 运行<{}>编译语言包": 44
|
||||
}
|
@ -1,57 +1,28 @@
|
||||
|
||||
const messageIds = require("./idMap")
|
||||
const { translate,I18nManager } = require("@voerkai18n/runtime")
|
||||
const defaultMessages = require("./cn.js")
|
||||
const { translate,I18nManager,i18nScope } = require("@voerkai18n/runtime")
|
||||
const scopeSettings = require("./settings.js")
|
||||
const formatters = require("./formatters.js")
|
||||
const defaultMessages = require("./cn.js")
|
||||
const activeMessages = defaultMessages
|
||||
|
||||
|
||||
// 自动创建全局VoerkaI18n实例
|
||||
if(!globalThis.VoerkaI18n){
|
||||
globalThis.VoerkaI18n = new I18nManager(scopeSettings)
|
||||
}
|
||||
|
||||
let scope = {
|
||||
defaultLanguage: "cn", // 默认语言名称
|
||||
// 语言作用域
|
||||
const scope = new i18nScope({
|
||||
...scopeSettings, // languages,defaultLanguage,activeLanguage,namespaces,formatters
|
||||
id: "@voerkai18n/tools", // 当前作用域的id,自动取当前工程的package.json的name
|
||||
default: defaultMessages, // 默认语言包
|
||||
messages : defaultMessages, // 当前语言包
|
||||
messages : activeMessages, // 当前语言包
|
||||
idMap:messageIds, // 消息id映射列表
|
||||
formatters:{}, // 当前作用域的格式化函数列表
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
global:{}, // 引用全局VoerkaI18n配置,注册后自动引用
|
||||
// 主要用来缓存格式化器的引用,当使用格式化器时可以直接引用,避免检索
|
||||
$cache:{
|
||||
activeLanguage:null,
|
||||
typedFormatters:{},
|
||||
formatters:{},
|
||||
formatters, // 当前作用域的格式化函数列表
|
||||
loaders:{
|
||||
"en" : ()=>import("./en.js")
|
||||
}
|
||||
}
|
||||
|
||||
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.scope = scope
|
||||
module.exports.i18nManager = VoerkaI18n
|
||||
|
||||
|
@ -1,21 +1,270 @@
|
||||
{
|
||||
"工程项目所在目录" : {
|
||||
"en" : "Project Directory",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"支持的语言\\t: {}": {
|
||||
"en" : "支持的语言\\t: {}",
|
||||
"en": "Supported languages\\t: {}",
|
||||
"$file": [
|
||||
"compile.command.js",
|
||||
"extract.plugin.js"
|
||||
]
|
||||
},
|
||||
"保存{}\\n" : {
|
||||
"en" : "保存{}\\n",
|
||||
"默认语言\\t: {}": {
|
||||
"en": "Default language\\t: {}",
|
||||
"$file": [
|
||||
"stringify.js"
|
||||
"compile.command.js",
|
||||
"extract.plugin.js"
|
||||
]
|
||||
},
|
||||
"激活语言\\t: {}": {
|
||||
"en": "Active language\\t\\t: {}",
|
||||
"$file": [
|
||||
"compile.command.js",
|
||||
"extract.plugin.js"
|
||||
]
|
||||
},
|
||||
"名称空间\\t: {}": {
|
||||
"en": "Namespaces\\t\\t: {}",
|
||||
"$file": [
|
||||
"compile.command.js",
|
||||
"extract.plugin.js"
|
||||
]
|
||||
},
|
||||
"模块类型\\t: {}": {
|
||||
"en": "Module type\\t\\t: {}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
"编译结果输出至:{}": {
|
||||
"en": "Compile to:{}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
"读取语言文件{}失败:{}": {
|
||||
"en": "Error while read language of file<{}>:{}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - 共合成{}条语言包文本": {
|
||||
"en": " - Total {} messages",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - 语言包文件: {}": {
|
||||
"en": " - language messages: {}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - idMap文件: {}": {
|
||||
"en": " - idMap file: {}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - 格式化器:{}": {
|
||||
"en": " - Formatters:{}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - 更新格式化器:{}": {
|
||||
"en": " - Update formatters:{}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
" - 访问入口文件: {}": {
|
||||
"en": " - Entry of language: {}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
"加载多语言配置文件<{}>失败: {} ": {
|
||||
"en": "Failed to load multilingual configuration file <{}>: {}",
|
||||
"$file": [
|
||||
"compile.command.js"
|
||||
]
|
||||
},
|
||||
"目标文件夹<{}>不存在": {
|
||||
"en": "The destination folder < {} > does not exist",
|
||||
"$file": [
|
||||
"extract.command.js"
|
||||
]
|
||||
},
|
||||
"扫描提取范围:": {
|
||||
"en": "Scan for:",
|
||||
"$file": [
|
||||
"extract.command.js"
|
||||
]
|
||||
},
|
||||
"工程项目所在目录": {
|
||||
"en": "Project directory",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"初始化项目国际化配置": {
|
||||
"en": "Initialize project i18n configuration",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"输出调试信息": {
|
||||
"en": "Output debug information",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"重新生成当前项目的语言配置": {
|
||||
"en": "Regenerate the language configuration of the current project",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"生成的js模块类型,取值auto,esm,cjs": {
|
||||
"en": "Generated JS module type, with values of auto, esm, cjs",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"支持的语言列表": {
|
||||
"en": "Supported languages",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"工程目录:{}": {
|
||||
"en": "Folder of project:{}",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"扫描并提取所有待翻译的字符串到<languages/translates>文件夹中": {
|
||||
"en": "Scan and extract all strings to be translated into the <languages/translations> folder",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"支持的语言": {
|
||||
"en": "Supported languages",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"默认语言": {
|
||||
"en": "Default language",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"激活语言": {
|
||||
"en": "Active language",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"翻译名称空间": {
|
||||
"en": "Namespaces",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"排除要扫描的文件夹,多个用逗号分隔": {
|
||||
"en": "Exclude folders to scan, multiple separated by commas",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"本次提取内容与已存在内容的数据合并策略,默认取值sync=同步,overwrite=覆盖,merge=合并": {
|
||||
"en": " strategy of messages merge,with value of sync(default),overwrite,merge",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"要扫描的文件类型": {
|
||||
"en": "Type of file to scan",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"语言配置文件<{}>已存在,将优先使用此配置文件中参数来提取文本": {
|
||||
"en": "The language configuration file <{}> already exists. It will be used preferentially to extract messages",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"编译指定项目的语言包": {
|
||||
"en": "Compiles the language messages for project",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"输出模块类型,取值auto,esm,cjs": {
|
||||
"en": "Output module type, values: auto, esm, cjs",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"语言包文件夹<{}>不存在": {
|
||||
"en": "The language messages folder <{}> does not exist",
|
||||
"$file": [
|
||||
"index.js"
|
||||
]
|
||||
},
|
||||
"找不到{}文件,{}只能在js项目工程中使用": {
|
||||
"en": "Cannot find {} file, {} can only be used in JS project",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
"找不到package.json文件,voerkai18n只能在js项目工程中使用": {
|
||||
"en": "Cannot find <package.json> file, voerkai18n can only be used in JS project",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
"语言配置文件{}文件已存在,跳过创建。\\n使用{}可以重新覆盖创建": {
|
||||
"en": "Language configuration {} file already exists, skipping creation\\n use {} to overwrite the creation",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
"生成语言配置文件:{}": {
|
||||
"en": "Generate language configuration: {}",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
"拟支持的语言:{}": {
|
||||
"en": "Languages to be supported:{}",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
"初始化成功,下一步:": {
|
||||
"en": "Initialization succeeded, next step:",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
" - 编辑{}确定拟支持的语言种类等参数": {
|
||||
"en": " - Edit language parameters in {}",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
" - 运行<{}>扫描提取要翻译的文本": {
|
||||
"en": " - Run <{}> scan to extract the messages to be translated",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
},
|
||||
" - 运行<{}>编译语言包": {
|
||||
"en": " - Run <{}> compile language messages",
|
||||
"$file": [
|
||||
"init.command.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"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": "node ./index.js compile -d",
|
||||
"compile:en": "cross-env LANGUAGE=en node ./index.js compile -d"
|
||||
},
|
||||
"author": "",
|
||||
"bin": {
|
||||
@ -20,12 +21,15 @@
|
||||
"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.8",
|
||||
"readjson": "^2.2.2",
|
||||
"through2": "^4.0.2",
|
||||
"vinyl": "^2.2.1"
|
||||
"vinyl": "^2.2.1",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ const { isPlainObject } = require("./utils")
|
||||
* @returns
|
||||
*/
|
||||
function escape(str){
|
||||
return str.replaceAll('\\','\\\\')
|
||||
return str.replaceAll(/\\\\(?![trnbvf'"]{1})/g,"\\\\")
|
||||
.replaceAll("\t","\\t")
|
||||
.replaceAll("\n","\\n")
|
||||
.replaceAll("\b","\\b")
|
||||
@ -49,8 +49,21 @@ const { isPlainObject } = require("./utils")
|
||||
.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("\\\\",'\\')
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取字符串的长度,中文算两个字符
|
||||
* @param {*} s
|
||||
@ -67,7 +80,7 @@ function getStringWidth(s){
|
||||
return realLength;
|
||||
}
|
||||
|
||||
function objectToString(obj,{indent=4,alignKey=true}={}){
|
||||
function objectStringify(obj,{indent=4,alignKey=true}={}){
|
||||
function nodeToString(node,level,last=false){
|
||||
let results = [],beginChar = "",endChar = ""
|
||||
level++
|
||||
@ -136,6 +149,10 @@ function objectToString(obj,{indent=4,alignKey=true}={}){
|
||||
}
|
||||
return nodeToString(obj,0,true)
|
||||
}
|
||||
module.exports = objectToString
|
||||
|
||||
module.exports = {
|
||||
objectStringify,
|
||||
escape,
|
||||
escape2,
|
||||
unescape
|
||||
}
|
||||
|
@ -1,55 +1,42 @@
|
||||
{{if moduleType === "esm"}}
|
||||
import messageIds from "./idMap.js"
|
||||
import { translate,I18nManager } from "@voerkai18n/runtime"
|
||||
import defaultMessages from "./{{defaultLanguage}}.js"
|
||||
import { translate,I18nManager,i18nScope } from "@voerkai18n/runtime"
|
||||
import scopeSettings from "./settings.js"
|
||||
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 } = require("@voerkai18n/runtime")
|
||||
const defaultMessages = require("./{{defaultLanguage}}.js")
|
||||
const { translate,I18nManager,i18nScope } = require("@voerkai18n/runtime")
|
||||
const scopeSettings = require("./settings.js")
|
||||
const formatters = require("./formatters.js")
|
||||
const defaultMessages = require("./{{defaultLanguage}}.js")
|
||||
{{if defaultLanguage === activeLanguage}}const activeMessages = defaultMessages
|
||||
{{else}}const activeMessages = require("./{{activeLanguage}}.js"){{/if}}
|
||||
{{/if}}
|
||||
|
||||
// 自动创建全局VoerkaI18n实例
|
||||
if(!globalThis.VoerkaI18n){
|
||||
globalThis.VoerkaI18n = new I18nManager(scopeSettings)
|
||||
}
|
||||
|
||||
let scope = {
|
||||
defaultLanguage: "{{defaultLanguage}}", // 默认语言名称
|
||||
// 语言作用域
|
||||
const scope = new i18nScope({
|
||||
...scopeSettings, // languages,defaultLanguage,activeLanguage,namespaces,formatters
|
||||
id: "{{scopeId}}", // 当前作用域的id,自动取当前工程的package.json的name
|
||||
default: defaultMessages, // 默认语言包
|
||||
messages : defaultMessages, // 当前语言包
|
||||
messages : activeMessages, // 当前语言包
|
||||
idMap:messageIds, // 消息id映射列表
|
||||
formatters:{}, // 当前作用域的格式化函数列表
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
global:{}, // 引用全局VoerkaI18n配置,注册后自动引用
|
||||
// 主要用来缓存格式化器的引用,当使用格式化器时可以直接引用,避免检索
|
||||
$cache:{
|
||||
activeLanguage:null,
|
||||
typedFormatters:{},
|
||||
formatters:{},
|
||||
formatters, // 当前作用域的格式化函数列表
|
||||
loaders:{ {{each languages}}{{if $value.name !== defaultLanguage}}
|
||||
{{if $value.name == activeLanguage}}"{{$value.name}}" : ()=>activeMessages{{else}}"{{$value.name}}" : ()=>import("./{{$value.name}}.js"){{/if}}{{if $index !== languages.length - 1}},{{/if}}{{/if}}{{/each}}
|
||||
}
|
||||
}
|
||||
|
||||
let supportedlanguages = {}
|
||||
|
||||
{{each languages}}{{if $value.name !== defaultLanguage}}
|
||||
scope.loaders["{{$value.name}}"] = ()=>import("./{{$value.name}}.js")
|
||||
{{/if}}{{/each}}
|
||||
|
||||
})
|
||||
// 翻译函数
|
||||
const t = translate.bind(scope)
|
||||
const languages = {{@ JSON.stringify(languages,null,4) }}
|
||||
// 注册当前作用域到全局VoerkaI18n实例
|
||||
VoerkaI18n.register(scope)
|
||||
|
||||
{{if moduleType === "esm"}}
|
||||
export { t, languages,scope,i18nManager:VoerkaI18n, changeLanguage:VoerkaI18n.change.bind(VoerkaI18n),addLanguageListener:VoerkaI18n.on.bind(VoerkaI18n),removeLanguageListener:VoerkaI18n.off.bind(VoerkaI18n) }
|
||||
export {
|
||||
t,
|
||||
scope,
|
||||
i18nManager: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.scope = scope
|
||||
module.exports.i18nManager = VoerkaI18n
|
||||
{{/if}}
|
||||
|
@ -47,38 +47,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const formatters = {
|
||||
"*":{ }, // 在所有语言下生效的格式化器
|
||||
$types:{ } // 在所有语言下只作用于特定数据类型的格式化器
|
||||
{{each languages}}
|
||||
{{$value.name}}:{
|
||||
$types:{
|
||||
{{if moduleType === "esm"}}export default {{else}}module.exports = {{/if}}{
|
||||
// 在所有语言下生效的格式化器
|
||||
"*":{
|
||||
//[格式化名称]:(value)=>{...},
|
||||
//[格式化名称]:(value,arg)=>{...},
|
||||
},
|
||||
// 在所有语言下只作用于特定数据类型的格式化器
|
||||
$types:{
|
||||
|
||||
},
|
||||
Date:{
|
||||
{{each languages}} {{$value.name}}:{
|
||||
$types:{
|
||||
// 所有类型的默认格式化器
|
||||
// "*":{
|
||||
|
||||
},
|
||||
Number:{
|
||||
// },
|
||||
// Date:{
|
||||
|
||||
},
|
||||
String:{
|
||||
// },
|
||||
// Number:{
|
||||
|
||||
},
|
||||
Array:{
|
||||
// },
|
||||
// String:{
|
||||
|
||||
},
|
||||
Object:{
|
||||
// },
|
||||
// Array:{
|
||||
|
||||
// },
|
||||
// Object:{
|
||||
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
{{if moduleType === "esm"}}
|
||||
export default formatters
|
||||
{{else}}
|
||||
module.exports = formatters
|
||||
{{/if}}
|
||||
|
||||
}{{if $index !== languages.length - 1}},{{/if}}
|
||||
{{/each}}}
|
@ -28,6 +28,31 @@ async function importModule(url){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 读取指定文件夹的package.json文件,如果当前文件夹没有package.json文件,则向上查找
|
||||
* @param {*} folder
|
||||
* @param {*} exclueSelf =true 排除folder,从folder的父级开始查找
|
||||
* @returns
|
||||
*/
|
||||
function getCurrentPackageJson(folder,exclueSelf=true){
|
||||
try{
|
||||
let pkgPath =exclueSelf ?
|
||||
path.join(folder, "..", "package.json")
|
||||
: path.join(folder, "package.json")
|
||||
if(fs.existsSync(pkgPath)){
|
||||
let pkg = readJson.sync(pkgPath)
|
||||
return pkg
|
||||
}
|
||||
let parent = path.dirname(folder)
|
||||
if(parent===folder) return null
|
||||
return getCurrentPackageJson(parent,false)
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function createPackageJsonFile(targetPath,moduleType="auto"){
|
||||
if(moduleType==="auto"){
|
||||
moduleType = findModuleType(targetPath)
|
||||
@ -111,12 +136,39 @@ function createJsModuleFile(filename,defaultExports={},namedExports={},moduleTyp
|
||||
}
|
||||
}
|
||||
|
||||
function escape(str){
|
||||
return str
|
||||
.replaceAll("\\t","\t")
|
||||
.replaceAll("\\n","\n")
|
||||
.replaceAll("\\b","\b")
|
||||
.replaceAll("\\r","\r")
|
||||
.replaceAll("\\f","\f")
|
||||
.replaceAll("\\'","\'")
|
||||
.replaceAll('\\"','\"')
|
||||
.replaceAll('\\v','\v')
|
||||
.replaceAll("\\\\",'\\')
|
||||
}
|
||||
|
||||
// 翻译函数
|
||||
// @voerkai18n/tools工程本身使用了voerkai18n,即@voerkai18n/tools的extract和compile依赖于其自己生成的languages运行时
|
||||
// 这样产生了鸡蛋问题,因此在extract与compile调试阶段如果t函数无法使用(即编译的languages无法正常使用),则需要提供t函数
|
||||
// 此函数的目的是提供一种容错方式
|
||||
let t
|
||||
try{
|
||||
t = require("./languages").t
|
||||
}catch(e){
|
||||
console.warn(e.stack)
|
||||
t = v=>v
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
importModule,
|
||||
findModuleType,
|
||||
createPackageJsonFile,
|
||||
isPlainObject
|
||||
isPlainObject,
|
||||
getCurrentPackageJson,
|
||||
escape,
|
||||
t
|
||||
}
|
||||
|
||||
|
||||
|
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@ -48,7 +48,7 @@ importers:
|
||||
packages/demo/apps/app:
|
||||
specifiers:
|
||||
'@voerkai18n/tools': workspace:^1.0.0
|
||||
dependencies:
|
||||
devDependencies:
|
||||
'@voerkai18n/tools': link:../../../tools
|
||||
|
||||
packages/demo/apps/app/languages:
|
||||
@ -91,8 +91,8 @@ importers:
|
||||
'@voerkai18n/runtime': workspace:^1.0.0
|
||||
art-template: ^4.13.2
|
||||
commander: ^9.0.0
|
||||
cross-env: ^7.0.3
|
||||
deepmerge: ^4.2.2
|
||||
dotenv: ^16.0.0
|
||||
glob: ^7.2.0
|
||||
gulp: ^4.0.2
|
||||
logsets: ^1.0.8
|
||||
@ -106,13 +106,14 @@ 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.8
|
||||
readjson: 2.2.2
|
||||
through2: 4.0.2
|
||||
vinyl: 2.2.1
|
||||
devDependencies:
|
||||
cross-env: 7.0.3
|
||||
|
||||
packages/vue:
|
||||
specifiers:
|
||||
@ -2426,6 +2427,14 @@ packages:
|
||||
/core-util-is/1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
/cross-env/7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
dev: true
|
||||
|
||||
/cross-spawn/7.0.3:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
@ -2569,11 +2578,6 @@ 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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user