使用 deepseek-api 快速完成 国际化项目(i18n)新增语种工具
解决痛点
快下班的你突然被告知 多语言项目要新增一个语种?你是不是满屏???? 😋不要慌我用 deepseek来帮你准点下班!还能多俩天💰的用来摸鱼的工时 嘿嘿嘿!!!
项目概述
这个项目是一个用于管理多语言国际化(i18n)的工具,主要针对国际xx平台的前端应用。该工具提供了简单的命令行接口,帮助开发人员轻松地添加新语言和批量修改现有语言变量,大大提高了国际化开发的效率。
功能特点
- 新增语言支持 :通过简单的命令行操作,可以快速添加新的语言支持,系统会自动翻译现有内容
- 自动翻译 :集成了DeepSeek AI翻译API,能够自动将中文内容翻译成目标语言
- 支持多种文件结构 :同时支持 lang 目录和 page 目录下的语言文件管理
技术架构
- 语言 :TypeScript
- 运行环境 :Node.js
- 依赖 :
- axios:用于API请求
- ts-node:执行TypeScript脚本
- fs/path:文件系统操作
使用方法
1. 新增语言
执行后,按照提示:
- 输入语种中文名称(如:英语)
- 输入语种英文代码(如:en-US) 系统会自动:
- 扫描所有现有的中文语言文件
- 使用AI翻译内容
- 在相应目录生成新语言文件
- 更新主索引文件
工作原理
-
添加新语言 :
- 扫描目录lang和page下面所有zh-CN.ts文件
- 提取需要翻译的文本
- 调用DeepSeek API进行批量翻译
- 生成新的语言文件
- 更新主索引文件
优势
- 提高效率 :自动化翻译和文件生成,减少手动操作
- 保持一致性 :批量修改确保所有语言文件同步更新
- 专业翻译 :使用AI翻译,针对物联网行业进行优化
- 易于使用 :简单的命令行界面,无需复杂配置
📖注意事项
- 翻译API需要有效的DeepSeek API密钥💰
- 翻译结果可能需要人工审核以确保准确性,可以修改给大模型的提示提具体的细分行业
- 项目使用ESM模块系统,确保Node.js版本支持 这个工具极大地简化了国际化开发流程,特别适合需要支持多语言的物联网平台项目使用。
📖源码
script/addLanguage.ts
typescript
import fs from 'fs';
import path from 'path';
import readline from 'readline';
import { fileURLToPath } from 'url';
import { batchTranslate } from '../utils/index.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const BASE_PATH = path.resolve(__dirname, '..');
/** 创建命令行交互界面 */
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
/** 递归获取目录下所有 ts 文件 */
async function getAllTsFiles(dir: string): Promise<string[]> {
const files = await fs.promises.readdir(dir);
const tsFiles: string[] = [];
for (const file of files) {
const fullPath = path.join(dir, file);
const stat = await fs.promises.stat(fullPath);
if (stat.isDirectory()) {
const subFiles = await getAllTsFiles(fullPath);
tsFiles.push(...subFiles);
} else if (file === 'zh-CN.ts') {
tsFiles.push(fullPath);
}
}
return tsFiles;
}
/** 处理单个文件的翻译 */
async function translateFile(zhFilePath: string, langCode: string) {
const content = await fs.promises.readFile(zhFilePath, 'utf-8');
// 判断是否为 page 目录下的文件
const isPageFile = zhFilePath.includes(path.join('page', ''));
if (isPageFile) {
// 处理 page 目录下的文件
const lines = content.split('\n');
const objectName = path.basename(path.dirname(zhFilePath)); // 获取目录名作为对象名
// 找到对象初始化的行
const initLine = lines.find(line => line.includes(`t.${objectName} = {}`));
if (!initLine) return;
// 构建对象
const resultObj: any = {};
resultObj[objectName] = {};
// 提取所有属性赋值
const regex = new RegExp(`t\\.${objectName}\\.(\\w+)\\s*=\\s*"([^"]+)"`, 'g');
let match;
while ((match = regex.exec(content)) !== null) {
const [, key, value] = match;
resultObj[objectName][key] = value;
}
// 翻译内容
const translatedContent = await batchTranslate(resultObj, langCode);
// 生成新文件内容
const newContent = [
`import { IObject } from "@/types/interface";`,
`const t: IObject = {};`,
`t.${objectName} = {};`
];
// 添加翻译后的属性
Object.entries(translatedContent[objectName]).forEach(([key, value]) => {
newContent.push(`t.${objectName}.${key} = "${value}";`);
});
newContent.push('export default t;');
// 写入新文件
const newFilePath = zhFilePath.replace('zh-CN.ts', `${langCode}.ts`);
await fs.promises.writeFile(newFilePath, newContent.join('\n'));
} else {
// 处理 lang 目录下的文件
const match = content.match(/export\s+default\s+({[\s\S]*})/);
if (!match) return;
const zhContent = eval('(' + match[1] + ')');
const translatedContent = await batchTranslate(zhContent, langCode);
const newFilePath = zhFilePath.replace('zh-CN.ts', `${langCode}.ts`);
const fileContent = `export default ${JSON.stringify(translatedContent, null, 4)}`;
await fs.promises.writeFile(newFilePath, fileContent);
}
}
export async function addNewLanguage() {
try {
// 询问用户新语言信息
const langName = await new Promise(resolve => {
rl.question('请输入要添加的语言中文名称(如:英语): ', resolve);
});
const langCode: string = await new Promise(resolve => {
rl.question('请输入对应的语言代码(如:en-US): ', resolve);
});
console.log('开始翻译...');
// 获取所有需要处理的文件
const langFiles = await getAllTsFiles(path.join(BASE_PATH, 'lang'));
const pageFiles = await getAllTsFiles(path.join(BASE_PATH, 'page'));
const allFiles = [...langFiles, ...pageFiles];
// 处理所有文件
for (const file of allFiles) {
console.log(`正在处理: ${file}`);
await translateFile(file, langCode);
}
// 更新 index.ts
const indexPath = path.join(BASE_PATH, 'index.ts');
let indexContent = await fs.promises.readFile(indexPath, 'utf-8');
// 添加导入语句
const importStatement = `import ${langCode}Locale from './lang/${langCode}'\n`;
indexContent = indexContent.replace(
/(import.*from.*\n)(?!import)/,
`$1${importStatement}`
);
// 添加到 messages 对象
const messagesAddition = ` ${langCode}: {\n ...${langCode}Locale\n },\n`;
indexContent = indexContent.replace(
/(const messages = {\n)([^}]*)(\n})/,
`$1$2 ${messagesAddition}$3`
);
// 更新 vantLocales 函数
const vantLocalesAddition = ` } else if (lang === '${langCode}') {\n Locale.use(lang, ${langCode}Locale)`;
indexContent = indexContent.replace(
/(function vantLocales.*?\{[\s\S]*?)(}\n)/,
`$1${vantLocalesAddition}\n $2`
);
await fs.promises.writeFile(indexPath, indexContent);
console.log(`成功添加新语言 ${langName}(${langCode})!`);
} catch (error) {
console.error('发生错误:', error);
} finally {
rl.close();
}
}
export default addNewLanguage;
addNewLanguage();
script/utils.ts
typescript
import axios from 'axios';
interface TranslationMap {
[key: string]: string | TranslationMap;
}
const DEEPSEEK_API_KEY = '*************************'; // 需要替换为你的 DeepSeek API key
// 将嵌套对象扁平化为待翻译的文本数组
function flattenTranslationObject(obj: TranslationMap): string[] {
const texts: string[] = [];
function traverse(current: TranslationMap) {
for (const key in current) {
const value = current[key];
if (typeof value === 'string' && /[\u4e00-\u9fa5]/.test(value)) {
texts.push(value);
} else if (typeof value === 'object') {
traverse(value as TranslationMap);
}
}
}
traverse(obj);
return texts;
}
// 使用翻译结果重建原始结构
function rebuildTranslationObject(obj: TranslationMap, translations: string[],): TranslationMap {
let index = 0;
const result: TranslationMap = {};
function traverse(current: TranslationMap, target: TranslationMap) {
for (const key in current) {
const value = current[key];
if (typeof value === 'string' && /[\u4e00-\u9fa5]/.test(value)) {
target[key] = translations[index++];
} else if (typeof value === 'object') {
target[key] = {};
traverse(value as TranslationMap, target[key] as TranslationMap);
} else {
target[key] = value;
}
}
}
traverse(obj, result);
return result;
}
async function translateAPI(texts: string[], targetLang: string) {
// 预处理:移除文本中的换行符
const processedTexts = texts.map(text => text.replace(/\n/g, ' '));
const messages = [{
role: 'user',
content: `请将以下内容翻译为${targetLang},翻译语义切近于物联网行业,按顺序返回翻译结果,每个翻译结果占一行(请不要在翻译结果中使用换行符):\n${processedTexts.join('\n')}`
}];
try {
const response = await axios.post(
'https://api.deepseek.com/chat/completions',
{
model: 'deepseek-chat',
messages,
temperature: 1.3
},
{
headers: {
'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,
'Content-Type': 'application/json'
},
timeout: 60000
}
);
const result = response.data.choices[0].message.content.trim();
console.log({result, targetLang});
// 将结果按行分割,并确保每个翻译项都是单行文本
const translatedTexts = result.split('\n').map((text: string) => text.replace(/\n/g, ' ').trim());
return translatedTexts.length === texts.length ? translatedTexts : texts.map(() => '');
} catch (error: any) {
console.error('翻译失败:', error.response?.data || error.message);
return texts.map(() => ''); // 失败时返回空值数组
}
}
// 示例使用
export const batchTranslate = async (sourceObj: TranslationMap, langCode: string): Promise<TranslationMap> => {
console.log({sourceObj})
// 1. 扁平化对象,获取所有需要翻译的文本
const textsToTranslate = flattenTranslationObject(sourceObj);
console.log({textsToTranslate, langCode})
// 2. 这里需要调用实际的翻译API进行批量翻译
const translations = await translateAPI(textsToTranslate, langCode);
// 3. 使用翻译结果重建对象
return rebuildTranslationObject(sourceObj, translations);
}
package.json
perl
{
"name": "i18ndemo",
"version": "1.0.0",
"type": "module",
"scripts": {
"add-lang": "ts-node-esm scripts/addLanguage.ts"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^22.13.10",
"axios": "^1.8.2",
"ts-node": "^10.9.2",
"typescript": "^5.8.2"
}
}
项目结构





