优化vite翻译插件
This commit is contained in:
parent
50d96b4f8e
commit
bbacb2deb6
@ -26,17 +26,51 @@ const CONSTANTS = {
|
||||
* @param {string} closeChar - 闭括号字符
|
||||
* @returns {number} 闭括号的位置,未找到返回 -1
|
||||
*/
|
||||
/**
|
||||
* 查找平衡括号的结束位置,正确处理字符串字面量中的括号
|
||||
* @param {string} code - 代码字符串
|
||||
* @param {number} startPos - 起始位置(开括号之后)
|
||||
* @param {string} openChar - 开括号字符
|
||||
* @param {string} closeChar - 闭括号字符
|
||||
* @returns {number} 闭括号的位置,未找到返回 -1
|
||||
*/
|
||||
function findBalancedBracket(code, startPos, openChar = '(', closeChar = ')') {
|
||||
let depth = 1;
|
||||
let pos = startPos;
|
||||
let inString = false;
|
||||
let stringChar = null;
|
||||
|
||||
while (pos < code.length && depth > 0) {
|
||||
const char = code[pos];
|
||||
if (char === openChar) depth++;
|
||||
else if (char === closeChar) {
|
||||
depth--;
|
||||
if (depth === 0) return pos;
|
||||
|
||||
// 处理字符串字面量
|
||||
if (!inString && (char === '"' || char === "'")) {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
} else if (inString && char === stringChar) {
|
||||
// 检查是否是转义的引号:计算连续的反斜杠数量
|
||||
let backslashCount = 0;
|
||||
let checkPos = pos - 1;
|
||||
while (checkPos >= 0 && code[checkPos] === '\\') {
|
||||
backslashCount++;
|
||||
checkPos--;
|
||||
}
|
||||
// 如果反斜杠数量是偶数,则引号未转义,字符串结束
|
||||
if (backslashCount % 2 === 0) {
|
||||
inString = false;
|
||||
stringChar = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 只在非字符串中计算括号深度
|
||||
if (!inString) {
|
||||
if (char === openChar) depth++;
|
||||
else if (char === closeChar) {
|
||||
depth--;
|
||||
if (depth === 0) return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
@ -189,12 +223,14 @@ function processVueSFC(code, translations) {
|
||||
|
||||
if (depth === 0 && templateContent) {
|
||||
let replacedContent = templateContent;
|
||||
replacedContent = replaceInterpolations(replacedContent, translations);
|
||||
// 先处理属性绑定(包括带参数的),避免 replaceInterpolations 误处理
|
||||
replacedContent = replaceAttributeBindings(replacedContent, translations);
|
||||
// 处理属性绑定中的数组字面量,如 :items="[{ label: $t('key'), ... }]"
|
||||
replacedContent = replaceAttributeArrayLiterals(replacedContent, translations);
|
||||
// 处理属性绑定中的对象字面量,如 :options="{ title: $t('key'), ... }"
|
||||
replacedContent = replaceAttributeObjectLiterals(replacedContent, translations);
|
||||
// 最后处理插值表达式
|
||||
replacedContent = replaceInterpolations(replacedContent, translations);
|
||||
|
||||
code = code.substring(0, startIndex) + openTag + replacedContent + CONSTANTS.VUE_TEMPLATE_CLOSE_TAG +
|
||||
code.substring(startIndex + openTag.length + templateContent.length + CONSTANTS.VUE_TEMPLATE_CLOSE_TAG_LEN);
|
||||
@ -239,9 +275,18 @@ function replaceInterpolations(code, translations) {
|
||||
|
||||
// 处理插值表达式内部的 $t() 调用(不在单独的 {{ $t(...) }} 中)
|
||||
// 例如:{{ condition ? $t('key1') : $t('key2') }}
|
||||
// 注意:跳过属性绑定中的内容(它们会在 replaceAttributeBindings 中处理)
|
||||
code = code.replace(
|
||||
/\{\{[\s\S]*?\}\}/g,
|
||||
(interpolation) => {
|
||||
(interpolation, offset, string) => {
|
||||
// 检查这个插值是否在属性绑定中(前后有 = 和引号)
|
||||
const beforeMatch = string.substring(Math.max(0, offset - 20), offset);
|
||||
const afterMatch = string.substring(offset + interpolation.length, Math.min(string.length, offset + interpolation.length + 20));
|
||||
if (/[=:]["']\s*$/.test(beforeMatch) || /^["']\s*[>\/\s]/.test(afterMatch)) {
|
||||
// 在属性绑定中,跳过处理
|
||||
return interpolation;
|
||||
}
|
||||
|
||||
// 如果这个插值已经是一个完整的 {{ $t(...) }},跳过
|
||||
if (/^\{\{\s*[\$]t\s*\(/.test(interpolation.trim())) {
|
||||
return interpolation;
|
||||
@ -422,6 +467,7 @@ function replaceAttributeBindings(code, translations) {
|
||||
}
|
||||
|
||||
// 再处理不带参数的调用::label="$t('key')"
|
||||
// 统一处理逻辑:保留 $t() 调用结构,只替换翻译键(与带参数的处理保持一致)
|
||||
return code.replace(
|
||||
/([:@]|v-bind:|v-on:)([a-zA-Z0-9_-]+)=(["'])[\$]?t\((['"])((?:\\.|(?!\4).)*)\4\)\3/g,
|
||||
(match, prefix, attrName, outerQuote, innerQuote, key) => {
|
||||
@ -429,14 +475,10 @@ function replaceAttributeBindings(code, translations) {
|
||||
const translation = translations[unescapedKey];
|
||||
if (!translation || !translation.trim()) return match;
|
||||
|
||||
let escaped = escapeString(translation, outerQuote);
|
||||
if (outerQuote === '"') {
|
||||
escaped = escaped.replace(/'/g, "\\'");
|
||||
return `${prefix}${attrName}="'${escaped}'"`;
|
||||
} else {
|
||||
escaped = escaped.replace(/"/g, '\\"');
|
||||
return `${prefix}${attrName}='"${escaped}"'`;
|
||||
}
|
||||
// 转义翻译文本以适配内层引号(与带参数的处理保持一致)
|
||||
const escaped = escapeString(translation, innerQuote);
|
||||
// 保留 $t() 调用结构,只替换翻译键
|
||||
return `${prefix}${attrName}=${outerQuote}$t(${innerQuote}${escaped}${innerQuote})${outerQuote}`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user