This commit is contained in:
zhaoyingbo 2022-11-09 13:03:55 +08:00
parent 39f7755468
commit c40c40d3f5
6 changed files with 138 additions and 120 deletions

View File

@ -210,6 +210,7 @@ function extractAll({ dirPath, prefix }) {
const langsPrefix = prefix ? prefix.replace(/^I18N\./, '') : null;
// 获取目标文件下全部中文文案
const allTargetStrs = findAllChineseText(dir);
console.log(allTargetStrs);
if (allTargetStrs.length === 0) {
console.log(utils_1.highlightText('没有发现可替换的文案!'));
return;

File diff suppressed because one or more lines are too long

View File

@ -6,40 +6,43 @@ require('ts-node').register({
compilerOptions: {
module: 'commonjs'
}
});
import * as fs from 'fs';
import { tsvFormatRows } from 'd3-dsv';
import { getAllMessages, getProjectConfig } from './utils';
import * as _ from 'lodash';
})
import * as fs from 'fs'
import { tsvFormatRows } from 'd3-dsv'
import { getTargetLangObjs, getProjectConfig } from './utils'
import * as _ from 'lodash'
function exportMessages(file?: string, lang?: string) {
const CONFIG = getProjectConfig();
const langs = lang ? [lang] : CONFIG.distLangs;
const CONFIG = getProjectConfig()
const langs = lang ? [lang] : CONFIG.distLangs
const srcLangObjs = getTargetLangObjs(CONFIG.srcLang)
langs.map(lang => {
const allMessages = getAllMessages(CONFIG.srcLang);
const existingTranslations = getAllMessages(
// 已经存在的翻译文案
const targetLangObjs = getTargetLangObjs(
lang,
(message, key) => !/[\u4E00-\u9FA5]/.test(allMessages[key]) || allMessages[key] !== message
);
const messagesToTranslate = Object.keys(allMessages)
.filter(key => !existingTranslations.hasOwnProperty(key))
(message, key) => !/[\u4E00-\u9FA5]/.test(srcLangObjs[key]) || srcLangObjs[key] !== message
)
// 待翻译的文案
const messagesToTranslate = Object.keys(srcLangObjs)
.filter(key => !targetLangObjs.hasOwnProperty(key))
.map(key => {
let message = allMessages[key];
message = JSON.stringify(message).slice(1, -1);
return [key, message];
});
// 把中文文案直接写回对应的文案
let message = srcLangObjs[key]
message = JSON.stringify(message).slice(1, -1)
targetLangObjs[key] = message
})
if (messagesToTranslate.length === 0) {
console.log('All the messages have been translated.');
return;
console.log('未发现未翻译文案')
return
}
const content = tsvFormatRows(messagesToTranslate);
const sourceFile = file || `./export-${lang}`;
fs.writeFileSync(sourceFile, content);
console.log(`Exported ${messagesToTranslate.length} message(s).`);
});
const content = tsvFormatRows(messagesToTranslate)
const sourceFile = file || `./export-${lang}`
fs.writeFileSync(sourceFile, content)
console.log(`Exported ${messagesToTranslate.length} message(s).`)
})
}
export { exportMessages };
export { exportMessages }

View File

