欢迎光临
我们一直在努力

AI 生成前端国际化翻译:从 Key 到多语言文案的全流程

AI 生成前端国际化翻译:从 Key 到多语言文案的全流程

一、前端国际化翻译的工程化挑战

前端国际化(i18n)需要维护多语言翻译文件,传统方案依赖人工翻译或第三方翻译服务。随着产品功能迭代,翻译文件同步成为负担,容易出现遗漏或翻译不准确。

AI 辅助翻译能够通过大语言模型(LLM),自动生成多语言翻译,并保持术语一致性。核心挑战包括:上下文理解、术语统一、复数形式处理、变量插值支持。

// 传统国际化文件维护方式(存在问题)
// en-US.json
{
"welcome.message": "Welcome to our app",
"button.save": "Save",
"user.profile.title": "User Profile",
"notification.unread_count": "You have {count} unread notifications"
}

// zh-CN.json(需要人工翻译,容易遗漏或翻译不一致)
{
"welcome.message": "欢迎使用我们的应用",
"button.save": "保存",
"user.profile.title": "用户资料",
"notification.unread_count": "您有 {count} 条未读通知"
}

// 新增键时,需要手动添加到所有语言文件
// 删除键时,需要手动从所有语言文件中删除
// 这导致维护成本高、容易出错

二、AI 翻译的技术架构与流程

