add voerkai18n-loader for webpack
This commit is contained in:
parent
af07ef7d93
commit
22123a8e36
@ -94,6 +94,15 @@ module.exports = function(srcPath,{moduleType='cjs',isTypeScript,debug = true,la
|
||||
tasks.error(e.message)
|
||||
}
|
||||
|
||||
try{
|
||||
tasks.add("生成IdMap文件")
|
||||
const entryContent = isTypeScript ? "export default {}" : (moduleType=='cjs' ? "module.exports={}" :"export default {}")
|
||||
fs.writeFileSync(path.join(lngPath,`idMap.${isTypeScript ? 'ts' : 'js'}`),entryContent)
|
||||
tasks.complete()
|
||||
}catch(e){
|
||||
tasks.error(e.message)
|
||||
}
|
||||
|
||||
try{
|
||||
tasks.add(t("安装运行时依赖@voerkai18n/runtime"))
|
||||
installPackage('@voerkai18n/runtime')
|
||||
|
@ -16,11 +16,21 @@ const { translate,i18nScope } = require("@voerkai18n/runtime")
|
||||
const scope = new VoerkaI18nScope({
|
||||
id : "{{scopeId}}", // 当前作用域的id,自动取当前工程的package.json的name
|
||||
debug : false, // 是否在控制台输出高度信息
|
||||
default : {}, // 默认语言包
|
||||
messages : {}, // 当前语言包
|
||||
idMap : {}, // 消息id映射列表
|
||||
formatters, // 扩展自定义格式化器
|
||||
loaders : {} // 语言包加载器
|
||||
default : {}, // 默认语言包
|
||||
messages : {}, // 当前语言包
|
||||
idMap : {}, // 消息id映射列表
|
||||
formatters : {}, // 扩展自定义格式化器
|
||||
loaders : {}, // 语言包加载器
|
||||
languages: [
|
||||
{
|
||||
name: "zh",
|
||||
title: "中文"
|
||||
},
|
||||
{
|
||||
name: "en",
|
||||
title: "英文"
|
||||
}
|
||||
]
|
||||
})
|
||||
// 翻译函数
|
||||
const scopedTtranslate = translate.bind(scope)
|
||||
|
@ -12,11 +12,21 @@ const { translate,VoerkaI18nScope } = runtime
|
||||
const scope = new VoerkaI18nScope({
|
||||
id : "{{scopeId}}", // 当前作用域的id,自动取当前工程的package.json的name
|
||||
debug : false, // 是否在控制台输出高度信息
|
||||
default : {}, // 默认语言包
|
||||
messages : {}, // 当前语言包
|
||||
idMap : {}, // 消息id映射列表
|
||||
formatters : {}, // 扩展自定义格式化器
|
||||
loaders : {} // 语言包加载器
|
||||
default : {}, // 默认语言包
|
||||
messages : {}, // 当前语言包
|
||||
idMap : {}, // 消息id映射列表
|
||||
formatters : {}, // 扩展自定义格式化器
|
||||
loaders : {}, // 语言包加载器
|
||||
languages: [
|
||||
{
|
||||
name: "zh",
|
||||
title: "中文"
|
||||
},
|
||||
{
|
||||
name: "en",
|
||||
title: "英文"
|
||||
}
|
||||
]
|
||||
})
|
||||
// 翻译函数
|
||||
const scopedTtranslate = translate.bind(scope)
|
||||
|
@ -60,6 +60,19 @@ module.exports = class VoerkaI18nScope {
|
||||
* - 将en配置为默认回退语言
|
||||
*/
|
||||
_initiLanguages(){
|
||||
if(!isPlainObject(this._languages)){
|
||||
console.warn("[VoerkaI18n] 无效的语言配置")
|
||||
this._languages = [
|
||||
{
|
||||
name: "zh",
|
||||
title: "中文"
|
||||
},
|
||||
{
|
||||
name: "en",
|
||||
title: "英文"
|
||||
}
|
||||
]
|
||||
}
|
||||
Object.entries(this._languages).forEach(([name,language])=>{
|
||||
if(!language.fallback) language.fallback = "en"
|
||||
})
|
||||
|
@ -476,6 +476,123 @@ function installPackage(packageName){
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 读取当前工程下languages/idMap.(js|ts)文件
|
||||
*
|
||||
* @param {*} location 项目根文件夹或者当前项目下的任意一个文件夹
|
||||
* @returns
|
||||
*/
|
||||
function readIdMapFile(location="./"){
|
||||
let searchIdMapFiles = []
|
||||
if(!path.isAbsolute(location)){
|
||||
location = path.join(process.cwd(),location)
|
||||
}
|
||||
searchIdMapFiles.push(path.join(location,"src","languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(location,"languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(location,"idMap.js"))
|
||||
|
||||
searchIdMapFiles.push(path.join(location,"src","languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(location,"languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(location,"idMap.ts"))
|
||||
|
||||
let projectRoot = getProjectRootFolder(location)
|
||||
searchIdMapFiles.push(path.join(projectRoot,"src","languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"idMap.js"))
|
||||
|
||||
searchIdMapFiles.push(path.join(projectRoot,"src","languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"idMap.ts"))
|
||||
|
||||
let idMapFile
|
||||
for( idMapFile of searchIdMapFiles){
|
||||
// 如果不存在idMap文件,则尝试从location/languages/中导入
|
||||
if(fs.existsSync(idMapFile)){
|
||||
try{
|
||||
// 由于idMap.js可能是esm或cjs,并且babel插件不支持异步
|
||||
// 当require(idMap.js)失败时,对esm模块尝试采用直接读取的方式
|
||||
return require(idMapFile)
|
||||
}catch(e){
|
||||
// 出错原因可能是因为无效require esm模块,由于idMap.js文件格式相对简单,因此尝试直接读取解析
|
||||
try{
|
||||
let idMapContent = fs.readFileSync(idMapFile).toString()
|
||||
idMapContent = idMapContent.trim().replace(/^\s*export\s*default\s/g,"")
|
||||
return JSON.parse(idMapContent)
|
||||
}catch{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`${idMapFile}文件不存在,无法对翻译文本进行转换。`)
|
||||
return {}
|
||||
}
|
||||
|
||||
|
||||
//const TranslateRegex = /\bt\(\s*("|'){1}(?:((?<namespace>\w+)::))?(?<text>[^\1]*?)(?=(\1\s*\))|(\1\s*\,))/gm
|
||||
// 匹配t('xxxx')的正则表达式
|
||||
const TranslateRegex =/(?<=\bt\(\s*("|'){1})(?<text>[^\1]*?)(?=(\1\s*\))|(\1\s*\,))/gm
|
||||
|
||||
/**
|
||||
* 将code中的t("xxxx")使用idMap进行映射为t("1"),t("2")的形式
|
||||
*
|
||||
* @param {*} code
|
||||
* @param {*} idmap
|
||||
*/
|
||||
function replaceTranslateText(code, idmap) {
|
||||
return code.replaceAll(TranslateRegex, (message) => {
|
||||
if(message in idmap) {
|
||||
return idmap[message]
|
||||
}else{
|
||||
let result
|
||||
// 为什么要decodeURIComponent/unescape? 一些vite插件会将中文编码转义导致无法进行替换,所以要解码一下
|
||||
try{
|
||||
result = decodeURIComponent(message.replaceAll("\\u","%u"))
|
||||
return result in idmap ? idmap[result] : message
|
||||
}catch{
|
||||
return message
|
||||
}
|
||||
}
|
||||
})
|
||||
// decodeURI 或 decodeURIComponent 对特殊字符进行转义序列编码和解码。
|
||||
}
|
||||
|
||||
// 匹配 import {t } from 的正则表达式
|
||||
const importTRegex = /^[^\w\r\n\s]*import\s*\{(.*)\bt\b(.*)\}\s*from/gm
|
||||
|
||||
|
||||
/**
|
||||
* 判定代码中是否导入了Translate函数
|
||||
* @param {*} code
|
||||
* @returns
|
||||
*/
|
||||
function hasImportTranslateFunction(code){
|
||||
return importTRegex.test(code)
|
||||
}
|
||||
|
||||
|
||||
function importTranslateFunction(code,sourceFile,langPath){
|
||||
let importSource = path.relative(path.dirname(sourceFile),langPath)
|
||||
if(!importSource.startsWith(".")){
|
||||
importSource = "./" + importSource
|
||||
}
|
||||
importSource= importSource.replaceAll("\\","/")
|
||||
const extName = path.extname(sourceFile)
|
||||
// Vue文件
|
||||
if(extName==".vue"){
|
||||
// 优先在<script setup></script>中导入
|
||||
const setupScriptRegex = /(^\s*\<script.*\s*setup\s*.*\>)/gmi
|
||||
if(setupScriptRegex.test(code)){
|
||||
code = code.replace(setupScriptRegex,`$1\nimport { t } from '${importSource}';`)
|
||||
}else{// 如果没有<script setup>则在<script></script>中导入
|
||||
code = code.replace(/(^\s*\<script.*\>)/gmi,`$1\nimport { t } from '${importSource}';`)
|
||||
}
|
||||
}else if(['.jsx','.js','.ts','.tsx'].includes(extName)){
|
||||
// 普通js/ts文件直接添加到最前面
|
||||
code = code = `import { t } from '${importSource}';\n${code}`
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
fileMatcher, // 文件名称匹配器
|
||||
getProjectRootFolder, // 查找获取项目根目录
|
||||
@ -492,8 +609,12 @@ module.exports = {
|
||||
deepMerge, // 深度合并对象
|
||||
getDataTypeName, // 获取指定变量类型名称
|
||||
isGitRepo, // 判断当前工程是否是git工程
|
||||
fileIsExists,
|
||||
isTypeScriptProject,
|
||||
getPackageTool,
|
||||
installPackage
|
||||
fileIsExists, // 检查文件是否存在
|
||||
isTypeScriptProject, // 当前是否是TypeScript工程
|
||||
getPackageTool, // 获取当前工程使用的包工具,如pnpm,yarn,npm
|
||||
installPackage, // 安装指定的包
|
||||
readIdMapFile, // 读取当前工程下的idMap文件
|
||||
replaceTranslateText,
|
||||
hasImportTranslateFunction,
|
||||
importTranslateFunction // 在代码中导入t函数
|
||||
}
|
2
packages/vite/index.d.ts
vendored
2
packages/vite/index.d.ts
vendored
@ -1,7 +1,7 @@
|
||||
import type { PluginOption } from "vite"
|
||||
export interface Voerkai18nPluginOptions{
|
||||
location?: string // 指定当前工程目录
|
||||
autoImport?: boolean // 是否自动导入t函数
|
||||
autoImport?: boolean | string[] // 是否自动导入t函数,或者[".js"]代表只对js文件进行自动导入,允许只对约定的扩展名进行自动导入
|
||||
debug?:boolean // 是否输出调试信息,当=true时,在控制台输出转换匹配的文件清单
|
||||
patterns?:(string | RegExp)[]
|
||||
}
|
||||
|
@ -1,89 +1,16 @@
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const { fileMatcher,getProjectRootFolder,getProjectLanguageFolder } = require("@voerkai18n/utils")
|
||||
|
||||
//const TranslateRegex = /\bt\(\s*("|'){1}(?:((?<namespace>\w+)::))?(?<text>[^\1]*?)(?=(\1\s*\))|(\1\s*\,))/gm
|
||||
|
||||
const TranslateRegex =/(?<=\bt\(\s*("|'){1})(?<text>[^\1]*?)(?=(\1\s*\))|(\1\s*\,))/gm
|
||||
|
||||
// 匹配正则表达式
|
||||
const importTRegex = /^[^\w\r\n\s]*import\s*\{(.*)\bt\b(.*)\}\s*from/gm
|
||||
const {
|
||||
fileMatcher,
|
||||
getProjectRootFolder,
|
||||
getProjectLanguageFolder,
|
||||
readIdMapFile,
|
||||
hasImportTranslateFunction,
|
||||
replaceTranslateText,
|
||||
importTranslateFunction
|
||||
} = require("@voerkai18n/utils")
|
||||
|
||||
|
||||
/**
|
||||
* 读取idMap.js文件
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {*} options
|
||||
* @returns
|
||||
*/
|
||||
function readIdMapFile(options){
|
||||
let { location } = options
|
||||
let searchIdMapFiles = []
|
||||
if(!path.isAbsolute(location)){
|
||||
location = path.join(process.cwd(),location)
|
||||
}
|
||||
searchIdMapFiles.push(path.join(location,"src","languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(location,"languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(location,"idMap.js"))
|
||||
|
||||
searchIdMapFiles.push(path.join(location,"src","languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(location,"languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(location,"idMap.ts"))
|
||||
|
||||
let projectRoot = getProjectRootFolder(location)
|
||||
searchIdMapFiles.push(path.join(projectRoot,"src","languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"languages/idMap.js"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"idMap.js"))
|
||||
|
||||
searchIdMapFiles.push(path.join(projectRoot,"src","languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"languages/idMap.ts"))
|
||||
searchIdMapFiles.push(path.join(projectRoot,"idMap.ts"))
|
||||
|
||||
let idMapFile
|
||||
for( idMapFile of searchIdMapFiles){
|
||||
// 如果不存在idMap文件,则尝试从location/languages/中导入
|
||||
if(fs.existsSync(idMapFile)){
|
||||
try{
|
||||
// 由于idMap.js可能是esm或cjs,并且babel插件不支持异步
|
||||
// 当require(idMap.js)失败时,对esm模块尝试采用直接读取的方式
|
||||
return require(idMapFile)
|
||||
}catch(e){
|
||||
// 出错原因可能是因为无效require esm模块,由于idMap.js文件格式相对简单,因此尝试直接读取解析
|
||||
try{
|
||||
let idMapContent = fs.readFileSync(idMapFile).toString()
|
||||
idMapContent = idMapContent.trim().replace(/^\s*export\s*default\s/g,"")
|
||||
return JSON.parse(idMapContent)
|
||||
}catch{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
// 所有尝试完成后触发错误
|
||||
throw new Error(`${idMapFile}文件不存在,无法对翻译文本进行转换。\n原因可能是babel-plugin-voerkai18n插件的location参数未指向有效的语言包所在的目录。`)
|
||||
|
||||
}
|
||||
|
||||
|
||||
function replaceCode(code, idmap) {
|
||||
return code.replaceAll(TranslateRegex, (message) => {
|
||||
if(message in idmap) {
|
||||
return idmap[message]
|
||||
}else{
|
||||
const msg = unescape(message.replaceAll("\\u","%u"))
|
||||
return msg in idmap ? idmap[msg] : message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 判定代码中是否导入了Translate函数
|
||||
* @param {*} code
|
||||
* @returns
|
||||
*/
|
||||
function hasImportTranslateFunction(code){
|
||||
return importTRegex.test(code)
|
||||
}
|
||||
|
||||
/**
|
||||
options = {
|
||||
@ -95,7 +22,7 @@ function hasImportTranslateFunction(code){
|
||||
module.exports = function VoerkaI18nPlugin(opts={}) {
|
||||
let options = Object.assign({
|
||||
location: "./", // 指定当前工程目录
|
||||
autoImport: false, // 是否自动导入t函数
|
||||
autoImport: false, // 是否自动导入t函数
|
||||
debug:false, // 是否输出调试信息,当=true时,在控制台输出转换匹配的文件清单
|
||||
patterns:[
|
||||
"!\.(svg|css|json|scss|less|sass)$",
|
||||
@ -126,7 +53,7 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
|
||||
})
|
||||
let idMap
|
||||
try{
|
||||
idMap = readIdMapFile(options)
|
||||
idMap = readIdMapFile(options.location)
|
||||
}catch(e){
|
||||
console.warn("读取idMap.js文件失败,@voerkai18n/vite未启用")
|
||||
return
|
||||
@ -138,40 +65,23 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
|
||||
let [isMatched,pattern] = debug ? matcher.test(id) : [matcher.test(id),null]
|
||||
if(isMatched){
|
||||
if(debug){
|
||||
console.log(`File=${path.relative(projectRoot,id)}, pattern=[${pattern}], import from "${path.relative(path.dirname(id),languageFolder)}"`)
|
||||
console.log(`[VoerkaI18n] File=${path.relative(projectRoot,id)}, pattern=[${pattern}], import from "${path.relative(path.dirname(id),languageFolder)}"`)
|
||||
}
|
||||
try{
|
||||
// 判断是否使用了t函数
|
||||
if(TranslateRegex.test(src)){
|
||||
let code = replaceCode(src,idMap)
|
||||
let code = replaceTranslateText(src,idMap)
|
||||
// 如果没有导入t函数,则尝试自动导入
|
||||
if(autoImport && !hasImportTranslateFunction(code)){
|
||||
let importSource = path.relative(path.dirname(id),languageFolder)
|
||||
if(!importSource.startsWith(".")){
|
||||
importSource = "./" + importSource
|
||||
}
|
||||
importSource=importSource.replace("\\","/")
|
||||
const extName = path.extname(id)
|
||||
// 转换Vue文件
|
||||
if(extName==".vue"){
|
||||
// 优先在<script setup></script>中导入
|
||||
const setupScriptRegex = /(^\s*\<script.*\s*setup\s*.*\>)/gmi
|
||||
if(setupScriptRegex.test(code)){
|
||||
code = code.replace(setupScriptRegex,`$1\nimport { t } from '${importSource}';`)
|
||||
}else{// 如果没有<script setup>则在<script></script>中导入
|
||||
code = code.replace(/(^\s*\<script.*\>)/gmi,`$1\nimport { t } from '${importSource}';`)
|
||||
}
|
||||
}else if(['.js','.ts'].includes(extName)){// 普通js/ts文件需要添加到最前面
|
||||
code = code = `import { t } from '${importSource}';\n${code}`
|
||||
}
|
||||
}
|
||||
if(autoImport && !hasImportTranslateFunction(code)){
|
||||
code = importTranslateFunction(code,id,languageFolder)
|
||||
}
|
||||
return {
|
||||
code,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
console.warn(`vite-plugin-voerkai18n转换<${id}>文件出错:${e.message}`)
|
||||
console.warn(`[voerkai18n]转换<${id}>文件出错:${e.message}`)
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
@ -1,117 +0,0 @@
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
/**
|
||||
*
|
||||
* 匹配指定路径或文件名称
|
||||
*
|
||||
* const matcher = fileMatcher([
|
||||
* "<pattern>", // 匹配正则表达式字符串
|
||||
* "!<pattern>", // 以!开头代表否定匹配
|
||||
* /正则表达式/
|
||||
* ],{
|
||||
* basePath:"<指定一个基准目录,所有不是以此开头的均视为不匹配>",
|
||||
* defaultPatterns:["<默认排除的模式>","<默认排除的模式>","<默认排除的模式>"],
|
||||
* debug:<true/>false,是否输出调试信息,当=true时,.test()方法返回[<true/false>,pattern] *
|
||||
* })
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {*} patterns
|
||||
* @param {*} basePath 如果指定basePath,则所有不是以basePath开头的文件都排除
|
||||
* @param {*} defaultPatterns 默认的匹配模式
|
||||
* @param {*} debug 是否输出调试信息
|
||||
*/
|
||||
|
||||
function fileMatcher(patterns,{basePath,defaultPatterns=[],debug=true}={}) {
|
||||
if(basePath) {
|
||||
basePath = path.normalize(basePath)
|
||||
}
|
||||
//[[pattern,exclude],[pattern,false],[pattern,true]]
|
||||
let finalPatterns = []
|
||||
let inputPatterns = Array.isArray(patterns) ? patterns : (patterns ? [patterns] : [])
|
||||
|
||||
// 默认排除模式
|
||||
if(defaultPatterns.length===0){
|
||||
finalPatterns.push([/.*\/node_modules\/.*/,true])
|
||||
finalPatterns.push([/.*\/languages\/.*/,true]) // 默认排除语言文件
|
||||
finalPatterns.push([/\.babelrc/,true])
|
||||
finalPatterns.push([/babel\.config\.js/,true])
|
||||
finalPatterns.push([/package\.json$/,true])
|
||||
finalPatterns.push([/vite\.config\.js$/,true])
|
||||
finalPatterns.push([/^plugin-vue:.*/,true])
|
||||
}
|
||||
|
||||
inputPatterns.forEach(pattern=>{
|
||||
if(typeof pattern === "string"){
|
||||
pattern.replaceAll("**",".*")
|
||||
pattern.replaceAll("?","[^\/]?")
|
||||
pattern.replaceAll(/(?<!\.)\*/g,"[^\/]*")
|
||||
// 以!开头的表示排除
|
||||
if(pattern.startsWith("!")){
|
||||
finalPatterns.unshift([new RegExp(pattern.substring(1),"g"),true])
|
||||
}else{
|
||||
finalPatterns.push([new RegExp(pattern,"g"),false])
|
||||
}
|
||||
}else{
|
||||
finalPatterns.push([pattern,false])
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
patterns:finalPatterns,
|
||||
basePath,
|
||||
test: (filename) => {
|
||||
let isMatched = false
|
||||
let file = filename
|
||||
// 如果指定basePath,则文件名称必须是以basePath开头
|
||||
if(basePath){
|
||||
if(path.isAbsolute(file)){
|
||||
if(!path.normalize(file).startsWith(basePath)){
|
||||
return debug ? [false,`!^${basePath}`] : false
|
||||
}else{
|
||||
isMatched = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if(finalPatterns.length===0){
|
||||
return debug ? [true,"*"] : true
|
||||
}else{
|
||||
for(const pattern of finalPatterns){
|
||||
pattern[0].lastIndex = 0
|
||||
if(pattern[1]===true){
|
||||
if(pattern[0].test(file)) return debug ? [false,pattern[0].toString()] : false
|
||||
}else{
|
||||
if(pattern[0].test(file)) return debug ? [true,pattern[0].toString()] : true
|
||||
}
|
||||
}
|
||||
}
|
||||
return debug ? [isMatched,"*"] : isMatched
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getProjectRootFolder(folder,exclueCurrent=false){
|
||||
if(!path.isAbsolute(folder)){
|
||||
folder = path.join(process.cwd(),folder)
|
||||
}
|
||||
try{
|
||||
const pkgFile =exclueCurrent ?
|
||||
path.join(folder, "..", "package.json")
|
||||
: path.join(folder, "package.json")
|
||||
if(fs.existsSync(pkgFile)){
|
||||
return path.dirname(pkgFile)
|
||||
}
|
||||
const parent = path.dirname(folder)
|
||||
if(parent===folder) return null
|
||||
return getProjectRootFolder(parent,false)
|
||||
}catch(e){
|
||||
return process.cwd()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fileMatcher,
|
||||
getProjectRootFolder
|
||||
}
|
1
packages/vue/index.d.ts
vendored
1
packages/vue/index.d.ts
vendored
@ -1,4 +1,3 @@
|
||||
export {}
|
||||
import type { VoerkaI18nSupportedLanguages, VoerkaI18nTranslate } from "@Voerkai18n/runtime"
|
||||
import type { InjectionKey,Plugin } from "vue"
|
||||
|
||||
|
6
packages/webpack/data/app.js
Normal file
6
packages/webpack/data/app.js
Normal file
@ -0,0 +1,6 @@
|
||||
t("中国")
|
||||
t("中华人民共和国")
|
||||
t('中国')
|
||||
t('中华人民共和国')
|
||||
t("中国",1)
|
||||
t("中华人民共和国","fdf")
|
67
packages/webpack/debug-loader.js
Normal file
67
packages/webpack/debug-loader.js
Normal file
@ -0,0 +1,67 @@
|
||||
const { runLoaders } = require("loader-runner")
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
|
||||
runLoaders({
|
||||
resource: path.join(__dirname, "data","app.js"),
|
||||
// String: Absolute path to the resource (optionally including query string)
|
||||
|
||||
// loaders: [path.join(__dirname, "loader.js?x=1")],
|
||||
loaders:[
|
||||
{
|
||||
loader:path.join(__dirname, "loader.js"),
|
||||
options:{
|
||||
a:1
|
||||
}
|
||||
}
|
||||
],
|
||||
// String[]: Absolute paths to the loaders (optionally including query string)
|
||||
// {loader, options}[]: Absolute paths to the loaders with options object
|
||||
|
||||
context: { minimize: true },
|
||||
// Additional loader context which is used as base context
|
||||
|
||||
// processResource: (loaderContext, resourcePath, callback) => {
|
||||
// console.log("loaderContext=",resourcePath)
|
||||
// callback()
|
||||
// },
|
||||
// Optional: A function to process the resource
|
||||
// Must have signature function(context, path, function(err, buffer))
|
||||
// By default readResource is used and the resource is added a fileDependency
|
||||
|
||||
readResource: fs.readFile.bind(fs)
|
||||
// Optional: A function to read the resource
|
||||
// Only used when 'processResource' is not provided
|
||||
// Must have signature function(path, function(err, buffer))
|
||||
// By default fs.readFile is used
|
||||
}, function(err, result) {
|
||||
if(err){
|
||||
console.error("替换失败!!!")
|
||||
console.error(err.stack)
|
||||
}else{
|
||||
console.log("********** 成功 **********")
|
||||
console.log(result.result)
|
||||
}
|
||||
|
||||
// err: Error?
|
||||
|
||||
// result.result: Buffer | String
|
||||
// The result
|
||||
// only available when no error occured
|
||||
|
||||
// result.resourceBuffer: Buffer
|
||||
// The raw resource as Buffer (useful for SourceMaps)
|
||||
// only available when no error occured
|
||||
|
||||
// result.cacheable: Bool
|
||||
// Is the result cacheable or do it require reexecution?
|
||||
|
||||
// result.fileDependencies: String[]
|
||||
// An array of paths (existing files) on which the result depends on
|
||||
|
||||
// result.missingDependencies: String[]
|
||||
// An array of paths (not existing files) on which the result depends on
|
||||
|
||||
// result.contextDependencies: String[]
|
||||
// An array of paths (directories) on which the result depends on
|
||||
})
|
39
packages/webpack/loader.js
Normal file
39
packages/webpack/loader.js
Normal file
@ -0,0 +1,39 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const {
|
||||
getProjectRootFolder,
|
||||
getProjectLanguageFolder,
|
||||
readIdMapFile,
|
||||
replaceTranslateText,
|
||||
hasImportTranslateFunction,
|
||||
importTranslateFunction
|
||||
} = require('@voerkai18n/utils')
|
||||
|
||||
|
||||
function voerkaI18nLoader(content, map, meta) {
|
||||
const { autoImport,debug } =Object.assign({
|
||||
autoImport: false, // 是否自动导入t函数
|
||||
debug:false // 输出一些调试信息
|
||||
},this.query || {})
|
||||
try{
|
||||
const projectPath = getProjectRootFolder(this.resourcePath)
|
||||
const lngPath = getProjectLanguageFolder(projectPath)
|
||||
if(debug){
|
||||
console.log("[voerkai18n-loader]",`source=${this.resourcePath}`)
|
||||
}
|
||||
// 是否自动导入t函数
|
||||
if(autoImport && !hasImportTranslateFunction(content) ){
|
||||
content = importTranslateFunction(content, this.resourcePath , lngPath)
|
||||
}
|
||||
const idMap = readIdMapFile(projectPath)
|
||||
return replaceTranslateText(content,idMap)
|
||||
}catch(e){
|
||||
if(debug){
|
||||
console.error("[voerkai18n-loader]",this.resourcePath,e.stack)
|
||||
}
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
module.exports = voerkaI18nLoader;
|
19
packages/webpack/package.json
Normal file
19
packages/webpack/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "voerkai18n-loader",
|
||||
"version": "1.0.0",
|
||||
"description": "voerkai18n loader for webpack",
|
||||
"main": "loader.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"loader-runner": "^4.3.0",
|
||||
"loader-utils": "^3.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@voerkai18n/utils": "workspace:^1.0.21"
|
||||
}
|
||||
}
|
4
packages/webpack/src/languages/idMap.js
Normal file
4
packages/webpack/src/languages/idMap.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
中国:1,
|
||||
中华人民共和国:2
|
||||
}
|
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@ -247,7 +247,7 @@ importers:
|
||||
rollup-plugin-terser: ^7.0.2
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
'@babel/runtime-corejs3': 7.20.7
|
||||
'@babel/runtime-corejs3': 7.20.13
|
||||
core-js: 3.27.1
|
||||
devDependencies:
|
||||
'@babel/cli': 7.18.10_@babel+core@7.18.10
|
||||
@ -288,6 +288,17 @@ importers:
|
||||
'@voerkai18n/runtime': link:../runtime
|
||||
vue: 3.2.45
|
||||
|
||||
packages/webpack:
|
||||
specifiers:
|
||||
'@voerkai18n/utils': workspace:^1.0.21
|
||||
loader-runner: ^4.3.0
|
||||
loader-utils: ^3.2.1
|
||||
dependencies:
|
||||
'@voerkai18n/utils': link:../utils
|
||||
devDependencies:
|
||||
loader-runner: 4.3.0
|
||||
loader-utils: 3.2.1
|
||||
|
||||
packages:
|
||||
|
||||
/@ampproject/remapping/2.2.0:
|
||||
@ -1504,6 +1515,14 @@ packages:
|
||||
core-js-pure: 3.24.1
|
||||
regenerator-runtime: 0.13.9
|
||||
|
||||
/@babel/runtime-corejs3/7.20.13:
|
||||
resolution: {integrity: sha512-p39/6rmY9uvlzRiLZBIB3G9/EBr66LBMcYm7fIDeSBNdRjF2AGD3rFZucUyAgGHC2N+7DdLvVi33uTjSE44FIw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
core-js-pure: 3.27.1
|
||||
regenerator-runtime: 0.13.11
|
||||
dev: false
|
||||
|
||||
/@babel/runtime-corejs3/7.20.7:
|
||||
resolution: {integrity: sha512-jr9lCZ4RbRQmCR28Q8U8Fu49zvFqLxTY9AMOUz+iyMohMoAgpEcVxY+wJNay99oXOpOcCTODkk70NDN2aaJEeg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -8160,6 +8179,11 @@ packages:
|
||||
pinkie-promise: 2.0.1
|
||||
strip-bom: 2.0.0
|
||||
|
||||
/loader-runner/4.3.0:
|
||||
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
|
||||
engines: {node: '>=6.11.5'}
|
||||
dev: true
|
||||
|
||||
/loader-utils/1.4.0:
|
||||
resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@ -8169,6 +8193,11 @@ packages:
|
||||
json5: 1.0.1
|
||||
dev: true
|
||||
|
||||
/loader-utils/3.2.1:
|
||||
resolution: {integrity: sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
dev: true
|
||||
|
||||
/locate-path/5.0.0:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
|
Loading…
x
Reference in New Issue
Block a user