😎1、增加清理相关js脚本 2、调整翻译模式及增加Deepseek翻译
This commit is contained in:
parent
6e9780c507
commit
b9265c4fd9
3
Web/.env
3
Web/.env
@ -12,3 +12,6 @@ VITE_PUBLIC_PATH =
|
|||||||
|
|
||||||
# 国密SM公钥(保持空即可,自动从后台获取)
|
# 国密SM公钥(保持空即可,自动从后台获取)
|
||||||
VITE_SM_PUBLIC_KEY =
|
VITE_SM_PUBLIC_KEY =
|
||||||
|
|
||||||
|
# 翻译 DeepSeek API key: https://platform.deepseek.com/api_keys
|
||||||
|
DEEPSEEK_API_KEY = "你的 DeepSeek API key"
|
||||||
@ -3,7 +3,7 @@ CHCP 65001
|
|||||||
|
|
||||||
set dir=%~dp0
|
set dir=%~dp0
|
||||||
|
|
||||||
set moduleName=apiServices
|
set moduleName=system
|
||||||
set apiServicesPath=%dir%..\src\api-services\system\
|
set apiServicesPath=%dir%..\src\api-services\system\
|
||||||
set apiUrl=http://localhost:5005/swagger/Default/swagger.json
|
set apiUrl=http://localhost:5005/swagger/Default/swagger.json
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
## 脚本一键生成
|
## 脚本一键生成
|
||||||
|
|
||||||
Swagger Codegen 读取 swagger.json 生成 typescript-axios 客户端后,直接拷贝到 src/api-services/system 子文件夹中
|
Swagger Codegen 读取 swagger.json 生成 typescript-axios 客户端后,直接拷贝到 src/api-services/system 子文件夹中(建议每个应用层创建一个独立的文件夹单独放置,和框架接口分开)
|
||||||
|
|
||||||
> Swagger Codegen 可以通过为任何 API 生成服务端代码和客户端代码的方式来简化 OpenAPI 的构建过程,因此,项目开发团队可以更好地关注 API 的实现和应用
|
> Swagger Codegen 可以通过为任何 API 生成服务端代码和客户端代码的方式来简化 OpenAPI 的构建过程,因此,项目开发团队可以更好地关注 API 的实现和应用
|
||||||
> Github:https://github.com/swagger-api/swagger-codegen
|
> Github:https://github.com/swagger-api/swagger-codegen
|
||||||
|
|||||||
11040
Web/lang/index.json
11040
Web/lang/index.json
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,8 @@
|
|||||||
"build-api": "cd api_build/ && build.bat",
|
"build-api": "cd api_build/ && build.bat",
|
||||||
"build-approvalFlow-api": "cd api_build/ && build.bat approvalFlow",
|
"build-approvalFlow-api": "cd api_build/ && build.bat approvalFlow",
|
||||||
"build-dingTalk-api": "cd api_build/ && build.bat dingTalk",
|
"build-dingTalk-api": "cd api_build/ && build.bat dingTalk",
|
||||||
"build-goView-api": "cd api_build/ && build.bat goView"
|
"build-goView-api": "cd api_build/ && build.bat goView",
|
||||||
|
"translate": "node script/translate.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
@ -95,7 +96,10 @@
|
|||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||||
"@vue/compiler-sfc": "^3.5.18",
|
"@vue/compiler-sfc": "^3.5.18",
|
||||||
|
"cli-progress": "^3.12.0",
|
||||||
"code-inspector-plugin": "^1.0.5",
|
"code-inspector-plugin": "^1.0.5",
|
||||||
|
"colors": "^1.4.0",
|
||||||
|
"dotenv": "^17.2.1",
|
||||||
"eslint": "^9.33.0",
|
"eslint": "^9.33.0",
|
||||||
"eslint-plugin-vue": "^10.4.0",
|
"eslint-plugin-vue": "^10.4.0",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
|
|||||||
16
Web/script/clean-dist.js
Normal file
16
Web/script/clean-dist.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 清理dist文件夹
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const directoriesToDelete = ['dist'];
|
||||||
|
|
||||||
|
// 删除文件夹
|
||||||
|
directoriesToDelete.forEach((dir) => {
|
||||||
|
const dirPath = path.join(process.cwd(), dir);
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||||
|
console.log(`Deleted directory: ${dirPath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Directory not found: ${dirPath}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
16
Web/script/clean-lock.js
Normal file
16
Web/script/clean-lock.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 清理lock文件
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const filesToDelete = ['pnpm-lock.yaml', 'package-lock.json'];
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
filesToDelete.forEach((file) => {
|
||||||
|
const filePath = path.join(process.cwd(), file);
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
console.log(`Deleted file: ${filePath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`File not found: ${filePath}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
16
Web/script/clean-modules.js
Normal file
16
Web/script/clean-modules.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 清理modules文件夹
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const directoriesToDelete = ['node_modules'];
|
||||||
|
|
||||||
|
// 删除文件夹
|
||||||
|
directoriesToDelete.forEach((dir) => {
|
||||||
|
const dirPath = path.join(process.cwd(), dir);
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||||
|
console.log(`Deleted directory: ${dirPath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Directory not found: ${dirPath}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
238
Web/script/translate.cjs
Normal file
238
Web/script/translate.cjs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
// DeepSeek 翻译
|
||||||
|
require('dotenv').config();
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const API_URL = 'https://api.deepseek.com/v1/chat/completions';
|
||||||
|
const API_KEY = process.env.DEEPSEEK_API_KEY;
|
||||||
|
const SOURCE_LANG = 'zh-cn';
|
||||||
|
const LOCALE_FILE = path.resolve(__dirname, '../lang/index.json');
|
||||||
|
|
||||||
|
// 引入进度条库
|
||||||
|
const cliProgress = require('cli-progress');
|
||||||
|
const colors = require('colors');
|
||||||
|
|
||||||
|
async function translateBatch(texts, targetLang) {
|
||||||
|
// 构建批量内容:每行以[index]开头
|
||||||
|
const batchContent = texts.map((text, index) => `[${index}] ${text}`).join('\n');
|
||||||
|
|
||||||
|
const response = await fetch(API_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${API_KEY}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: 'deepseek-chat',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: `作为企业软件系统专业翻译,严格遵守以下铁律:
|
||||||
|
|
||||||
|
■ 核心原则
|
||||||
|
1. 严格逐符号翻译(${SOURCE_LANG}→${targetLang})
|
||||||
|
2. 禁止添加/删除/改写任何内容
|
||||||
|
3. 保持批量翻译的编号格式
|
||||||
|
|
||||||
|
■ 符号保留规则
|
||||||
|
! 所有符号必须原样保留:
|
||||||
|
• 编程符号:\${ } <% %> @ # & |
|
||||||
|
• UI占位符:{0} %s [ ]
|
||||||
|
• 货币单位:¥100.00 kg cm²
|
||||||
|
• 中文符号:【 】 《 》 :
|
||||||
|
|
||||||
|
■ 中文符号位置规范
|
||||||
|
# 三级处理机制:
|
||||||
|
1. 成对符号必须保持完整结构:
|
||||||
|
✓ 正确:【Warning】Text
|
||||||
|
✗ 禁止:Warning【 】Text
|
||||||
|
|
||||||
|
2. 独立符号位置:
|
||||||
|
• 优先句尾 → Text】?
|
||||||
|
• 次选句首 → 】Text?
|
||||||
|
• 禁止句中 → Text】Text?
|
||||||
|
|
||||||
|
3. 跨字符串符号处理:
|
||||||
|
• 前段含【时 → 保留在段尾("Synchronize【")
|
||||||
|
• 后段含】时 → 保留在段首("】authorization data?")
|
||||||
|
• 符号后接字母时添加空格:】 Authorization
|
||||||
|
|
||||||
|
■ 语法规范
|
||||||
|
• 外文 → 被动语态("Item was created")
|
||||||
|
• 中文 → 主动语态("已创建项目")
|
||||||
|
• 禁止推测上下文(只翻译当前字符串内容)
|
||||||
|
|
||||||
|
■ 错误预防(绝对禁止)
|
||||||
|
✗ 将中文符号改为西式符号(】→])
|
||||||
|
✗ 移动非中文符号位置
|
||||||
|
✗ 添加原文不存在的内容
|
||||||
|
✗ 合并/拆分原始字符串
|
||||||
|
|
||||||
|
■ 批量处理
|
||||||
|
▸ 严格保持原始JSON结构
|
||||||
|
▸ 语言键名精确匹配(zh-cn/en/it等)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: batchContent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
temperature: 0.3,
|
||||||
|
max_tokens: 4000,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (!response.ok || !data.choices || !data.choices[0]?.message?.content) {
|
||||||
|
const errorMsg = data.error?.message || `HTTP ${response.status}: ${response.statusText}`;
|
||||||
|
throw new Error(`翻译API返回错误:${errorMsg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析批量响应
|
||||||
|
const batchResult = data.choices[0].message.content.trim();
|
||||||
|
const translations = {};
|
||||||
|
|
||||||
|
// 按行分割结果
|
||||||
|
const lines = batchResult.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
// 使用更精确的匹配模式
|
||||||
|
const match = line.match(/^\[(\d+)\]\s*(.+)/);
|
||||||
|
if (match) {
|
||||||
|
const index = parseInt(match[1]);
|
||||||
|
translations[index] = match[2].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTargetLangs(localeData) {
|
||||||
|
const allLangs = new Set();
|
||||||
|
for (const translations of Object.values(localeData)) {
|
||||||
|
for (const lang of Object.keys(translations)) {
|
||||||
|
if (lang !== SOURCE_LANG) {
|
||||||
|
allLangs.add(lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...allLangs];
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupTasksByLang(localeData, targetLangs) {
|
||||||
|
const tasks = {};
|
||||||
|
|
||||||
|
for (const lang of targetLangs) {
|
||||||
|
tasks[lang] = {
|
||||||
|
keys: [],
|
||||||
|
texts: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, translations] of Object.entries(localeData)) {
|
||||||
|
const sourceText = translations[SOURCE_LANG];
|
||||||
|
if (!sourceText) {
|
||||||
|
console.warn(`⚠️ 缺少源语言(${SOURCE_LANG})文本: ${key}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const lang of targetLangs) {
|
||||||
|
if (!translations[lang] || translations[lang].trim() === '') {
|
||||||
|
tasks[lang].keys.push(key);
|
||||||
|
tasks[lang].texts.push(sourceText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// 读取语言文件
|
||||||
|
const rawData = fs.readFileSync(LOCALE_FILE);
|
||||||
|
const localeData = JSON.parse(rawData);
|
||||||
|
const TARGET_LANGS = extractTargetLangs(localeData);
|
||||||
|
const langTasks = groupTasksByLang(localeData, TARGET_LANGS);
|
||||||
|
|
||||||
|
let totalUpdated = 0;
|
||||||
|
const BATCH_SIZE = 10;
|
||||||
|
// 创建多进度条容器
|
||||||
|
const multibar = new cliProgress.MultiBar(
|
||||||
|
{
|
||||||
|
format: '{lang} |' + colors.cyan('{bar}') + '| {percentage}% | {value}/{total} 条',
|
||||||
|
barCompleteChar: '\u2588',
|
||||||
|
barIncompleteChar: '\u2591',
|
||||||
|
hideCursor: true,
|
||||||
|
clearOnComplete: true,
|
||||||
|
stopOnComplete: true,
|
||||||
|
},
|
||||||
|
cliProgress.Presets.shades_grey
|
||||||
|
);
|
||||||
|
// 为每个语言创建进度条
|
||||||
|
const progressBars = {};
|
||||||
|
for (const lang of TARGET_LANGS) {
|
||||||
|
if (langTasks[lang].texts.length > 0) {
|
||||||
|
progressBars[lang] = multibar.create(langTasks[lang].texts.length, 0, {
|
||||||
|
lang: lang.padEnd(6, ' '),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并行处理所有语言
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(langTasks).map(async ([lang, task]) => {
|
||||||
|
if (task.texts.length === 0) return;
|
||||||
|
|
||||||
|
// 分批处理
|
||||||
|
for (let i = 0; i < task.texts.length; i += BATCH_SIZE) {
|
||||||
|
const batchKeys = task.keys.slice(i, i + BATCH_SIZE);
|
||||||
|
const batchTexts = task.texts.slice(i, i + BATCH_SIZE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const batchResults = await translateBatch(batchTexts, lang);
|
||||||
|
|
||||||
|
// 更新翻译结果
|
||||||
|
batchKeys.forEach((key, index) => {
|
||||||
|
if (batchResults[index] !== undefined) {
|
||||||
|
localeData[key][lang] = batchResults[index];
|
||||||
|
totalUpdated++;
|
||||||
|
} else {
|
||||||
|
console.error(`❌ 缺失翻译结果 [${key}@${lang}]`);
|
||||||
|
localeData[key][lang] = `[BATCH_ERROR] ${localeData[key][SOURCE_LANG]}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新进度条
|
||||||
|
progressBars[lang].increment(batchTexts.length);
|
||||||
|
|
||||||
|
// 每批处理后保存进度
|
||||||
|
fs.writeFileSync(LOCALE_FILE, JSON.stringify(localeData, null, 2));
|
||||||
|
|
||||||
|
// 添加请求间隔避免速率限制
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`\n❌ 批次翻译失败 [${lang}]:`, error.message);
|
||||||
|
// 标记失败条目
|
||||||
|
batchKeys.forEach((key) => {
|
||||||
|
localeData[key][lang] = `[TRANSLATION_FAILED] ${localeData[key][SOURCE_LANG]}`;
|
||||||
|
});
|
||||||
|
// 跳过当前批次继续处理
|
||||||
|
progressBars[lang].increment(batchTexts.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 停止所有进度条
|
||||||
|
multibar.stop();
|
||||||
|
|
||||||
|
// 最终保存
|
||||||
|
fs.writeFileSync(LOCALE_FILE, JSON.stringify(localeData, null, 2));
|
||||||
|
|
||||||
|
// 显示最终结果
|
||||||
|
if (totalUpdated > 0) {
|
||||||
|
console.log(`\n✅ 翻译完成! 共更新 ${totalUpdated} 处翻译`);
|
||||||
|
} else {
|
||||||
|
console.log('\nℹ️ 没有需要更新的翻译');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
@ -76,7 +76,8 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
|
|||||||
translateKey: '$tr',
|
translateKey: '$tr',
|
||||||
// 是否清除已经不在上下文中的内容(清除项目中不再使用到的源语言键值对)
|
// 是否清除已经不在上下文中的内容(清除项目中不再使用到的源语言键值对)
|
||||||
isClear: true,
|
isClear: true,
|
||||||
// 翻译器(支持 doubao 或 deepseek)
|
// 翻译器
|
||||||
|
translator: new EmptyTranslator(),
|
||||||
// translator: new VolcengineTranslator({
|
// translator: new VolcengineTranslator({
|
||||||
// apiKey: '',
|
// apiKey: '',
|
||||||
// model: '',
|
// model: '',
|
||||||
@ -85,16 +86,15 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
|
|||||||
// appId: '',
|
// appId: '',
|
||||||
// appKey: '',
|
// appKey: '',
|
||||||
// }),
|
// }),
|
||||||
// translator: new EmptyTranslator(),
|
// translator: new GoogleTranslator({
|
||||||
translator: new GoogleTranslator({
|
// proxyOption: {
|
||||||
proxyOption: {
|
// host: '127.0.0.1',
|
||||||
host: '127.0.0.1',
|
// port: 7890,
|
||||||
port: 7890,
|
// headers: {
|
||||||
headers: {
|
// 'User-Agent': 'Node',
|
||||||
'User-Agent': 'Node',
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// }),
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
root: process.cwd(),
|
root: process.cwd(),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user