NAC_Blockchain/services/nac-admin/server/i18nTranslation.ts

329 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* NAC知识引擎 - 多语言AI翻译服务
*
* 支持七种语言zh中文、en英文、ar阿拉伯文、ja日文、ko韩文、fr法文、ru俄文
*
* 使用OpenAI兼容接口无Manus依赖中国大陆可正常访问
* 通过环境变量配置:
* NAC_AI_API_URL - AI接口地址如 https://api.openai.com 或国内兼容端点)
* NAC_AI_API_KEY - AI接口密钥
* NAC_AI_MODEL - 模型名称(默认 gpt-3.5-turbo
*
* 阿拉伯语ar特殊处理
* - 文本方向RTL从右到左
* - 字符集Unicode阿拉伯字符块U+0600-U+06FF
* - 前端需设置 dir="rtl" 属性
*/
export const SUPPORTED_LANGUAGES = ["zh", "en", "ar", "ja", "ko", "fr", "ru"] as const;
export type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number];
export const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {
zh: "中文(简体)",
en: "English",
ar: "العربية",
ja: "日本語",
ko: "한국어",
fr: "Français",
ru: "Русский",
};
/**
* RTL语言列表从右到左书写
* 前端渲染时需要为这些语言设置 dir="rtl"
*/
export const RTL_LANGUAGES: SupportedLanguage[] = ["ar"];
/**
* 判断语言是否为RTL
*/
export function isRTL(lang: SupportedLanguage): boolean {
return RTL_LANGUAGES.includes(lang);
}
/**
* 多语言内容结构
*/
export interface MultiLangContent {
zh?: string;
en?: string;
ar?: string;
ja?: string;
ko?: string;
fr?: string;
ru?: string;
}
// ─── AI翻译接口OpenAI兼容无Manus依赖────────────────────────
/**
* 检查AI翻译服务是否已配置
*/
export function isAiTranslationConfigured(): boolean {
return !!(process.env.NAC_AI_API_URL && process.env.NAC_AI_API_KEY);
}
/**
* 调用AI接口进行翻译OpenAI兼容格式
*/
async function callAiTranslation(
systemPrompt: string,
userPrompt: string
): Promise<string> {
const apiUrl = process.env.NAC_AI_API_URL;
const apiKey = process.env.NAC_AI_API_KEY;
const model = process.env.NAC_AI_MODEL || "gpt-3.5-turbo";
if (!apiUrl || !apiKey) {
throw new Error(
"[AI翻译] 未配置AI接口。请在 .env 文件中设置 NAC_AI_API_URL 和 NAC_AI_API_KEY。\n" +
"支持任何OpenAI兼容接口如 OpenAI、Azure OpenAI、国内大模型等。"
);
}
const endpoint = `${apiUrl.replace(/\/$/, "")}/v1/chat/completions`;
const response = await fetch(endpoint, {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt },
],
max_tokens: 2048,
temperature: 0.3, // 低温度保证翻译一致性
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`AI接口调用失败: ${response.status} ${response.statusText} ${errorText}`);
}
const result = await response.json() as {
choices: Array<{ message: { content: string } }>;
};
const content = result.choices?.[0]?.message?.content;
return (typeof content === "string" ? content : "").trim();
}
/**
* 使用AI翻译单个文本到指定语言
*
* 阿拉伯语特殊说明:
* - 翻译结果为标准现代阿拉伯语MSA
* - 返回的文本为Unicode阿拉伯字符前端需设置 dir="rtl"
* - 专有名词NAC、RWA等保持英文不翻译
*/
export async function translateText(
sourceText: string,
sourceLang: SupportedLanguage,
targetLang: SupportedLanguage,
context?: string
): Promise<string> {
if (sourceLang === targetLang) return sourceText;
if (!sourceText.trim()) return sourceText;
const contextHint = context
? `\n\n背景信息仅供参考不要翻译${context}`
: "";
// 阿拉伯语特殊提示
const arabicHint = targetLang === "ar"
? "\n特别说明翻译成标准现代阿拉伯语MSA使用Unicode阿拉伯字符文本方向为从右到左RTL。"
: "";
const systemPrompt = `你是NACNewAssetChain公链的专业法律合规翻译专家。
NAC是一条专注于RWA真实世界资产的原生公链使用Charter智能合约语言、NVM虚拟机、CBPP共识协议。
你的任务是将合规规则文本从${LANGUAGE_NAMES[sourceLang]}翻译成${LANGUAGE_NAMES[targetLang]}
要求:
1. 保持法律术语的准确性和专业性
2. 保留专有名词NAC、RWA、Charter、NVM、CBPP、CSNP、CNNL、ACC-20、GNACS、XTZH不翻译
3. 保留机构名称SEC、SFC、MAS、ESMA、DFSA、DLD不翻译
4. 只返回翻译结果,不要添加任何解释或注释${arabicHint}`;
const userPrompt = `请将以下文本翻译成${LANGUAGE_NAMES[targetLang]}\n\n${sourceText}${contextHint}`;
try {
const translated = await callAiTranslation(systemPrompt, userPrompt);
return translated || sourceText;
} catch (error) {
console.error(`[Translation] 翻译到 ${targetLang} 失败:`, (error as Error).message);
return sourceText; // 降级返回原文
}
}
/**
* 为合规规则生成完整的七语言翻译
* @param ruleName 规则名称(源语言)
* @param description 规则描述(源语言)
* @param sourceLang 源语言
* @param existingTranslations 已有的翻译(跳过已有的)
*/
export async function generateRuleTranslations(
ruleName: string,
description: string,
sourceLang: SupportedLanguage = "zh",
existingTranslations?: { ruleNameI18n?: MultiLangContent; descriptionI18n?: MultiLangContent }
): Promise<{ ruleNameI18n: MultiLangContent; descriptionI18n: MultiLangContent }> {
const ruleNameI18n: MultiLangContent = { ...(existingTranslations?.ruleNameI18n || {}) };
const descriptionI18n: MultiLangContent = { ...(existingTranslations?.descriptionI18n || {}) };
// 设置源语言内容
ruleNameI18n[sourceLang] = ruleName;
descriptionI18n[sourceLang] = description;
// 并行翻译所有缺失的语言
const targetLangs = SUPPORTED_LANGUAGES.filter(
(lang) => lang !== sourceLang && !ruleNameI18n[lang]
);
await Promise.all(
targetLangs.map(async (targetLang) => {
const [translatedName, translatedDesc] = await Promise.all([
translateText(ruleName, sourceLang, targetLang, `这是一条${description.slice(0, 50)}...的合规规则`),
translateText(description, sourceLang, targetLang),
]);
ruleNameI18n[targetLang] = translatedName;
descriptionI18n[targetLang] = translatedDesc;
})
);
return { ruleNameI18n, descriptionI18n };
}
/**
* 批量迁移现有规则,为其生成多语言翻译
*/
export async function migrateRuleToMultiLang(rule: {
ruleName: string;
description: string;
ruleNameI18n?: MultiLangContent;
descriptionI18n?: MultiLangContent;
}): Promise<{ ruleNameI18n: MultiLangContent; descriptionI18n: MultiLangContent }> {
const sourceLang: SupportedLanguage = "zh";
return generateRuleTranslations(
rule.ruleName,
rule.description,
sourceLang,
{ ruleNameI18n: rule.ruleNameI18n, descriptionI18n: rule.descriptionI18n }
);
}
/**
* 阿拉伯语RTL测试用例
* 用于验证阿拉伯语翻译质量和RTL布局
*/
export const ARABIC_RTL_TEST_CASES = [
{
id: "ar-rtl-001",
sourceLang: "zh" as SupportedLanguage,
targetLang: "ar" as SupportedLanguage,
sourceText: "不动产登记证要求",
expectedContains: ["عقار", "تسجيل", "شهادة"], // 应包含房产/登记/证书相关词汇
isRTL: true,
description: "房产登记证要求阿拉伯语RTL测试",
},
{
id: "ar-rtl-002",
sourceLang: "en" as SupportedLanguage,
targetLang: "ar" as SupportedLanguage,
sourceText: "RWA asset compliance verification",
expectedContains: ["امتثال", "أصول", "التحقق"], // 应包含合规/资产/验证相关词汇
isRTL: true,
description: "RWA资产合规验证阿拉伯语RTL测试",
},
{
id: "ar-rtl-003",
sourceLang: "zh" as SupportedLanguage,
targetLang: "ar" as SupportedLanguage,
sourceText: "NAC公链智能合约审批流程",
expectedContains: ["NAC", "عقد", "موافقة"], // NAC不翻译包含合约/审批相关词汇
isRTL: true,
description: "NAC专有名词保留测试阿拉伯语RTL",
},
];
/**
* 执行阿拉伯语RTL专项测试
* 返回测试结果报告
*/
export async function runArabicRTLTests(): Promise<{
passed: number;
failed: number;
results: Array<{
id: string;
description: string;
passed: boolean;
translated: string;
isRTL: boolean;
issues: string[];
}>;
}> {
if (!isAiTranslationConfigured()) {
return {
passed: 0,
failed: ARABIC_RTL_TEST_CASES.length,
results: ARABIC_RTL_TEST_CASES.map(tc => ({
id: tc.id,
description: tc.description,
passed: false,
translated: "",
isRTL: true,
issues: ["AI翻译服务未配置请设置 NAC_AI_API_URL 和 NAC_AI_API_KEY"],
})),
};
}
const results = await Promise.all(
ARABIC_RTL_TEST_CASES.map(async (tc) => {
const issues: string[] = [];
let translated = "";
try {
translated = await translateText(tc.sourceText, tc.sourceLang, tc.targetLang);
// 检查是否包含阿拉伯字符Unicode范围 U+0600-U+06FF
const hasArabicChars = /[\u0600-\u06FF]/.test(translated);
if (!hasArabicChars) {
issues.push("翻译结果不包含阿拉伯字符");
}
// 检查专有名词是否保留NAC不应被翻译
if (tc.sourceText.includes("NAC") && !translated.includes("NAC")) {
issues.push("专有名词NAC被错误翻译应保留英文");
}
// 检查是否为空
if (!translated.trim()) {
issues.push("翻译结果为空");
}
} catch (error) {
issues.push(`翻译失败: ${(error as Error).message}`);
}
return {
id: tc.id,
description: tc.description,
passed: issues.length === 0,
translated,
isRTL: tc.isRTL,
issues,
};
})
);
const passed = results.filter(r => r.passed).length;
const failed = results.filter(r => !r.passed).length;
return { passed, failed, results };
}