Skip to content

Commit a29869a

Browse files
committed
fix(lyric): 修复 TTML 多语言翻译
增加 `sortTTMLTranslations` 函数来排序翻译(此函数包含 AI 生成部分),可能确实有些问题,但也是作为临时解决方案,应该在 AMLL 的 TTML 解析器支持多语言翻译后删除 fix #718
1 parent 672d62d commit a29869a

1 file changed

Lines changed: 56 additions & 2 deletions

File tree

src/core/player/LyricManager.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ class LyricManager {
405405
}
406406
if (isStale()) return;
407407
if (!ttmlContent || typeof ttmlContent !== "string") return;
408-
const parsed = parseTTML(ttmlContent);
408+
const sorted = this.sortTTMLTranslations(ttmlContent);
409+
const parsed = parseTTML(sorted);
409410
const lines = parsed?.lines || [];
410411
if (!lines.length) return;
411412
result.yrcData = lines;
@@ -488,7 +489,8 @@ class LyricManager {
488489
if (!lyric) return { lrcData: [], yrcData: [] };
489490
// TTML 直接返回
490491
if (format === "ttml") {
491-
const ttml = parseTTML(lyric);
492+
const sorted = this.sortTTMLTranslations(lyric);
493+
const ttml = parseTTML(sorted);
492494
const lines = ttml?.lines || [];
493495
statusStore.usingTTMLLyric = true;
494496
return { lrcData: [], yrcData: lines };
@@ -514,6 +516,58 @@ class LyricManager {
514516
}
515517
}
516518

519+
/**
520+
* 处理 TTML 内容并排序翻译
521+
* @param ttmlContent 原始 TTML 内容
522+
* @param translationOrder 翻译排序顺序
523+
* @returns 排序后的 TTML 内容
524+
*/
525+
// 此函数应该在 AMLL 的 TTML 解析器支持多语言翻译后删除
526+
private sortTTMLTranslations(
527+
ttmlContent: string,
528+
translationOrder: string[] = ["zh-CN", "zh-Hans", "zh-TW", "zh-Hant"],
529+
): string {
530+
// 使用 DOMParser 解析 XML 内容
531+
const parser = new DOMParser();
532+
const xmlDoc = parser.parseFromString(ttmlContent, "text/xml");
533+
534+
// 查找所有歌词行元素
535+
const lyricsElements = xmlDoc.querySelectorAll("tt > body > div > p");
536+
537+
lyricsElements.forEach((element: Element) => {
538+
// 获取当前歌词行的所有翻译元素
539+
const translationElements = Array.from(element.children).filter(
540+
(child) =>
541+
child.hasAttribute("ttm:role") && child.getAttribute("ttm:role") === "x-translation",
542+
);
543+
544+
// 按照指定顺序对翻译进行排序
545+
translationElements.sort((a, b) => {
546+
const aLang = a.getAttribute("xml:lang") || a.getAttribute("lang") || "";
547+
const bLang = b.getAttribute("xml:lang") || b.getAttribute("lang") || "";
548+
549+
const aIndex = translationOrder.findIndex((lang) =>
550+
aLang.toLowerCase().includes(lang.toLowerCase()),
551+
);
552+
const bIndex = translationOrder.findIndex((lang) =>
553+
bLang.toLowerCase().includes(lang.toLowerCase()),
554+
);
555+
556+
// 如果找不到指定语言,则放在最后
557+
return (aIndex === -1 ? Infinity : aIndex) - (bIndex === -1 ? Infinity : bIndex);
558+
});
559+
560+
// 重新排列翻译元素
561+
translationElements.forEach((translationElement) => {
562+
element.appendChild(translationElement); // 移动到末尾以实现排序
563+
});
564+
});
565+
566+
// 序列化回字符串
567+
const serializer = new XMLSerializer();
568+
return serializer.serializeToString(xmlDoc);
569+
}
570+
517571
/**
518572
* 检测本地歌词覆盖
519573
* @param id 歌曲 ID

0 commit comments

Comments
 (0)