AI 驱动的国际化翻译系统包含以下核心模块:

  • Key 提取模块:从源代码中自动提取国际化 Key
  • 上下文分析模块:理解 Key 的使用场景和语义
  • 翻译生成模块:调用 LLM API 生成多语言翻译
  • 术语一致性检查:确保同一术语在不同地方翻译一致
  • 翻译文件更新:自动更新 JSON/YAML 翻译文件
  • graph TD
    A[源代码扫描] –> B[提取 i18n Keys]
    B –> C{翻译文件是否存在}
    C –>|否| D[创建新翻译条目]
    C –>|是| E[检查是否需要更新]

    D –> F[上下文分析]
    E –> F

    F –> G[调用 LLM 生成翻译]
    G –> H[术语一致性检查]
    H –> I[人工审核(可选)]
    I –> J[更新翻译文件]

    J –> K[生成 TypeScript 类型]
    K –> L[集成到构建流程]

    style A fill:#e1f5fe
    style G fill:#fff3e0
    style J fill:#e8f5e9
    style L fill:#f3e5f5

    技术选型:

    • Key 提取:使用 AST 分析工具(Babel Parser)扫描代码中的 t()、i18n.t() 等调用
    • LLM 选择:OpenAI GPT-4(翻译质量高)、Claude 3(上下文理解强)、或开源模型(成本低)
    • 术语库:使用 JSON 或数据库存储产品特定术语的翻译映射
    • 质量控制:使用 BLEU 评分、人工审核、A/B 测试

    场景描述:在一次实际项目中,我们用 Babel Parser 做 Key 提取时发现了一个隐蔽问题:动态 Key 无法被 AST 静态分析捕获。比如 t(\\user.status.${status}`)` 这种模板字符串方式的调用,AST 看到的不是一个 StringLiteral 而是一个 TemplateLiteral,导致提取器漏掉了大量 Key。等到日语版本的翻译文件上线后,大量未翻译的 Key 直接以英文原文显示在页面上。

    // Babel Parser 只能静态分析,这种动态 key 会漏掉
    const statusText = t(`user.status.${status}`); // ❌ 不会被提取

    // 解决方案:使用枚举替换模板字符串
    const statusMap = {
    active: 'user.status.active',
    inactive: 'user.status.inactive',
    banned: 'user.status.banned'
    } as const;
    const statusText = t(statusMap[status]); // ✅ 可被静态分析

    这个案例的教训是:AST 提取不能 100% 覆盖所有场景,团队需要建立规范,限制动态 Key 的使用。同时应该在 CI 中加入覆盖度检查——运行时收集所有实际调用过的 t() key,与静态提取的结果做 diff,发现遗漏及时补充。

    // i18n Key 提取器实现
    // services/i18nKeyExtractor.ts
    import * as babelParser from '@babel/parser';
    import * as t from '@babel/types';
    import traverse from '@babel/traverse';
    import * as fs from 'fs';
    import * as path from 'path';

    export interface I18nKeyInfo {
    key: string;
    defaultValue?: string;
    filePath: string;
    lineNumber: number;
    context?: string; // 周围的代码上下文
    }

    export class I18nKeyExtractor {
    private patterns: Array<{
    name: string;
    detect: (node: t.Node) => I18nKeyInfo | null;
    }>;

    constructor() {
    this.patterns = [
    {
    name: 'react-i18next t()',
    detect: (node) => {
    if (
    t.isCallExpression(node) &&
    t.isIdentifier(node.callee) &&
    node.callee.name === 't' &&
    node.arguments.length >= 1 &&
    t.isStringLiteral(node.arguments[0])
    ) {
    return {
    key: node.arguments[0].value,
    defaultValue: node.arguments[1]?.type === 'StringLiteral'
    ? node.arguments[1].value
    : undefined,
    filePath: '', // 需要在遍历时设置
    lineNumber: node.loc?.start.line || 0
    };
    }
    return null;
    }
    },
    {
    name: 'i18next t()',
    detect: (node) => {
    if (
    t.isCallExpression(node) &&
    t.isMemberExpression(node.callee) &&
    t.isIdentifier(node.callee.object) &&
    node.callee.object.name === 'i18next' &&
    t.isIdentifier(node.callee.property) &&
    node.callee.property.name === 't' &&
    node.arguments.length >= 1 &&
    t.isStringLiteral(node.arguments[0])
    ) {
    return {
    key: node.arguments[0].value,
    filePath: '',
    lineNumber: node.loc?.start.line || 0
    };
    }
    return null;
    }
    },
    {
    name: 'FormattedMessage (react-intl)',
    detect: (node) => {
    if (
    t.isJSXElement(node) &&
    t.isJSXOpeningElement(node.openingElement) &&
    t.isJSXIdentifier(node.openingElement.name) &&
    node.openingElement.name.name === 'FormattedMessage'
    ) {
    const idAttr = node.openingElement.attributes.find(
    attr => t.isJSXAttribute(attr) &&
    t.isJSXIdentifier(attr.name) &&
    attr.name.name === 'id'
    ) as t.JSXAttribute | undefined;

    if (idAttr?.value && t.isStringLiteral(idAttr.value)) {
    return {
    key: idAttr.value.value,
    filePath: '',
    lineNumber: node.loc?.start.line || 0
    };
    }
    }
    return null;
    }
    }
    ];
    }

    /**
    * 从文件中提取 i18n Keys
    */
    extractFromFile(filePath: string): I18nKeyInfo[] {
    const code = fs.readFileSync(filePath, 'utf-8');
    const keys: I18nKeyInfo[] = [];

    try {
    const ast = babelParser.parse(code, {
    sourceType: 'module',
    plugins: ['jsx', 'typescript', 'decorators-legacy']
    });

    traverse(ast, {
    enter: (path) => {
    for (const pattern of this.patterns) {
    const result = pattern.detect(path.node);
    if (result) {
    result.filePath = filePath;
    // 获取更多上下文(前 3 行代码)
    const lines = code.split('\\n');
    const startLine = Math.max(0, result.lineNumber – 4);
    result.context = lines.slice(startLine, result.lineNumber).join('\\n');
    keys.push(result);
    break; // 一个节点只匹配一个模式
    }
    }
    }
    });
    } catch (error) {
    console.error(`Failed to parse file ${filePath}:`, error);
    }

    return keys;
    }

    /**
    * 扫描目录,提取所有 i18n Keys
    */
    extractFromDirectory(dirPath: string, extensions: string[] = ['.tsx', '.ts', '.jsx', '.js']): I18nKeyInfo[] {
    const allKeys: I18nKeyInfo[] = [];

    const scanDirectory = (currentPath: string) => {
    const items = fs.readdirSync(currentPath);

    for (const item of items) {
    const itemPath = path.join(currentPath, item);
    const stat = fs.statSync(itemPath);

    if (stat.isDirectory()) {
    // 跳过 node_modules、dist 等目录
    if (!['node_modules', 'dist', 'build', '.git'].includes(item)) {
    scanDirectory(itemPath);
    }
    } else if (stat.isFile()) {
    const ext = path.extname(itemPath);
    if (extensions.includes(ext)) {
    const keys = this.extractFromFile(itemPath);
    allKeys.push(…keys);
    }
    }
    }
    };

    scanDirectory(dirPath);
    return allKeys;
    }
    }

    三、实战:AI 翻译生成与术语一致性保障

    以下实现完整的 AI 翻译生成流程,包括术语库管理、LLM 调用、翻译质量控制。

    1. 术语库管理

    // services/terminologyManager.ts
    export interface TermEntry {
    term: string; // 源语言术语
    translations: Record<string, string>; // 各语言的翻译
    context?: string; // 使用场景说明
    approved: boolean; // 是否已审核
    }

    export class TerminologyManager {
    private terms: Map<string, TermEntry> = new Map();
    private filePath: string;

    constructor(filePath: string = './i18n/terminology.json') {
    this.filePath = filePath;
    this.loadTerms();
    }

    /**
    * 加载术语库
    */
    private loadTerms(): void {
    try {
    const content = fs.readFileSync(this.filePath, 'utf-8');
    const data = JSON.parse(content);

    for (const [term, entry] of Object.entries(data)) {
    this.terms.set(term, entry as TermEntry);
    }

    console.log(`Loaded ${this.terms.size} terminology entries`);
    } catch (error) {
    console.log('Terminology file not found, starting with empty terminology');
    }
    }

    /**
    * 保存术语库
    */
    private saveTerms(): void {
    const data = Object.fromEntries(this.terms);
    fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), 'utf-8');
    }

    /**
    * 添加或更新术语
    */
    addTerm(term: string, translations: Record<string, string>, context?: string): void {
    const existing = this.terms.get(term);

    this.terms.set(term, {
    term,
    translations: { …existing?.translations, …translations },
    context: context || existing?.context,
    approved: false
    });

    this.saveTerms();
    }

    /**
    * 查找术语翻译
    */
    findTranslation(term: string, language: string): string | null {
    const entry = this.terms.get(term);
    return entry?.translations[language] || null;
    }

    /**
    * 构建 LLM Prompt 的术语部分
    */
    buildTerminologyPrompt(languages: string[]): string {
    if (this.terms.size === 0) {
    return '';
    }

    let prompt = '\\n\\n## Terminology (必须严格遵守)\\n';

    for (const [term, entry] of this.terms) {
    prompt += `- ${term}:`;
    for (const lang of languages) {
    const translation = entry.translations[lang];
    if (translation) {
    prompt += ` [${lang}] ${translation}`;
    }
    }
    if (entry.context) {
    prompt += ` (上下文: ${entry.context})`;
    }
    prompt += '\\n';
    }

    return prompt;
    }
    }

    2. AI 翻译生成服务

    // services/aiTranslationService.ts
    import OpenAI from 'openai';

    export interface TranslationRequest {
    keys: Array<{
    key: string;
    defaultValue?: string;
    context?: string;
    }>;
    sourceLanguage: string;
    targetLanguages: string[];
    terminologyPrompt?: string;
    }

    export interface TranslationResult {
    [key: string]: {
    [language: string]: string;
    };
    }

    export class AITranslationService {
    private openai: OpenAI;
    private terminologyManager: TerminologyManager;

    constructor(apiKey: string, terminologyManager: TerminologyManager) {
    this.openai = new OpenAI({ apiKey });
    this.terminologyManager = terminologyManager;
    }

    /**
    * 批量生成翻译
    */
    async generateTranslations(request: TranslationRequest): Promise<TranslationResult> {
    const { keys, sourceLanguage, targetLanguages, terminologyPrompt } = request;

    // 构建 LLM Prompt
    const prompt = this.buildPrompt(
    keys,
    sourceLanguage,
    targetLanguages,
    terminologyPrompt
    );

    try {
    const response = await this.openai.chat.completions.create({
    model: 'gpt-4',
    messages: [
    {
    role: 'system',
    content: 'You are a professional translator specializing in software UI translation. Provide accurate, concise, and contextually appropriate translations.'
    },
    {
    role: 'user',
    content: prompt
    }
    ],
    temperature: 0.3, // 低温度确保一致性
    response_format: { type: 'json_object' }
    });

    const content = response.choices[0]?.message?.content;

    if (!content) {
    throw new Error('Empty response from LLM');
    }

    const translations = JSON.parse(content) as TranslationResult;

    // 验证翻译结果
    this.validateTranslations(translations, keys, targetLanguages);

    return translations;
    } catch (error) {
    console.error('Failed to generate translations:', error);
    throw error;
    }
    }

    /**
    * 构建翻译 Prompt
    */
    private buildPrompt(
    keys: TranslationRequest['keys'],
    sourceLanguage: string,
    targetLanguages: string[],
    terminologyPrompt?: string
    ): string {
    let prompt = `# Task: Translate UI strings for a software application\\n\\n`;
    prompt += `## Source Language: ${sourceLanguage}\\n`;
    prompt += `## Target Languages: ${targetLanguages.join(', ')}\\n\\n`;
    prompt += `## Strings to Translate\\n\\n`;

    for (const item of keys) {
    prompt += `### Key: ${item.key}\\n`;
    if (item.defaultValue) {
    prompt += `Default Value: "${item.defaultValue}"\\n`;
    }
    if (item.context) {
    prompt += `Context: ${item.context}\\n`;
    }
    prompt += `\\n`;
    }

    // 添加术语库约束
    if (terminologyPrompt) {
    prompt += terminologyPrompt;
    }

    prompt += `\\n## Instructions\\n`;
    prompt += `1. Provide translations for ALL target languages.\\n`;
    prompt += `2. Keep translations concise and appropriate for UI elements.\\n`;
    prompt += `3. Preserve variables in the format {variableName} unchanged.\\n`;
    prompt += `4. For plural forms, use the format: "one item | {count} items".\\n`;
    prompt += `5. Return the result as a JSON object with the following structure:\\n`;
    prompt += `{\\n`;
    prompt += ` "key_name": {\\n`;
    prompt += ` "language_code": "translation"\\n`;
    prompt += ` }\\n`;
    prompt += `}\\n`;

    return prompt;
    }

    /**
    * 验证翻译结果
    */
    private validateTranslations(
    translations: TranslationResult,
    keys: TranslationRequest['keys'],
    targetLanguages: string[]
    ): void {
    const errors: string[] = [];

    for (const keyInfo of keys) {
    const translation = translations[keyInfo.key];

    if (!translation) {
    errors.push(`Missing translation for key: ${keyInfo.key}`);
    continue;
    }

    for (const lang of targetLanguages) {
    if (!translation[lang]) {
    errors.push(`Missing ${lang} translation for key: ${keyInfo.key}`);
    }
    }

    // 检查变量是否保留
    if (keyInfo.defaultValue) {
    const variables = keyInfo.defaultValue.match(/\\{(\\w+)\\}/g) || [];

    for (const lang of targetLanguages) {
    const translated = translation[lang];
    if (translated) {
    for (const variable of variables) {
    if (!translated.includes(variable)) {
    errors.push(`Variable ${variable} not preserved in ${lang} translation for key: ${keyInfo.key}`);
    }
    }
    }
    }
    }
    }

    if (errors.length > 0) {
    console.warn('Translation validation warnings:');
    errors.forEach(err => console.warn(` – ${err}`));
    }
    }
    }

    3. 翻译文件自动更新

    场景描述:自动更新翻译文件虽然方便,但是 AI 翻译偶尔会把占位符 {variable} 翻译成目标语言的等价表达,比如把 {count} items 的日语翻译写成 {count} アイテム 就对了,但有时模型会把 {count} 当成原文的一部分翻译成 {個数},导致运行时变量不再被识别为插值点,直接显示为原始字符串。我们的做法是在 updateTranslations 方法中增加校验步骤,检查翻译后占位符数量和原始值的占位符数量是否一致:

    // 占位符完整性校验
    function validatePlaceholders(original: string, translated: string): boolean {
    const regex = /\\{(\\w+)\\}/g;
    const origMatches = original.match(regex) || [];
    const transMatches = translated.match(regex) || [];
    return origMatches.every(m => transMatches.includes(m));
    }

    如果校验失败,对应的 key 标记为需要人工复审,不直接写入文件。这个简单的校验避免了多次线上"文案出现 {個数} 而不是实际数字"的事故。

    // services/translationFileManager.ts
    export class TranslationFileManager {
    private basePath: string;

    constructor(basePath: string = './public/locales') {
    this.basePath = basePath;
    }

    /**
    * 更新翻译文件
    */
    async updateTranslations(
    translations: TranslationResult,
    targetLanguages: string[]
    ): Promise<void> {
    for (const lang of targetLanguages) {
    const filePath = path.join(this.basePath, lang, 'translation.json');

    // 读取现有翻译
    let existing: Record<string, string> = {};
    try {
    const content = fs.readFileSync(filePath, 'utf-8');
    existing = JSON.parse(content);
    } catch (error) {
    console.log(`Creating new translation file: ${filePath}`);
    }

    // 更新翻译
    let updated = false;
    for (const [key, translation] of Object.entries(translations)) {
    if (translation[lang] && existing[key] !== translation[lang]) {
    existing[key] = translation[lang];
    updated = true;
    }
    }

    // 写回文件
    if (updated) {
    fs.mkdirSync(path.dirname(filePath), { recursive: true });
    fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), 'utf-8');
    console.log(`Updated translation file: ${filePath}`);
    }
    }
    }

    /**
    * 检测缺失的翻译
    */
    findMissingTranslations(
    keys: string[],
    targetLanguages: string[]
    ): Record<string, string[]> {
    const missing: Record<string, string[]> = {};

    for (const lang of targetLanguages) {
    const filePath = path.join(this.basePath, lang, 'translation.json');

    try {
    const content = fs.readFileSync(filePath, 'utf-8');
    const translations = JSON.parse(content);

    const missingKeys = keys.filter(key => !translations[key]);

    if (missingKeys.length > 0) {
    missing[lang] = missingKeys;
    }
    } catch (error) {
    console.error(`Failed to read translation file for ${lang}:`, error);
    missing[lang] = keys; // 文件不存在,所有 key 都缺失
    }
    }

    return missing;
    }
    }

    4. 完整工作流集成

    // scripts/generate-translations.ts – CLI 工具
    import { I18nKeyExtractor } from '../services/i18nKeyExtractor';
    import { TerminologyManager } from '../services/terminologyManager';
    import { AITranslationService } from '../services/aiTranslationService';
    import { TranslationFileManager } from '../services/translationFileManager';

    async function main() {
    // 1. 提取 i18n Keys
    console.log('Extracting i18n keys…');
    const extractor = new I18nKeyExtractor();
    const keys = extractor.extractFromDirectory('./src');
    console.log(`Found ${keys.length} i18n keys`);

    // 2. 加载术语库
    console.log('Loading terminology…');
    const termManager = new TerminologyManager();
    const terminologyPrompt = termManager.buildTerminologyPrompt(['zh-CN', 'ja-JP', 'ko-KR']);

    // 3. 检测缺失的翻译
    console.log('Checking for missing translations…');
    const fileManager = new TranslationFileManager();
    const missing = fileManager.findMissingTranslations(
    keys.map(k => k.key),
    ['zh-CN', 'ja-JP', 'ko-KR']
    );

    console.log('Missing translations:', missing);

    // 4. 生成翻译
    if (Object.keys(missing).length > 0) {
    console.log('Generating translations with AI…');
    const translationService = new AITranslationService(
    process.env.OPENAI_API_KEY!,
    termManager
    );

    const keysToTranslate = keys.filter(k =>
    Object.values(missing).some(langKeys => langKeys.includes(k.key))
    );

    const translations = await translationService.generateTranslations({
    keys: keysToTranslate.map(k => ({
    key: k.key,
    defaultValue: k.defaultValue,
    context: k.context
    })),
    sourceLanguage: 'en-US',
    targetLanguages: ['zh-CN', 'ja-JP', 'ko-KR'],
    terminologyPrompt
    });

    // 5. 更新翻译文件
    console.log('Updating translation files…');
    await fileManager.updateTranslations(translations, ['zh-CN', 'ja-JP', 'ko-KR']);

    console.log('✅ Translation generation complete!');
    } else {
    console.log('✅ All translations are up to date!');
    }
    }

    main().catch(error => {
    console.error('❌ Translation generation failed:', error);
    process.exit(1);
    });

    四、AI 翻译的工程化最佳实践

    1. 翻译质量评估:使用 BLEU 评分和人工审核结合的方式确保质量。

    // 翻译质量评估
    export async function evaluateTranslationQuality(
    sourceTexts: string[],
    translatedTexts: string[],
    referenceTranslations: string[]
    ): Promise<{ bleuScore: number; suggestions: string[] }> {
    // 简化版 BLEU 评分计算
    let totalScore = 0;
    const suggestions: string[] = [];

    for (let i = 0; i < sourceTexts.length; i++) {
    const translated = translatedTexts[i];
    const reference = referenceTranslations[i];

    // 计算 n-gram 重叠度(简化逻辑)
    const translatedWords = translated.split(/\\s+/);
    const referenceWords = reference.split(/\\s+/);
    const commonWords = translatedWords.filter(w => referenceWords.includes(w));
    const score = commonWords.length / Math.max(translatedWords.length, 1);

    totalScore += score;

    if (score < 0.5) {
    suggestions.push(`Low quality translation for: ${sourceTexts[i]}`);
    }
    }

    const bleuScore = totalScore / sourceTexts.length;

    return { bleuScore, suggestions };
    }

    2. 增量翻译:只翻译新增或修改的 Keys,避免重复翻译。

    // 增量翻译支持
    export class IncrementalTranslationManager {
    private cachePath: string;

    constructor(cachePath: string = './.i18n-cache.json') {
    this.cachePath = cachePath;
    }

    /**
    * 计算需要翻译的 Keys(新增或修改后的)
    */
    async getKeysToTranslate(keys: I18nKeyInfo[]): Promise<I18nKeyInfo[]> {
    const cache = this.loadCache();
    const keysToTranslate: I18nKeyInfo[] = [];

    for (const key of keys) {
    const cached = cache[key.key];
    const currentHash = this.hashCode(key.defaultValue || key.key);

    if (!cached || cached.hash !== currentHash) {
    keysToTranslate.push(key);
    }
    }

    return keysToTranslate;
    }

    /**
    * 更新缓存
    */
    async updateCache(keys: I18nKeyInfo[]): Promise<void> {
    const cache = this.loadCache();

    for (const key of keys) {
    cache[key.key] = {
    hash: this.hashCode(key.defaultValue || key.key),
    lastTranslated: new Date().toISOString()
    };
    }

    fs.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2), 'utf-8');
    }

    private loadCache(): Record<string, { hash: number; lastTranslated: string }> {
    try {
    return JSON.parse(fs.readFileSync(this.cachePath, 'utf-8'));
    } catch {
    return {};
    }
    }

    private hashCode(str: string): number {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = ((hash << 5) – hash) + char;
    hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
    }
    }

    3. CI/CD 集成:在 CI/CD 流程中自动检测并更新翻译。

    场景描述:将 AI 翻译集成到 CI/CD 中有一个风险点值得注意——如果 CI 每次 push 都自动生成并提交翻译,但 AI 翻译结果偶然出现了严重错误(比如把否定句翻译成了肯定句),这个错误会被自动合入代码库并可能随下一次部署上线。因此,我们增加了一个缓冲区:AI 翻译的 PR 只自动创建,不自动合入。由人工或简单的规则检查(如关键术语一致性校验)通过后再合入。

    # 关键:自动创建 PR 但不自动合入
    – name: Create Translation PR
    uses: peter-evans/create-pull-request@v5
    with:
    title: 'Auto-generated i18n translations'
    body: 'AI 自动生成的翻译更新,请检查后合入'
    branch: auto/i18n-update
    labels: 'i18n, auto-generated'
    # 不自动合入,等待人工或规则检查

    这样既保证了翻译同步的效率,又避免了一次 AI 的幻觉直接把错误翻译推到线上。

    # .github/workflows/translations.yml
    name: Update Translations

    on:
    push:
    branches: [main]
    paths:
    – 'src/**'
    – 'public/locales/**'

    jobs:
    update-translations:
    runs-on: ubuntu-latest

    steps:
    – uses: actions/checkout@v3

    – name: Setup Node.js
    uses: actions/setup-node@v3
    with:
    node-version: 18

    – name: Install dependencies
    run: npm ci

    – name: Extract i18n keys
    run: npm run i18n:extract

    – name: Generate translations (AI)
    env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    run: npm run i18n:translate

    – name: Commit updated translations
    uses: stefanzweifel/git-auto-commit-action@v4
    with:
    commit_message: 'Auto-update i18n translations [skip ci]'
    file_pattern: 'public/locales/**'

    五、总结

    AI 生成前端国际化翻译能够显著降低多语言维护成本。通过自动提取 i18n Keys、调用 LLM 生成翻译、保障术语一致性,可以实现翻译文件的自动更新。

    关键实践包括:建立术语库确保一致性、增量翻译避免重复成本、翻译质量评估、CI/CD 自动化集成。这些手段能够确保翻译质量,并减少人工介入。

    对于大型项目,建议建立翻译审核流程,AI 生成初稿后由人工审核关键翻译,持续提升翻译质量。

    赞(0)
    未经允许不得转载:171主机测评 » AI 生成前端国际化翻译:从 Key 到多语言文案的全流程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址