506 lines
24 KiB
JavaScript
506 lines
24 KiB
JavaScript
// process/tables.js
|
||
|
||
/**
|
||
* 在翻译前对Markdown表格进行特殊处理,将其替换为占位符,以确保表格结构在翻译过程中保持完整性。
|
||
*
|
||
* 主要步骤:
|
||
* 1. **预处理**:
|
||
* - 标准化换行符为 `\n`。
|
||
* - 移除每行表格前可能存在的影响识别的行首空格或制表符。
|
||
* 2. **表格边界检测**:
|
||
* - 使用更可靠的方法检测表格:寻找连续的表格行(包括表头、分隔行和数据行)。
|
||
* - 定义表格行 (`tableRowRegex`) 和表格分隔行 (`tableSepRegex`) 的正则表达式。
|
||
* - 扫描文本行,识别潜在的表格标题(以 "TABLE", "Table", "表" 开头且下一行是表格行的行)。
|
||
* 3. **表格范围确定**:
|
||
* - 遍历文本行,标记表格的开始和结束行号,存入 `tableRanges`。
|
||
* - 考虑最小有效表格行数 (`minTableRows`),避免将非表格内容误认为表格。
|
||
* - 如果表格前有识别到的标题,则将标题行也包含在表格范围内。
|
||
* - 特殊处理文档末尾的表格。
|
||
* 4. **合并相邻表格**:
|
||
* - 遍历 `tableRanges`,如果两个表格范围相邻或仅由空行/特定注释行隔开,则将它们合并为一个范围。
|
||
* 这有助于处理因 Markdown 解析不完美或原始文档格式问题导致的表格被错误分割的情况。
|
||
* 5. **提取表格并替换为占位符**:
|
||
* - 从后向前遍历 `tableRanges`(避免替换影响后续行号的准确性)。
|
||
* - 对每个表格范围,提取其完整的 Markdown 内容。
|
||
* - 生成唯一的占位符,如 `__TABLE_PLACEHOLDER_0__`。
|
||
* - 将原始表格内容存储在 `tablePlaceholders` 对象中,键为占位符,值为表格 Markdown 文本。
|
||
* - 在原始文本 (`processedText`) 中,用占位符替换掉实际的表格内容。
|
||
* - 同时更新 `lines` 数组(用于内部处理),将表格内容替换为占位符,以便后续步骤的正确索引。
|
||
* 6. **返回结果**:返回一个对象,包含:
|
||
* - `processedText`:表格已被占位符替换的 Markdown 文本。
|
||
* - `tablePlaceholders`:一个映射对象,键是占位符,值是对应的原始表格 Markdown 内容。
|
||
*
|
||
* @param {string} markdown - 原始的 Markdown 文本内容。
|
||
* @returns {Object} 一个包含两部分的对象:
|
||
* `{ processedText: string, tablePlaceholders: Object }`。
|
||
*/
|
||
function protectMarkdownTables(markdown) {
|
||
// 预处理:标准化换行符并确保每行表格前没有空格影响识别
|
||
let normalizedMarkdown = markdown.replace(/\r\n/g, '\n').replace(/^[ \t]+(\|[\s\S]+)$/gm, '$1');
|
||
|
||
// 首先检测所有表格边界
|
||
// 使用一种更可靠的表格检测方法:寻找连续的表格行(包括表头、分隔行和数据行)
|
||
|
||
// 表格相关的正则表达式
|
||
const tableRowRegex = /^\s*\|.*\|\s*$/; // 表格行: | 内容 |
|
||
const tableSepRegex = /^\s*\|[\s\-:]+\|\s*$/; // 表格分隔行: | --:-- |
|
||
|
||
// 按行分割文本
|
||
const lines = normalizedMarkdown.split('\n');
|
||
const tableRanges = []; // 存储表格的范围 [开始行, 结束行]
|
||
|
||
// 查找所有可能的表格标题
|
||
const tableTitles = {}; // 行号 -> 标题文本
|
||
for (let i = 0; i < lines.length; i++) {
|
||
if ((lines[i].trim().startsWith('TABLE') ||
|
||
lines[i].trim().startsWith('Table') ||
|
||
lines[i].trim().startsWith('表')) &&
|
||
i < lines.length - 1 &&
|
||
tableRowRegex.test(lines[i+1])) {
|
||
tableTitles[i] = lines[i];
|
||
}
|
||
}
|
||
|
||
// 扫描查找所有表格的范围
|
||
let inTable = false;
|
||
let tableStart = -1;
|
||
let minTableRows = 3; // 最小的有效表格行数
|
||
|
||
for (let i = 0; i < lines.length; i++) {
|
||
const line = lines[i];
|
||
const isTableRow = tableRowRegex.test(line);
|
||
|
||
if (!inTable && isTableRow) {
|
||
// 找到表格开始
|
||
tableStart = i;
|
||
inTable = true;
|
||
} else if (inTable && !isTableRow) {
|
||
// 找到表格结束
|
||
if (i - tableStart >= minTableRows) {
|
||
// 表格有足够多的行,是有效表格
|
||
// 检查是否有表格标题
|
||
const titleIndex = tableStart - 1;
|
||
if (tableTitles[titleIndex]) {
|
||
tableRanges.push([titleIndex, i - 1]);
|
||
delete tableTitles[titleIndex]; // 已使用此标题
|
||
} else {
|
||
tableRanges.push([tableStart, i - 1]);
|
||
}
|
||
}
|
||
inTable = false;
|
||
tableStart = -1;
|
||
}
|
||
}
|
||
|
||
// 处理文档尾部的表格
|
||
if (inTable && lines.length - tableStart >= minTableRows) {
|
||
const titleIndex = tableStart - 1;
|
||
if (tableTitles[titleIndex]) {
|
||
tableRanges.push([titleIndex, lines.length - 1]);
|
||
delete tableTitles[titleIndex];
|
||
} else {
|
||
tableRanges.push([tableStart, lines.length - 1]);
|
||
}
|
||
}
|
||
|
||
// 合并相邻的表格(处理错误分割的表格)
|
||
if (tableRanges.length > 1) {
|
||
const mergedRanges = [tableRanges[0]];
|
||
for (let i = 1; i < tableRanges.length; i++) {
|
||
const lastRange = mergedRanges[mergedRanges.length - 1];
|
||
const currentRange = tableRanges[i];
|
||
|
||
// 检查两个表格是否相邻或只隔了一个空行或注释行
|
||
if (currentRange[0] - lastRange[1] <= 2) {
|
||
// 检查中间行是否为注释行或空行
|
||
const middleLines = lines.slice(lastRange[1] + 1, currentRange[0]);
|
||
const areAllMiddleLinesNonTable = middleLines.every(line => {
|
||
return line.trim() === '' ||
|
||
line.trim().startsWith('^') ||
|
||
line.trim().startsWith('*') ||
|
||
line.trim().startsWith('$') ||
|
||
/^\s*\[\^\d+\]:/.test(line.trim()) ||
|
||
line.trim().match(/^[a-zA-Z]?\s*\{\s*\^\s*[a-z]+\s*\}/) ||
|
||
line.trim().match(/^\$\{\s*\^\s*\\?[a-z]+\s*\}\$/);
|
||
});
|
||
|
||
if (areAllMiddleLinesNonTable) {
|
||
// 合并这两个表格范围
|
||
lastRange[1] = currentRange[1];
|
||
} else {
|
||
mergedRanges.push(currentRange);
|
||
}
|
||
} else {
|
||
mergedRanges.push(currentRange);
|
||
}
|
||
}
|
||
tableRanges.length = 0;
|
||
tableRanges.push(...mergedRanges);
|
||
}
|
||
|
||
// 提取表格并替换为占位符
|
||
let tableCounter = 0;
|
||
const tablePlaceholders = {};
|
||
let processedText = normalizedMarkdown;
|
||
|
||
// 从后向前处理,避免替换影响索引
|
||
for (let i = tableRanges.length - 1; i >= 0; i--) {
|
||
const [start, end] = tableRanges[i];
|
||
const tableLines = lines.slice(start, end + 1);
|
||
const tableContent = tableLines.join('\n');
|
||
|
||
// 生成占位符
|
||
const placeholder = `__TABLE_PLACEHOLDER_${tableCounter}__`;
|
||
tablePlaceholders[placeholder] = tableContent;
|
||
tableCounter++;
|
||
|
||
// 替换原文中的表格内容
|
||
const beforeTable = lines.slice(0, start).join('\n');
|
||
const afterTable = lines.slice(end + 1).join('\n');
|
||
processedText = beforeTable + (beforeTable ? '\n' : '') +
|
||
placeholder +
|
||
(afterTable ? '\n' : '') + afterTable;
|
||
|
||
// 更新lines数组,以便后续处理
|
||
lines.splice(start, end - start + 1, placeholder);
|
||
}
|
||
|
||
// 为日志添加识别结果
|
||
console.log(`表格识别完成:找到 ${tableCounter} 个表格`);
|
||
|
||
return {
|
||
processedText,
|
||
tablePlaceholders
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 从大语言模型翻译返回的文本中提取并清理出 Markdown 表格内容。
|
||
* 模型返回的表格有时可能被包裹在代码块中,或者包含额外的解释性文字,此函数旨在尽可能准确地提取核心表格。
|
||
*
|
||
* 主要步骤:
|
||
* 1. **初步清理**:移除字符串首尾的空格。
|
||
* 2. **移除代码块标记**:如果文本以 ` ``` `开始和结束,则移除这些标记。
|
||
* - 如果代码块内部有语言标识 (如 `markdown` 或 `md`),也一并移除。
|
||
* 3. **提取潜在表格标题**:检查清理后文本的第一行是否以 "TABLE" 或 "表" 开头,如果是,则将其视为表格标题并暂存。
|
||
* 4. **提取表格行**:
|
||
* - 遍历剩余的文本行。
|
||
* - 如果一行以 `|` 开头,则认为进入了表格内容,将其加入 `tableLines` 数组。
|
||
* - 如果已在表格内部,但当前行不以 `|` 开头:
|
||
* - 若该行为空行或包含表格分隔符特征 (`---`),则仍视为表格的一部分。
|
||
* - 否则,认为表格内容结束。
|
||
* 5. **组合结果**:将提取到的标题行(如果有)和表格行重新组合成完整的表格 Markdown 文本。
|
||
* 6. **返回结果**:如果成功提取到表格内容,则返回该内容;否则返回 `null`。
|
||
*
|
||
* @param {string} translatedText - 从翻译 API 收到的原始响应文本,可能包含表格。
|
||
* @returns {string|null} 清理并提取出的 Markdown 表格文本。如果未找到有效表格结构,则返回 `null`。
|
||
*/
|
||
function extractTableFromTranslation(translatedText) {
|
||
// 清理可能的引号或代码块
|
||
let cleanedText = translatedText.trim();
|
||
|
||
// 移除可能的代码块标记
|
||
if (cleanedText.startsWith('```') && cleanedText.endsWith('```')) {
|
||
cleanedText = cleanedText.substring(3, cleanedText.length - 3).trim();
|
||
|
||
// 可能的语言标识
|
||
if (cleanedText.startsWith('markdown') || cleanedText.startsWith('md')) {
|
||
cleanedText = cleanedText.substring(cleanedText.indexOf('\n')).trim();
|
||
}
|
||
}
|
||
|
||
// 检查是否有表格标题 - 以TABLE或表开头的行
|
||
let titleLine = '';
|
||
const lines = cleanedText.split('\n');
|
||
const firstLine = lines[0].trim();
|
||
if (firstLine.startsWith('TABLE') || firstLine.startsWith('表')) {
|
||
titleLine = lines.shift();
|
||
}
|
||
|
||
// 提取表格内容 - 以|开头的连续行
|
||
const tableLines = [];
|
||
let inTable = false;
|
||
|
||
for (const line of lines) {
|
||
const trimmedLine = line.trim();
|
||
if (trimmedLine.startsWith('|')) {
|
||
inTable = true;
|
||
tableLines.push(line);
|
||
} else if (inTable) {
|
||
// 如果已经在表格内,但当前行不以|开头,检查是否是表格分隔行或空行
|
||
if (trimmedLine === '' || trimmedLine.includes('---')) {
|
||
tableLines.push(line);
|
||
} else {
|
||
// 真正的非表格内容,表格结束
|
||
inTable = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 组合标题和表格内容
|
||
const result = titleLine ? (titleLine + '\n' + tableLines.join('\n')) : tableLines.join('\n');
|
||
return result.length > 0 ? result : null;
|
||
}
|
||
|
||
/**
|
||
* 在翻译后的文本中恢复 Markdown 表格,并将原始表格内容(存储在占位符中的)进行翻译后再替换回去。
|
||
* 此函数旨在确保表格结构在整个翻译流程中保持不变,同时表格内的文本得到正确翻译。
|
||
*
|
||
* 主要步骤:
|
||
* 1. **收集待翻译表格**:遍历 `tablePlaceholders` 对象,将每个占位符及其对应的原始表格内容收集到 `tablesToTranslate` 数组中。
|
||
* 2. **批量翻译表格 (如果提供了 API 配置)**:
|
||
* - 检查是否提供了 `apiConfig` 和 `targetLang`。如果未提供或没有需要翻译的表格,则跳过翻译步骤,直接用原始表格替换占位符。
|
||
* - **构建专用提示词**:为表格翻译创建特定的系统提示 (`tableSystemPrompt`) 和用户提示 (`tableUserPrompt`)。
|
||
* 这些提示词强调保持表格结构(分隔符 `|`, 对齐标记 `:--:`, 行列数, 数学公式等)不变,仅翻译文本内容。
|
||
* - **逐个翻译表格**:
|
||
* - 对 `tablesToTranslate` 中的每个表格:
|
||
* - 使用 `apiConfig.bodyBuilder` 构建请求体。
|
||
* - 调用 `callTranslationApi` 发送翻译请求。
|
||
* - 使用 `extractTableFromTranslation` 从翻译结果中提取并清理表格内容。
|
||
* - 如果成功提取到翻译后的表格 (`cleanedTable`),则在主文本 (`result`) 中用它替换掉对应的占位符。
|
||
* - 如果提取失败或翻译出错,则记录警告/错误,并使用原始表格内容替换占位符作为兜底。
|
||
* 3. **直接恢复原始表格 (如果未提供 API 配置或无表格)**:
|
||
* - 如果跳过了翻译步骤,则直接遍历 `tablePlaceholders`,用原始表格内容替换主文本中的占位符。
|
||
* 4. **返回结果**:返回已恢复(并可能已翻译)表格的完整 Markdown 文本。
|
||
*
|
||
* @param {string} translatedText - 包含表格占位符的、已经过初步翻译的 Markdown 文本。
|
||
* @param {Object} tablePlaceholders - 一个对象,键是表格占位符 (如 `__TABLE_PLACEHOLDER_0__`),值是对应的原始表格 Markdown 内容。
|
||
* @param {Object} [apiConfig=null] - (可选) 用于翻译表格的 API 配置对象。如果为 `null`,表格将不被翻译,直接用原文恢复。
|
||
* @param {string} [targetLang=null] - (可选) 目标翻译语言。与 `apiConfig` 一同提供时用于翻译表格。
|
||
* @param {string} [model=null] - (可选, 未直接使用,但暗示了 apiConfig 的来源) 使用的模型名称。
|
||
* @param {string} [apiKey=null] - (可选, 未直接使用,但暗示了 apiConfig 的来源) API 密钥。
|
||
* @param {string} [logContext=""] - (可选) 日志记录的上下文前缀。
|
||
* @returns {Promise<string>} 已恢复表格(内容可能已翻译)的 Markdown 文本。
|
||
*/
|
||
async function restoreMarkdownTables(translatedText, tablePlaceholders, apiConfig = null, targetLang = null, model = null, apiKey = null, logContext = "") {
|
||
let result = translatedText;
|
||
const tablesToTranslate = [];
|
||
|
||
// 第一步:收集所有需要翻译的表格
|
||
for (const [placeholder, tableContent] of Object.entries(tablePlaceholders)) {
|
||
tablesToTranslate.push({
|
||
placeholder,
|
||
content: tableContent
|
||
});
|
||
}
|
||
|
||
// 第二步:批量翻译所有表格(如果提供了API配置)
|
||
if (apiConfig && targetLang && tablesToTranslate.length > 0) {
|
||
if (typeof addProgressLog === "function") {
|
||
addProgressLog(`${logContext} 准备翻译 ${tablesToTranslate.length} 个表格...`);
|
||
}
|
||
|
||
// 为表格翻译构建专门的系统提示词
|
||
const tableSystemPrompt = `你是一个精确翻译表格的助手。请将表格翻译成${targetLang},严格保持以下格式要求:
|
||
1. 保持所有表格分隔符(|)和结构完全不变
|
||
2. 保持表格对齐标记(:--:、:--、--:)不变
|
||
3. 保持表格的行数和列数完全一致
|
||
4. 保持数学公式、符号和百分比等专业内容不变
|
||
5. 翻译表格标题(如有)和表格内的文本内容
|
||
6. 表格内容与表格外内容要明确区分`;
|
||
|
||
for (let i = 0; i < tablesToTranslate.length; i++) {
|
||
const table = tablesToTranslate[i];
|
||
try {
|
||
if (typeof addProgressLog === "function") {
|
||
addProgressLog(`${logContext} 正在翻译第 ${i+1}/${tablesToTranslate.length} 个表格...`);
|
||
}
|
||
|
||
// 用户提示词
|
||
const tableUserPrompt = `请将以下Markdown表格翻译成${targetLang},请确保完全保持表格结构和格式:
|
||
|
||
${table.content}
|
||
|
||
注意:请保持表格格式完全不变,包括所有的 | 符号、对齐标记、数学公式和符号。`;
|
||
|
||
// 构建请求体
|
||
const requestBody = apiConfig.bodyBuilder
|
||
? apiConfig.bodyBuilder(tableSystemPrompt, tableUserPrompt)
|
||
: {
|
||
model: apiConfig.modelName,
|
||
messages: [
|
||
{ role: "system", content: tableSystemPrompt },
|
||
{ role: "user", content: tableUserPrompt }
|
||
]
|
||
};
|
||
|
||
// 调用API翻译表格
|
||
const translatedTable = await callTranslationApi(apiConfig, requestBody);
|
||
|
||
// 提取和清理翻译结果中的表格部分
|
||
const cleanedTable = extractTableFromTranslation(translatedTable);
|
||
|
||
if (cleanedTable) {
|
||
// 替换原始占位符为翻译后的表格
|
||
result = result.replace(table.placeholder, cleanedTable);
|
||
} else {
|
||
// 如果没有提取到表格,使用原始表格
|
||
console.warn(`${logContext} 无法从翻译结果中提取表格结构,将使用原始表格`);
|
||
result = result.replace(table.placeholder, table.content);
|
||
}
|
||
} catch (tableError) {
|
||
console.error(`表格翻译失败:`, tableError);
|
||
if (typeof addProgressLog === "function") {
|
||
addProgressLog(`${logContext} 表格 ${i+1} 翻译失败: ${tableError.message},将使用原表格`);
|
||
}
|
||
// 如果翻译失败,使用原表格
|
||
result = result.replace(table.placeholder, table.content);
|
||
}
|
||
}
|
||
} else {
|
||
// 没有提供翻译配置,直接恢复原表格
|
||
for (const [placeholder, tableContent] of Object.entries(tablePlaceholders)) {
|
||
result = result.replace(placeholder, tableContent);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 诊断并尝试修复 Markdown 表格的常见格式问题。
|
||
* 此函数主要关注表头与分隔行的一致性,以及数据行列数与表头的一致性。
|
||
*
|
||
* 主要步骤:
|
||
* 1. **预处理**:按行分割表格内容,移除空行和行首尾空格。
|
||
* 2. **基本检查**:如果行数少于3行(表头、分隔、至少一行数据),则认为不是有效表格或过于简单,直接返回原内容。
|
||
* 3. **表头分析**:分割表头行,计算列数 (`columnCount`)。
|
||
* 4. **分隔行修复**:
|
||
* - 检查分隔行 (`lines[1]`) 是否包含必要的 `-` 和 `|` 字符。
|
||
* - 如果格式明显错误(如缺少 `-`),则根据 `columnCount` 生成一个标准的分隔行 (`| --- | --- | ... |`) 并替换原分隔行。
|
||
* - 如果格式基本正确,但其单元格数量与表头不匹配,也重新生成标准分隔行并替换。
|
||
* 5. **数据行修复**:
|
||
* - 遍历从第三行开始的所有数据行。
|
||
* - 分割每行数据,计算其单元格数量。
|
||
* - 如果单元格数量与 `columnCount` 不匹配,则尝试通过添加或截断单元格来修复该行,使其列数与表头一致。
|
||
* (当前实现是补齐空单元格 `| |`)。
|
||
* 6. **返回结果**:返回修复后的表格内容(行通过 `\n` 连接)。
|
||
*
|
||
* @param {string} tableContent - 存在格式问题的 Markdown 表格文本。
|
||
* @returns {string} 尝试修复后的 Markdown 表格文本。
|
||
*/
|
||
function diagnoseAndFixTableFormat(tableContent) {
|
||
// 按行分割表格
|
||
const lines = tableContent.split('\n').map(l => l.trim()).filter(l => l);
|
||
|
||
if (lines.length < 3) {
|
||
console.log("表格行数不足");
|
||
return tableContent; // 行数不足,可能不是表格
|
||
}
|
||
|
||
// 检查第一行(表头)
|
||
const headerRow = lines[0];
|
||
const headerCells = headerRow.split('|').filter(cell => cell.trim() !== '');
|
||
const columnCount = headerCells.length;
|
||
|
||
// 检查第二行(分隔行)
|
||
const separatorRow = lines[1];
|
||
// 如果分隔行不包含足够的"-",可能是格式错误
|
||
if (!separatorRow.includes('-') || !separatorRow.includes('|')) {
|
||
console.log("分隔行格式错误,尝试修复");
|
||
|
||
// 创建正确的分隔行
|
||
let newSeparatorRow = '|';
|
||
for (let i = 0; i < columnCount; i++) {
|
||
newSeparatorRow += ' --- |';
|
||
}
|
||
|
||
// 插入新的分隔行
|
||
lines.splice(1, 1, newSeparatorRow);
|
||
} else {
|
||
// 检查分隔行的列数是否与表头匹配
|
||
const separatorCells = separatorRow.split('|').filter(cell => cell.trim() !== '');
|
||
if (separatorCells.length !== columnCount) {
|
||
console.log("分隔行列数与表头不匹配,尝试修复");
|
||
|
||
// 创建正确的分隔行
|
||
let newSeparatorRow = '|';
|
||
for (let i = 0; i < columnCount; i++) {
|
||
newSeparatorRow += ' --- |';
|
||
}
|
||
|
||
// 替换分隔行
|
||
lines.splice(1, 1, newSeparatorRow);
|
||
}
|
||
}
|
||
|
||
// 检查并修复所有数据行
|
||
for (let i = 2; i < lines.length; i++) {
|
||
const dataRow = lines[i];
|
||
const dataCells = dataRow.split('|').filter(cell => cell.trim() !== '');
|
||
|
||
// 如果数据行的列数与表头不匹配,进行修复
|
||
if (dataCells.length !== columnCount) {
|
||
console.log(`行 ${i+1} 列数与表头不匹配,尝试修复`);
|
||
|
||
// 创建正确的数据行
|
||
let newDataRow = '|';
|
||
for (let j = 0; j < columnCount; j++) {
|
||
newDataRow += (j < dataCells.length ? ` ${dataCells[j].trim()} |` : ' |');
|
||
}
|
||
|
||
// 替换数据行
|
||
lines.splice(i, 1, newDataRow);
|
||
}
|
||
}
|
||
|
||
return lines.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 尝试修复并验证 Markdown 表格的格式,特别是处理对齐标记行错位的问题。
|
||
* 此函数首先调用 `diagnoseAndFixTableFormat` 进行初步的格式修复,
|
||
* 然后专门检查是否存在多行对齐标记或对齐标记行不在第二行(分隔行)的情况。
|
||
*
|
||
* 主要步骤:
|
||
* 1. **初步修复**:调用 `diagnoseAndFixTableFormat(tableContent)` 对表格进行基础的格式修正。
|
||
* 2. **对齐标记行检查**:
|
||
* - 将修复后的表格按行分割,并过滤掉空行。
|
||
* - 查找所有包含对齐标记(`:--:`, `:--`, `--:`)的行 (`alignmentLines`)。
|
||
* - **错位处理**:如果找到了对齐标记行,并且表格的第二行(预期的分隔行位置)并不包含连字符 `-`(表明它可能不是一个正常的分隔行):
|
||
* - 找到第一个包含对齐标记的行的索引 (`alignmentLineIndex`)。
|
||
* - 如果该对齐标记行不在第二行 (`alignmentLineIndex > 1`),则将其移动到第二行的位置,即先删除原来的对齐标记行,然后在第二行处插入它。
|
||
* 3. **返回结果**:返回经过上述处理(可能被进一步修复)的表格 Markdown 文本。
|
||
*
|
||
* @param {string} tableContent - 可能包含格式问题(尤其是对齐标记错位)的 Markdown 表格文本。
|
||
* @returns {string} 修复后的 Markdown 表格文本。
|
||
*/
|
||
function fixTableFormat(tableContent) {
|
||
// 先尝试基本的修复
|
||
let fixedTable = diagnoseAndFixTableFormat(tableContent);
|
||
|
||
// 检测是否有特殊的对齐标记行放在错误位置的情况
|
||
const lines = fixedTable.split('\n').map(l => l.trim()).filter(l => l);
|
||
|
||
// 检查是否有多行包含对齐标记
|
||
const alignmentLines = lines.filter(line =>
|
||
line.includes(':--:') || line.includes(':--') || line.includes('--:')
|
||
);
|
||
|
||
if (alignmentLines.length > 0 && !lines[1].includes('-')) {
|
||
// 找到第一个包含对齐标记的行
|
||
const alignmentLineIndex = lines.findIndex(line =>
|
||
line.includes(':--:') || line.includes(':--') || line.includes('--:')
|
||
);
|
||
|
||
if (alignmentLineIndex > 1) {
|
||
// 移动对齐行到正确位置
|
||
const alignmentLine = lines[alignmentLineIndex];
|
||
lines.splice(alignmentLineIndex, 1);
|
||
lines.splice(1, 0, alignmentLine);
|
||
|
||
fixedTable = lines.join('\n');
|
||
}
|
||
}
|
||
|
||
return fixedTable;
|
||
}
|
||
|
||
// 将函数添加到processModule对象
|
||
if (typeof processModule !== 'undefined') {
|
||
processModule.protectMarkdownTables = protectMarkdownTables;
|
||
processModule.extractTableFromTranslation = extractTableFromTranslation;
|
||
processModule.restoreMarkdownTables = restoreMarkdownTables;
|
||
processModule.diagnoseAndFixTableFormat = diagnoseAndFixTableFormat;
|
||
processModule.fixTableFormat = fixTableFormat;
|
||
} |