@ -220,7 +220,7 @@ function extractAll({ dirPath, prefix }: { dirPath?: string; prefix?: string })
// 去除I18N前缀后续全局加
const langsPrefix = prefix ? prefix.replace(/^I18N\./, '') : null
// 获取目标文件下全部中文文案
// 获取目标文件下全部中文文案,并按文件夹归类
const allTargetStrs = findAllChineseText(dir)
if (allTargetStrs.length === 0) {
console.log(highlightText('没有发现可替换的文案!'))
@ -240,7 +240,9 @@ function extractAll({ dirPath, prefix }: { dirPath?: string; prefix?: string })
console.log('即将截取每个中文文案的前5位翻译生成key值并替换中...\n')
// 对当前文件进行文案key生成和替换
/**
* key生成和替换
*/
const generateKeyAndReplace = async item => {
const { texts, file: filePath } = item
console.log(`${highlightText(filePath)} 替换中...`)
@ -313,6 +315,11 @@ function extractAll({ dirPath, prefix }: { dirPath?: string; prefix?: string })
failInfo(e.message)
})
}
/**
*
*
*
*/
allTargetStrs
.reduce((prev, current) => {
return prev.then(() => {

View File

@ -11,7 +11,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as _ from 'lodash';
import { tsvParseRows } from 'd3-dsv';
import { getAllMessages, getProjectConfig, traverse } from './utils';
import { getTargetLangObjs, getProjectConfig, traverse } from './utils';
const CONFIG = getProjectConfig();
@ -57,7 +57,7 @@ function writeMessagesToFile(messages: any, file: string, lang: string) {
function importMessages(file: string, lang: string) {
let messagesToImport = getMessagesToImport(file);
const allMessages = getAllMessages(CONFIG.srcLang);
const allMessages = getTargetLangObjs(CONFIG.srcLang);
messagesToImport = _.pickBy(messagesToImport, (message, key) => allMessages.hasOwnProperty(key));
const keysByFiles = _.groupBy(Object.keys(messagesToImport), key => key.split('.')[0]);
const messagesByFiles = _.mapValues(keysByFiles, (keys, file) => {

View File

@ -2,30 +2,30 @@
* @author linhuiw
* @desc
*/
import * as path from 'path';
import * as _ from 'lodash';
import * as inquirer from 'inquirer';
import * as fs from 'fs';
import { pinyin } from 'pinyin-pro';
import { PROJECT_CONFIG, CANARY_CONFIG_FILE } from './const';
const colors = require('colors');
import * as path from 'path'
import * as _ from 'lodash'
import * as inquirer from 'inquirer'
import * as fs from 'fs'
import { pinyin } from 'pinyin-pro'
import { PROJECT_CONFIG, CANARY_CONFIG_FILE } from './const'
const colors = require('colors')
function lookForFiles(dir: string, fileName: string): string {
const files = fs.readdirSync(dir);
const files = fs.readdirSync(dir)
for (let file of files) {
const currName = path.join(dir, file);
const info = fs.statSync(currName);
const currName = path.join(dir, file)
const info = fs.statSync(currName)
if (info.isDirectory()) {
if (file === '.git' || file === 'node_modules') {
continue;
continue
}
const result = lookForFiles(currName, fileName);
const result = lookForFiles(currName, fileName)
if (result) {
return result;
return result
}
} else if (info.isFile() && file === fileName) {
return currName;
return currName
}
}
}
@ -34,26 +34,26 @@ function lookForFiles(dir: string, fileName: string): string {
*
*/
function getProjectConfig() {
const configFile = path.resolve(process.cwd(), `./${CANARY_CONFIG_FILE}`);
let obj = PROJECT_CONFIG.defaultConfig;
const configFile = path.resolve(process.cwd(), `./${CANARY_CONFIG_FILE}`)
let obj = PROJECT_CONFIG.defaultConfig
if (configFile && fs.existsSync(configFile)) {
obj = {
...obj,
...JSON.parse(fs.readFileSync(configFile, 'utf8'))
};
}
}
return obj;
return obj
}
/**
*
*/
function getCanaryDir() {
const config = getProjectConfig();
const config = getProjectConfig()
if (config) {
return config.canaryDir;
return config.canaryDir
}
}
@ -62,8 +62,8 @@ function getCanaryDir() {
* @param lang
*/
function getLangDir(lang) {
const langsDir = getCanaryDir();
return path.resolve(langsDir, lang);
const langsDir = getCanaryDir()
return path.resolve(langsDir, lang)
}
/**
@ -73,40 +73,47 @@ function traverse(obj, cb) {
function traverseInner(obj, cb, path) {
_.forEach(obj, (val, key) => {
if (typeof val === 'string') {
cb(val, [...path, key].join('.'));
cb(val, [...path, key].join('.'))
} else if (typeof val === 'object' && val !== null) {
traverseInner(val, cb, [...path, key]);
traverseInner(val, cb, [...path, key])
}
});
})
}
traverseInner(obj, cb, []);
traverseInner(obj, cb, [])
}
/**
*
*
* @return { 'common.test': '测试', 'common.chinese': '中国' }
*/
function getAllMessages(lang: string, filter = (message: string, key: string) => true) {
const srcLangDir = getLangDir(lang);
let files = fs.readdirSync(srcLangDir);
files = files.filter(file => file.endsWith('.ts') && file !== 'index.ts').map(file => path.resolve(srcLangDir, file));
function getTargetLangObjs(lang: string, filter = (message: string, key: string) => true) {
// 是否是JS项目
const { isJsProj } = getProjectConfig()
// 指定语言配置文件夹
const langDir = getLangDir(lang)
// 过滤文件并生成对应文件路径
let files = fs.readdirSync(langDir)
const tsFilter = file => file.endsWith('.ts') && file !== 'index.ts'
const jsFilter = file => file.endsWith('.js') && file !== 'index.js'
files = files.filter(isJsProj ? jsFilter : tsFilter).map(file => path.resolve(langDir, file))
const allMessages = files.map(file => {
const { default: messages } = require(file);
const fileNameWithoutExt = path.basename(file).split('.')[0];
const flattenedMessages = {};
console.log(fileNameWithoutExt, messages)
traverse(messages, (message, path) => {
const key = fileNameWithoutExt + '.' + path;
// 读取所有文件的文案配置并拍平
const langObjs = files.map(file => {
const { default: messages } = require(file)
const fileName = path.basename(file).split('.')[0]
const flattenedMessages = {}
traverse(messages, (message, keyPath) => {
const key = fileName + '.' + keyPath
if (filter(message, key)) {
flattenedMessages[key] = message;
flattenedMessages[key] = message
}
});
})
return flattenedMessages;
});
return flattenedMessages
})
return Object.assign({}, ...allMessages);
return Object.assign({}, ...langObjs)
}
/**
@ -115,15 +122,15 @@ function getAllMessages(lang: string, filter = (message: string, key: string) =>
* @param times
*/
function retry(asyncOperation, times = 1) {
let runTimes = 1;
let runTimes = 1
const handleReject = e => {
if (runTimes++ < times) {
return asyncOperation().catch(handleReject);
return asyncOperation().catch(handleReject)
} else {
throw e;
throw e
}
};
return asyncOperation().catch(handleReject);
}
return asyncOperation().catch(handleReject)
}
/**
@ -134,31 +141,31 @@ function retry(asyncOperation, times = 1) {
function withTimeout(promise, ms) {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(`Promise timed out after ${ms} ms.`);
}, ms);
});
return Promise.race([promise, timeoutPromise]);
reject(`Promise timed out after ${ms} ms.`)
}, ms)
})
return Promise.race([promise, timeoutPromise])
}
/**
* 使google翻译
*/
function translateText(text, toLang) {
const CONFIG = getProjectConfig();
const options = CONFIG.translateOptions;
const { translate: googleTranslate } = require('google-translate')(CONFIG.googleApiKey, options);
const CONFIG = getProjectConfig()
const options = CONFIG.translateOptions
const { translate: googleTranslate } = require('google-translate')(CONFIG.googleApiKey, options)
return withTimeout(
new Promise((resolve, reject) => {
googleTranslate(text, 'zh', PROJECT_CONFIG.langMap[toLang], (err, translation) => {
if (err) {
reject(err);
reject(err)
} else {
resolve(translation.translatedText);
resolve(translation.translatedText)
}
});
})
}),
5000
);
)
}
/**
@ -177,35 +184,35 @@ function translateWithBaiduPinyin(text: string, origin: string) {
baiduTranslate(appId, appKey, 'en', 'zh')(text)
.then(data => {
if (data && data.trans_result) {
const result = data.trans_result.map(item => item.dst) || [];
resolve(result);
const result = data.trans_result.map(item => item.dst) || []
resolve(result)
}
})
.catch(err => {
reject(err);
});
reject(err)
})
}
// Pinyin
if (origin === 'Pinyin') {
const result = pinyin(text, { toneType: 'none' });
resolve(result.split('$'));
const result = pinyin(text, { toneType: 'none' })
resolve(result.split('$'))
}
}),
3000
);
)
}
return retry(_translateText, 3);
return retry(_translateText, 3)
}
function findMatchKey(langObj, text) {
for (const key in langObj) {
if (langObj[key] === text) {
return key;
return key
}
}
return '';
return ''
}
/**
@ -215,46 +222,46 @@ function findMatchKey(langObj, text) {
*/
function flatten(obj, prefix = '') {
var propName = prefix ? prefix + '.' : '',
ret = {};
ret = {}
for (var attribute in obj) {
var attr = attribute.replace(/-/g, '_');
var attr = attribute.replace(/-/g, '_')
if (_.isArray(obj[attr])) {
var len = obj[attr].length;
ret[attr] = obj[attr].join(',');
var len = obj[attr].length
ret[attr] = obj[attr].join(',')
} else if (typeof obj[attr] === 'object') {
_.extend(ret, flatten(obj[attr], propName + attr));
_.extend(ret, flatten(obj[attr], propName + attr))
} else {
ret[propName + attr] = obj[attr];
ret[propName + attr] = obj[attr]
}
}
return ret;
return ret
}
/**
*
*/
async function getTranslateOriginType() {
const { googleApiKey, baiduApiKey } = getProjectConfig();
let translateType = ['Google', 'Baidu'];
const { googleApiKey, baiduApiKey } = getProjectConfig()
let translateType = ['Google', 'Baidu']
if (!googleApiKey) {
translateType = translateType.filter(item => item !== 'Google');
translateType = translateType.filter(item => item !== 'Google')
}
if (!baiduApiKey || !baiduApiKey.appId || !baiduApiKey.appKey) {
translateType = translateType.filter(item => item !== 'Baidu');
translateType = translateType.filter(item => item !== 'Baidu')
}
if (translateType.length === 0) {
console.log('请配置 googleApiKey 或 baiduApiKey ');
console.log('请配置 googleApiKey 或 baiduApiKey ')
return {
pass: false,
origin: ''
};
}
}
if (translateType.length == 1) {
return {
pass: true,
origin: translateType[0]
};
}
}
const { origin } = await inquirer.prompt({
type: 'list',
@ -262,32 +269,32 @@ async function getTranslateOriginType() {
message: '请选择使用的翻译源',
default: 'Google',
choices: ['Google', 'Baidu']
});
})
return {
pass: true,
origin: origin
};
}
}
/**
*
*/
function successInfo(message: string, needEnter = false) {
console.log(`${needEnter ? '\n' : ''}successInfo: `, colors.green(message));
console.log(`${needEnter ? '\n' : ''}successInfo: `, colors.green(message))
}
/**
*
*/
function failInfo(message: string) {
console.log('failInfo: ', colors.red(message));
console.log('failInfo: ', colors.red(message))
}
/**
*
*/
function highlightText(message: string | number) {
return colors.yellow(`${message}`);
return colors.yellow(`${message}`)
}
export {
@ -296,7 +303,7 @@ export {
traverse,
retry,
withTimeout,
getAllMessages,
getTargetLangObjs,
getProjectConfig,
translateText,
findMatchKey,
@ -307,4 +314,4 @@ export {
successInfo,
failInfo,
highlightText
};
}