fix(i18n,notice): make locale formatting idempotent and guard early notice translations
This commit is contained in:
@@ -945,11 +945,16 @@ function loadLocales() {
|
|||||||
function ensureBackup(localePath) {
|
function ensureBackup(localePath) {
|
||||||
const backupPath = `${localePath}.bak`;
|
const backupPath = `${localePath}.bak`;
|
||||||
if (fs.existsSync(backupPath)) {
|
if (fs.existsSync(backupPath)) {
|
||||||
|
try {
|
||||||
|
fs.rmSync(backupPath);
|
||||||
|
} catch (error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Backup file already exists for ${path.basename(localePath)}; ` +
|
`Failed to recycle existing backup for ${path.basename(
|
||||||
"either remove it manually or rerun with --no-backup",
|
localePath,
|
||||||
|
)}: ${error.message}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fs.copyFileSync(localePath, backupPath);
|
fs.copyFileSync(localePath, backupPath);
|
||||||
return backupPath;
|
return backupPath;
|
||||||
}
|
}
|
||||||
@@ -959,10 +964,27 @@ function backupIfNeeded(filePath, backups, options) {
|
|||||||
if (!fs.existsSync(filePath)) return;
|
if (!fs.existsSync(filePath)) return;
|
||||||
if (backups.has(filePath)) return;
|
if (backups.has(filePath)) return;
|
||||||
const backupPath = ensureBackup(filePath);
|
const backupPath = ensureBackup(filePath);
|
||||||
backups.add(filePath);
|
backups.set(filePath, backupPath);
|
||||||
return backupPath;
|
return backupPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanupBackups(backups) {
|
||||||
|
for (const backupPath of backups.values()) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(backupPath)) {
|
||||||
|
fs.rmSync(backupPath);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: failed to remove backup ${path.basename(
|
||||||
|
backupPath,
|
||||||
|
)}: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backups.clear();
|
||||||
|
}
|
||||||
|
|
||||||
function toModuleIdentifier(namespace, seen) {
|
function toModuleIdentifier(namespace, seen) {
|
||||||
const RESERVED = new Set([
|
const RESERVED = new Set([
|
||||||
"default",
|
"default",
|
||||||
@@ -1016,13 +1038,16 @@ export default resources;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeLocale(locale, data, options) {
|
function writeLocale(locale, data, options) {
|
||||||
const backups = new Set();
|
const backups = new Map();
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
if (locale.format === "single-file") {
|
if (locale.format === "single-file") {
|
||||||
const target = locale.files[0].path;
|
const target = locale.files[0].path;
|
||||||
backupIfNeeded(target, backups, options);
|
backupIfNeeded(target, backups, options);
|
||||||
const serialized = JSON.stringify(data, null, 2);
|
const serialized = JSON.stringify(data, null, 2);
|
||||||
fs.writeFileSync(target, `${serialized}\n`, "utf8");
|
fs.writeFileSync(target, `${serialized}\n`, "utf8");
|
||||||
|
success = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,6 +1081,12 @@ function writeLocale(locale, data, options) {
|
|||||||
namespace,
|
namespace,
|
||||||
path: path.join(locale.dir, `${namespace}.json`),
|
path: path.join(locale.dir, `${namespace}.json`),
|
||||||
}));
|
}));
|
||||||
|
success = true;
|
||||||
|
} finally {
|
||||||
|
if (success) {
|
||||||
|
cleanupBackups(backups);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processLocale(
|
function processLocale(
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ const DEFAULT_DURATIONS: Readonly<Record<NoticeType, number>> = {
|
|||||||
error: 8000,
|
error: 8000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TRANSLATION_KEY_PATTERN = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)+$/;
|
||||||
|
|
||||||
let nextId = 0;
|
let nextId = 0;
|
||||||
let notices: NoticeItem[] = [];
|
let notices: NoticeItem[] = [];
|
||||||
const subscribers: Set<NoticeSubscriber> = new Set();
|
const subscribers: Set<NoticeSubscriber> = new Set();
|
||||||
@@ -157,11 +159,16 @@ function createRawDescriptor(message: string): NoticeTranslationDescriptor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLikelyTranslationKey(key: string) {
|
||||||
|
return TRANSLATION_KEY_PATTERN.test(key);
|
||||||
|
}
|
||||||
|
|
||||||
function shouldUseTranslationKey(
|
function shouldUseTranslationKey(
|
||||||
key: string,
|
key: string,
|
||||||
params?: Record<string, unknown>,
|
params?: Record<string, unknown>,
|
||||||
) {
|
) {
|
||||||
if (params && Object.keys(params).length > 0) return true;
|
if (params && Object.keys(params).length > 0) return true;
|
||||||
|
if (isLikelyTranslationKey(key)) return true;
|
||||||
if (i18n.isInitialized) {
|
if (i18n.isInitialized) {
|
||||||
return i18n.exists(key);
|
return i18n.exists(key);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user