😋1分钟 新增i18n新语种,按完回车就下班 !!!

使用 deepseek-api 快速完成 国际化项目(i18n)新增语种工具

解决痛点

快下班的你突然被告知 多语言项目要新增一个语种?你是不是满屏???? 😋不要慌我用 deepseek来帮你准点下班!还能多俩天💰的用来摸鱼的工时 嘿嘿嘿!!!

项目概述

这个项目是一个用于管理多语言国际化(i18n)的工具,主要针对国际xx平台的前端应用。该工具提供了简单的命令行接口,帮助开发人员轻松地添加新语言和批量修改现有语言变量,大大提高了国际化开发的效率。

功能特点

  1. 新增语言支持 :通过简单的命令行操作,可以快速添加新的语言支持,系统会自动翻译现有内容
  2. 自动翻译 :集成了DeepSeek AI翻译API,能够自动将中文内容翻译成目标语言
  3. 支持多种文件结构 :同时支持 lang 目录和 page 目录下的语言文件管理

技术架构

  • 语言 :TypeScript
  • 运行环境 :Node.js
  • 依赖 :
    • axios:用于API请求
    • ts-node:执行TypeScript脚本
    • fs/path:文件系统操作

使用方法

1. 新增语言

执行后,按照提示:

  • 输入语种中文名称(如:英语)
  • 输入语种英文代码(如:en-US) 系统会自动:
  • 扫描所有现有的中文语言文件
  • 使用AI翻译内容
  • 在相应目录生成新语言文件
  • 更新主索引文件

工作原理

  1. 添加新语言 :

    • 扫描目录lang和page下面所有zh-CN.ts文件
    • 提取需要翻译的文本
    • 调用DeepSeek API进行批量翻译
    • 生成新的语言文件
    • 更新主索引文件

优势

  1. 提高效率 :自动化翻译和文件生成,减少手动操作
  2. 保持一致性 :批量修改确保所有语言文件同步更新
  3. 专业翻译 :使用AI翻译,针对物联网行业进行优化
  4. 易于使用 :简单的命令行界面,无需复杂配置

📖注意事项

  • 翻译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"
  }
}

项目结构

相关推荐
Carlos_sam1 分钟前
OpenLayers:extent与view extent 介绍
前端·javascript
小谭的成长日记8 分钟前
使用js使页面元素匀速横向滚动,如何能减少性能消耗
前端
前端小巷子9 分钟前
CSS伪类选择器大全:提升网页交互与样式的神奇工具
前端
前端涂涂10 分钟前
Node.js 的定义、用途、安装方法
前端
前端涂涂11 分钟前
Node.js 中的 Buffer(缓冲区)
前端
糖墨夕21 分钟前
【2】Three.js-创建3D场景
前端·webgl·three.js
三原24 分钟前
什么是微应用?我需不需要使用微应用?
前端·架构·设计
三原28 分钟前
前端微应用-乾坤(qiankun)原理分析-single-spa
前端·架构·设计
布兰妮甜37 分钟前
Angular 框架详解:从入门到进阶
前端·javascript·前端框架·angular.js
独立开阀者_FwtCoder1 小时前
做Docx预览,一定要做这个神库!!
前端·javascript·面试