Skip to content

Commit 04743e8

Browse files
authored
Merge pull request #719 from MoYingJi/pr/p
fix(lyric): 修复 TTML 多语言翻译
2 parents fe882f7 + 99084df commit 04743e8

1 file changed

Lines changed: 57 additions & 2 deletions

File tree

src/core/player/LyricManager.ts

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@ class LyricManager {
415415
}
416416
if (isStale()) return;
417417
if (!ttmlContent || typeof ttmlContent !== "string") return;
418-
const parsed = parseTTML(ttmlContent);
418+
const sorted = this.sortTTMLTranslations(ttmlContent);
419+
const parsed = parseTTML(sorted);
419420
const lines = parsed?.lines || [];
420421
if (!lines.length) return;
421422
result.yrcData = lines;
@@ -498,7 +499,8 @@ class LyricManager {
498499
if (!lyric) return { lrcData: [], yrcData: [] };
499500
// TTML 直接返回
500501
if (format === "ttml") {
501-
const ttml = parseTTML(lyric);
502+
const sorted = this.sortTTMLTranslations(lyric);
503+
const ttml = parseTTML(sorted);
502504
const lines = ttml?.lines || [];
503505
statusStore.usingTTMLLyric = true;
504506
return { lrcData: [], yrcData: lines };
@@ -524,6 +526,59 @@ class LyricManager {
524526
}
525527
}
526528

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

0 commit comments

Comments
 (0)