diff --git a/.gitignore b/.gitignore index f9cfc35..cafef6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.vscode -/node_modules \ No newline at end of file +/node_modules +/demodata/languages \ No newline at end of file diff --git a/package.json b/package.json index f667fe9..80f423d 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "deepmerge": "^4.2.2", "gulp": "^4.0.2", + "logsets": "^1.0.2", + "readjson": "^2.2.2", "through2": "^4.0.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca7753a..41aad80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,11 +3,15 @@ lockfileVersion: 5.3 specifiers: deepmerge: ^4.2.2 gulp: ^4.0.2 + logsets: ^1.0.2 + readjson: ^2.2.2 through2: ^4.0.2 dependencies: deepmerge: 4.2.2 gulp: 4.0.2 + logsets: registry.npmmirror.com/logsets/1.0.2 + readjson: registry.npmmirror.com/readjson/2.2.2 through2: 4.0.2 packages: @@ -191,14 +195,6 @@ packages: engines: {node: '>=0.10.0'} dev: false - /bindings/1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - requiresBuild: true - dependencies: - file-uri-to-path: 1.0.0 - dev: false - optional: true - /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -274,7 +270,7 @@ packages: readdirp: 2.2.1 upath: 1.2.0 optionalDependencies: - fsevents: 1.2.13 + fsevents: registry.npmmirror.com/fsevents/1.2.13 dev: false /class-utils/0.3.6: @@ -591,12 +587,6 @@ packages: resolution: {integrity: sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=} dev: false - /file-uri-to-path/1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - requiresBuild: true - dev: false - optional: true - /fill-range/4.0.0: resolution: {integrity: sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=} engines: {node: '>=0.10.0'} @@ -689,18 +679,6 @@ packages: resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} dev: false - /fsevents/1.2.13: - resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} - engines: {node: '>= 4.0'} - os: [darwin] - deprecated: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. - requiresBuild: true - dependencies: - bindings: 1.5.0 - nan: 2.15.0 - dev: false - optional: true - /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: false @@ -1269,12 +1247,6 @@ packages: engines: {node: '>= 0.10'} dev: false - /nan/2.15.0: - resolution: {integrity: sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==} - requiresBuild: true - dev: false - optional: true - /nanomatch/1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} @@ -2136,3 +2108,127 @@ packages: y18n: 3.2.2 yargs-parser: 5.0.1 dev: false + + registry.npmmirror.com/@babel/runtime-corejs3/7.17.2: + resolution: {integrity: sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz} + name: '@babel/runtime-corejs3' + version: 7.17.2 + engines: {node: '>=6.9.0'} + dependencies: + core-js-pure: registry.npmmirror.com/core-js-pure/3.21.1 + regenerator-runtime: registry.npmmirror.com/regenerator-runtime/0.13.9 + dev: false + + registry.npmmirror.com/ansicolor/1.1.100: + resolution: {integrity: sha512-Jl0pxRfa9WaQVUX57AB8/V2my6FJxrOR1Pp2qqFbig20QB4HzUoQ48THTKAgHlUCJeQm/s2WoOPcoIDhyCL/kw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ansicolor/-/ansicolor-1.1.100.tgz} + name: ansicolor + version: 1.1.100 + dev: false + + registry.npmmirror.com/bindings/1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz} + name: bindings + version: 1.5.0 + requiresBuild: true + dependencies: + file-uri-to-path: registry.npmmirror.com/file-uri-to-path/1.0.0 + dev: false + optional: true + + registry.npmmirror.com/core-js-pure/3.21.1: + resolution: {integrity: sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.21.1.tgz} + name: core-js-pure + version: 3.21.1 + requiresBuild: true + dev: false + + registry.npmmirror.com/core-js/3.21.1: + resolution: {integrity: sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/core-js/-/core-js-3.21.1.tgz} + name: core-js + version: 3.21.1 + requiresBuild: true + dev: false + + registry.npmmirror.com/deepmerge/4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deepmerge/-/deepmerge-4.2.2.tgz} + name: deepmerge + version: 4.2.2 + engines: {node: '>=0.10.0'} + dev: false + + registry.npmmirror.com/file-uri-to-path/1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz} + name: file-uri-to-path + version: 1.0.0 + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/fsevents/1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz} + name: fsevents + version: 1.2.13 + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. + requiresBuild: true + dependencies: + bindings: registry.npmmirror.com/bindings/1.5.0 + nan: registry.npmmirror.com/nan/2.15.0 + dev: false + optional: true + + registry.npmmirror.com/get-own-enumerable-property-symbols/3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz} + name: get-own-enumerable-property-symbols + version: 3.0.2 + dev: false + + registry.npmmirror.com/jju/1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz} + name: jju + version: 1.4.0 + dev: false + + registry.npmmirror.com/logsets/1.0.2: + resolution: {integrity: sha512-iA1FVnA89QeicyT3g3YtrWcoNIhwVIOalSAj507VJ2OXKvAMKrfsK/OFv5pCz8NOhWV1Vlo5jZUlPV9dUsrOJw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/logsets/-/logsets-1.0.2.tgz} + name: logsets + version: 1.0.2 + dependencies: + '@babel/runtime-corejs3': registry.npmmirror.com/@babel/runtime-corejs3/7.17.2 + ansicolor: registry.npmmirror.com/ansicolor/1.1.100 + core-js: registry.npmmirror.com/core-js/3.21.1 + deepmerge: registry.npmmirror.com/deepmerge/4.2.2 + get-own-enumerable-property-symbols: registry.npmmirror.com/get-own-enumerable-property-symbols/3.0.2 + dev: false + + registry.npmmirror.com/nan/2.15.0: + resolution: {integrity: sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nan/-/nan-2.15.0.tgz} + name: nan + version: 2.15.0 + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/readjson/2.2.2: + resolution: {integrity: sha512-PdeC9tsmLWBiL8vMhJvocq+OezQ3HhsH2HrN7YkhfYcTjQSa/iraB15A7Qvt7Xpr0Yd2rDNt6GbFwVQDg3HcAw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/readjson/-/readjson-2.2.2.tgz} + name: readjson + version: 2.2.2 + engines: {node: '>=10'} + dependencies: + jju: registry.npmmirror.com/jju/1.4.0 + try-catch: registry.npmmirror.com/try-catch/3.0.0 + dev: false + + registry.npmmirror.com/regenerator-runtime/0.13.9: + resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz} + name: regenerator-runtime + version: 0.13.9 + dev: false + + registry.npmmirror.com/try-catch/3.0.0: + resolution: {integrity: sha512-3uAqUnoemzca1ENvZ72EVimR+E8lqBbzwZ9v4CEbLjkaV3Q+FtdmPUt7jRtoSoTiYjyIMxEkf6YgUpe/voJ1ng==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/try-catch/-/try-catch-3.0.0.tgz} + name: try-catch + version: 3.0.0 + engines: {node: '>=6'} + dev: false diff --git a/src/extract.plugin.js b/src/extract.plugin.js index ee22710..722bb2f 100644 --- a/src/extract.plugin.js +++ b/src/extract.plugin.js @@ -5,50 +5,20 @@ const through2 = require('through2') const deepmerge = require("deepmerge") const path = require('path') +const fs = require('fs') +const readJson = require("readjson") +const { deepStrictEqual } = require('assert') // 捕获翻译函数的表达式 const DefaultTranslateMatcher = /\bt\(\s*("|'){1}(?:((?\w+):))?(?.*?)(((\1\s*\)){1})|((\1){1}\s*(,(\w|\d|(?:\{.*\})|(?:\[.*\])|([\"\'\(].*[\"\'\)]))*)*\s*\)))/gm - -/** - * 找出要翻译的文本列表 {namespace:[text,text],...} - * {namespace:{text:[],...} - * @param {*} content - * @param {*} matcher - * @returns - */ -function getTranslateTexts(content,matcher,namespace,file){ - if(!matcher) return - let texts = {default:{}} - while ((result = matcher.exec(content)) !== null) { - // 这对于避免零宽度匹配的无限循环是必要的 - if (result.index === matcher.lastIndex) { - matcher.lastIndex++; - } - const text = result.groups.text - if(text){ - const ns = result.groups.namespace || namespace - if(!(ns in texts)){ - texts[ns] = {} - } - texts[ns][text] = [file.relative] - } - } - return texts -} - -// 将texts合并到results中 -function mergeTranslateTexts(results,texts){ - -} - // 获取指定文件的名称空间 /** * * @param {*} file * @param {*} namespaces 名称空间配置 {:[path,...,path],:path,:(file)=>{}} */ -function getFileNamespace(file,options){ + function getFileNamespace(file,options){ const {output, namespaces } = options const refPath = file.relative.toLowerCase() for(let [name,paths] of Object.entries(options.namespaces)){ @@ -65,16 +35,63 @@ function getFileNamespace(file,options){ } +/** + * 找出要翻译的文本列表 {namespace:[text,text],...} + * {namespace:{text:{cn:"",en:"",$source:""},...} + * @param {*} content + * @param {*} matcher + * @returns + */ +function getTranslateTexts(content,file,options){ + + let { matcher,languages,defaultLanguage } = options + + if(!matcher) return + + // 获取当前文件的名称空间 + const namespace = getFileNamespace(file,options) + + let texts = {default:{}} + while ((result = matcher.exec(content)) !== null) { + // 这对于避免零宽度匹配的无限循环是必要的 + if (result.index === matcher.lastIndex) { + matcher.lastIndex++; + } + const text = result.groups.text + if(text){ + const ns = result.groups.namespace || namespace + if(!(ns in texts)){ + texts[ns] = {} + } + texts[ns][text] ={} + languages.forEach(language=>{ + if(language !== defaultLanguage){ + texts[ns][text][language] = "" + } + }) + texts[ns][text]["$file"]=[file.relative] + } + } + return texts +} + + +function mergeLanguageFile(langFile,texts,options){ + + + +} module.exports = function(options={}){ options = Object.assign({ debug : true, // 输出调试信息 format : "js", // 目标文件格式,取值JSON,JS - languages : ["en","zh"], // 目标语言列表 - defaultLanguage: "zh", // 默认语言 + languages : ["en","cn"], // 目标语言列表 + defaultLanguage: "cn", // 默认语言 matcher : DefaultTranslateMatcher, // 匹配翻译函数并提取内容的正则表达式 namespaces : {}, // 命名空间, {[name]: [path,...,path]} - output : null // 输出目录,如果没有指定,则转让 + output : null, // 输出目录,如果没有指定,则转让 + merge : true, // 输出文本时默认采用合并更新方式 },options) let {debug,output:outputPath,languages} = options // 输出语言文件 {cn:{default:<文件>,namespace:<文件>},en:{default:{}}} @@ -95,18 +112,28 @@ module.exports = function(options={}){ if(file.isStream()){ return callback() } - // 获取该文件的 - const namespace = getFileNamespace(file,options) - if(debug) console.log(namespace," : ",file.path) - // 提出出翻译文件 - const texts = getTranslateTexts(file.contents.toString(),options.matcher,namespace,file) + // 提取翻译文本 + const texts = getTranslateTexts(file.contents.toString(),file,options) results = deepmerge(results,texts) callback() },function(callback){ - console.log("输出路径:",outputPath) - console.log(JSON.stringify(results,null,2)) + console.log("输出路径:",outputPath) + const translatesPath = path.join(outputPath,"translates") + if(!fs.existsSync(translatesPath)) fs.mkdirSync(translatesPath) + + for(let [namespace,texts] of Object.entries(results)){ + const langFile = path.join(outputPath,"translates",`${namespace}.json`) + const isExists = fs.existsSync(langFile) + const langTexts = isExists ? readJson.sync(langFile) : {} + if(isExists && options.merge){ + mergeLanguageFile(langFile,langTexts,options) + }else{ + fs.writeFileSync(langFile,JSON.stringify(texts,null,4)) + } + } + callback() }); } \ No newline at end of file diff --git a/src/extract.test.js b/src/extract.test.js index a45acef..c945316 100644 --- a/src/extract.test.js +++ b/src/extract.test.js @@ -10,6 +10,7 @@ gulp.src([ "!"+ soucePath+ '/languages/**' ]).pipe(extract({ // output: path.join(soucePath , 'languages'), + languages: ['en','cn','de','jp'], namespaces:{ "a":"a", "b":"b",