dev #3
@ -304,7 +304,7 @@
|
||||
>
|
||||
{{
|
||||
$route.name == 'Login'
|
||||
? $t('Don\'t have an account? Create one.')
|
||||
? $t("Don't have an account? Create one.")
|
||||
: $t('Already have an account? Log in.')
|
||||
}}
|
||||
</router-link>
|
||||
|
||||
@ -68,11 +68,11 @@ function processNestedTranslations(paramsCode, translations, pattern) {
|
||||
// 格式1:带前缀的 (ctxPrefix, quote, key)
|
||||
ctxPrefix = captureGroups[0] || '';
|
||||
quote = captureGroups[1];
|
||||
key = captureGroups[2];
|
||||
key = unescapeString(captureGroups[2]);
|
||||
} else if (captureGroups.length === 2) {
|
||||
// 格式2或3:不带前缀的 (quote, key)
|
||||
quote = captureGroups[0];
|
||||
key = captureGroups[1];
|
||||
key = unescapeString(captureGroups[1]);
|
||||
} else {
|
||||
// 无法解析,返回原匹配
|
||||
return match;
|
||||
@ -137,9 +137,10 @@ function vitePluginTranslate(options = {}) {
|
||||
} else {
|
||||
// 非 Vue 文件
|
||||
code = code.replace(
|
||||
/\bt\((['"])([^'"]+)\1\)/g,
|
||||
/\bt\((['"])((?:\\.|(?!\1).)*)\1\)/g,
|
||||
(match, quote, key) => {
|
||||
const translation = translations[key];
|
||||
const unescapedKey = unescapeString(key);
|
||||
const translation = translations[unescapedKey];
|
||||
return translation ? quote + escapeString(translation, quote) + quote : match;
|
||||
}
|
||||
);
|
||||
@ -202,9 +203,10 @@ function processVueSFC(code, translations) {
|
||||
/(<script[^>]*>)([\s\S]*?)(<\/script>)/gi,
|
||||
(match, openTag, scriptContent, closeTag) => {
|
||||
const replacedContent = scriptContent.replace(
|
||||
/\$t\((['"])([^'"]+)\1\)/g,
|
||||
/\$t\((['"])((?:\\.|(?!\1).)*)\1\)/g,
|
||||
(match, quote, key) => {
|
||||
const translation = translations[key];
|
||||
const unescapedKey = unescapeString(key);
|
||||
const translation = translations[unescapedKey];
|
||||
return translation ? `$t(${quote}${escapeString(translation, quote)}${quote})` : match;
|
||||
}
|
||||
);
|
||||
@ -221,19 +223,43 @@ function processVueSFC(code, translations) {
|
||||
function replaceInterpolations(code, translations) {
|
||||
// 先处理不带参数的调用:{{ $t('xxx') }}
|
||||
code = code.replace(
|
||||
/\{\{\s*[\$]t\s*\(\s*(['"])([^'"]+)\1\s*\)\s*\}\}/g,
|
||||
(match, quote, key) => translations[key] || match
|
||||
/\{\{\s*[\$]t\s*\(\s*(['"])((?:\\.|(?!\1).)*)\1\s*\)\s*\}\}/g,
|
||||
(match, quote, key) => {
|
||||
const unescapedKey = unescapeString(key);
|
||||
return translations[unescapedKey] || match;
|
||||
}
|
||||
);
|
||||
|
||||
// 处理插值表达式内部的 $t() 调用(不在单独的 {{ $t(...) }} 中)
|
||||
// 例如:{{ condition ? $t('key1') : $t('key2') }}
|
||||
code = code.replace(
|
||||
/\{\{[\s\S]*?\}\}/g,
|
||||
(interpolation) => {
|
||||
// 如果这个插值已经是一个完整的 {{ $t(...) }},跳过
|
||||
if (/^\{\{\s*[\$]t\s*\(/.test(interpolation.trim())) {
|
||||
return interpolation;
|
||||
}
|
||||
// 否则,处理其中的 $t() 调用
|
||||
return interpolation.replace(
|
||||
/\$t\((['"])((?:\\.|(?!\1).)*)\1\)/g,
|
||||
(match, quote, key) => {
|
||||
const unescapedKey = unescapeString(key);
|
||||
const translation = translations[unescapedKey];
|
||||
return translation ? `$t(${quote}${escapeString(translation, quote)}${quote})` : match;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// 处理带参数的调用:{{ $t('key', { params }) }}
|
||||
const matches = [];
|
||||
const pattern = /\{\{\s*[\$]t\s*\(\s*(['"])([^'"]+)\1\s*,\s*/g;
|
||||
const pattern = /\{\{\s*[\$]t\s*\(\s*(['"])((?:\\.|(?!\1).)*)\1\s*,\s*/g;
|
||||
let match;
|
||||
|
||||
while ((match = pattern.exec(code)) !== null) {
|
||||
const startIndex = match.index;
|
||||
const quote = match[1];
|
||||
const key = match[2];
|
||||
const key = unescapeString(match[2]);
|
||||
const afterKeyIndex = match.index + match[0].length;
|
||||
|
||||
// 找到参数部分的结束位置
|
||||
@ -261,13 +287,13 @@ function replaceInterpolations(code, translations) {
|
||||
paramsPart = processNestedTranslations(
|
||||
paramsPart,
|
||||
translations,
|
||||
/\{\{\s*[\$]t\s*\(\s*(['"])([^'"]+)\1\s*\)\s*\}\}/g
|
||||
/\{\{\s*[\$]t\s*\(\s*(['"])((?:\\.|(?!\1).)*)\1\s*\)\s*\}\}/g
|
||||
);
|
||||
// 再处理 $t(...) 格式(参数对象中的直接调用)
|
||||
paramsPart = processNestedTranslations(
|
||||
paramsPart,
|
||||
translations,
|
||||
/\$t\((['"])([^'"]+)\1\)/g
|
||||
/\$t\((['"])((?:\\.|(?!\1).)*)\1\)/g
|
||||
);
|
||||
|
||||
const translation = translations[m.key];
|
||||
@ -286,14 +312,14 @@ function replaceInterpolations(code, translations) {
|
||||
function replaceCompiledFormat(code, translations) {
|
||||
// 处理带参数的调用
|
||||
const matches = [];
|
||||
const pattern = /([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])([^'"]+)\2\s*,\s*/g;
|
||||
const pattern = /([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])((?:\\.|(?!\2).)*)\2\s*,\s*/g;
|
||||
let match;
|
||||
|
||||
while ((match = pattern.exec(code)) !== null) {
|
||||
const startIndex = match.index;
|
||||
const ctxPrefix = match[1] || '';
|
||||
const quote = match[2];
|
||||
const key = match[3];
|
||||
const key = unescapeString(match[3]);
|
||||
const afterKeyIndex = match.index + match[0].length;
|
||||
|
||||
const paramsEnd = findBalancedBracket(code, afterKeyIndex);
|
||||
@ -311,7 +337,7 @@ function replaceCompiledFormat(code, translations) {
|
||||
paramsPart = processNestedTranslations(
|
||||
paramsPart,
|
||||
translations,
|
||||
/([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])([^'"]+)\2\)/g
|
||||
/([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])((?:\\.|(?!\2).)*)\2\)/g
|
||||
);
|
||||
|
||||
const translation = translations[m.key];
|
||||
@ -323,9 +349,10 @@ function replaceCompiledFormat(code, translations) {
|
||||
|
||||
// 处理不带参数的调用
|
||||
code = code.replace(
|
||||
/([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])([^'"]+)\2\)/g,
|
||||
/([a-zA-Z_$][a-zA-Z0-9_$]*\.)?[\$]t\((['"])((?:\\.|(?!\2).)*)\2\)/g,
|
||||
(match, ctxPrefix, quote, key) => {
|
||||
const translation = translations[key];
|
||||
const unescapedKey = unescapeString(key);
|
||||
const translation = translations[unescapedKey];
|
||||
return translation ? `${ctxPrefix || ''}$t(${quote}${escapeString(translation, quote)}${quote})` : match;
|
||||
}
|
||||
);
|
||||
@ -338,9 +365,10 @@ function replaceCompiledFormat(code, translations) {
|
||||
*/
|
||||
function replaceAttributeBindings(code, translations) {
|
||||
return code.replace(
|
||||
/([:@]|v-bind:|v-on:)([a-zA-Z0-9_-]+)=(["'])[\$]?t\((['"])([^'"]+)\4\)\3/g,
|
||||
/([:@]|v-bind:|v-on:)([a-zA-Z0-9_-]+)=(["'])[\$]?t\((['"])((?:\\.|(?!\4).)*)\4\)\3/g,
|
||||
(match, prefix, attrName, outerQuote, innerQuote, key) => {
|
||||
const translation = translations[key];
|
||||
const unescapedKey = unescapeString(key);
|
||||
const translation = translations[unescapedKey];
|
||||
if (!translation || !translation.trim()) return match;
|
||||
|
||||
let escaped = escapeString(translation, outerQuote);
|
||||
@ -355,6 +383,14 @@ function replaceAttributeBindings(code, translations) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除字符串中的转义字符(将 \' 转换为 ',\" 转换为 " 等)
|
||||
*/
|
||||
function unescapeString(text) {
|
||||
return text
|
||||
.replace(/\\(.)/g, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义字符串
|
||||
*/
|
||||
|
||||
@ -11,7 +11,7 @@ import { sentryVitePlugin } from '@sentry/vite-plugin';
|
||||
import vitePluginTranslate from './vite-plugin-translate.mjs';
|
||||
|
||||
// 语言配置:设置目标语言,默认为 'en'(英文),可设置为 'zh'(中文)等
|
||||
const locale = process.env.DASHBOARD_LOCALE || 'en';
|
||||
const locale = process.env.DASHBOARD_LOCALE || 'zh';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user