mirror of
https://github.com/zonemaster/zonemaster-gui.git
synced 2026-01-19 20:44:13 +01:00
166 lines
5.3 KiB
TypeScript
166 lines
5.3 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
// Regex to find placeholders like {variableName}
|
|
const placeholderRegex = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
|
|
interface Messages {
|
|
[key: string]: string;
|
|
}
|
|
|
|
interface AllMessages {
|
|
[lang: string]: Messages;
|
|
}
|
|
|
|
interface GenerateConfig {
|
|
defaultLanguage: string;
|
|
enabledLanguages: string[];
|
|
}
|
|
|
|
function getFileHeader(): string {
|
|
return `/* Auto-generated by vite-plugin-messages - DO NOT EDIT */\n/* Generated: ${new Date().toISOString()} */\n`;
|
|
}
|
|
|
|
export function generateMessages(langDir: string, outDir: string, config: GenerateConfig) {
|
|
const { defaultLanguage, enabledLanguages } = config;
|
|
|
|
if (!fs.existsSync(outDir)) {
|
|
fs.mkdirSync(outDir, { recursive: true });
|
|
}
|
|
|
|
const allMessages: AllMessages = {};
|
|
|
|
for (const lang of enabledLanguages) {
|
|
const filePath = path.join(langDir, `${lang}.json`);
|
|
try {
|
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
allMessages[lang] = JSON.parse(content);
|
|
} catch (e) {
|
|
console.warn(`⚠️ Failed to load ${lang}.json:`, e instanceof Error ? e.message : String(e));
|
|
allMessages[lang] = {};
|
|
}
|
|
}
|
|
|
|
if (Object.keys(allMessages).length === 0) {
|
|
console.warn('⚠️ No language files found');
|
|
return;
|
|
}
|
|
|
|
const defaultMessages = allMessages[defaultLanguage] || {};
|
|
const allKeys = Object.keys(defaultMessages);
|
|
|
|
for (const lang of enabledLanguages) {
|
|
const messages = allMessages[lang] || {};
|
|
const functions: string[] = [];
|
|
const reExports: string[] = [];
|
|
|
|
for (const key of allKeys) {
|
|
const hasTranslation = key in messages;
|
|
|
|
if (hasTranslation) {
|
|
functions.push(createMessageFunction(key, messages[key]));
|
|
} else if (lang !== defaultLanguage) {
|
|
reExports.push(key);
|
|
}
|
|
}
|
|
|
|
let content = getFileHeader();
|
|
|
|
if (reExports.length > 0) {
|
|
content += `\nexport { ${reExports.join(', ')} } from './${defaultLanguage}.ts';\n`;
|
|
}
|
|
|
|
if (functions.length > 0) {
|
|
content += `\n${functions.join('\n\n')}\n`;
|
|
}
|
|
|
|
const filePath = path.join(outDir, `${lang}.ts`);
|
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
console.log(`✅ Generated messages/${lang}.ts (${functions.length} translated, ${reExports.length} re-exported)`);
|
|
}
|
|
|
|
generateIndexFile(outDir, allKeys, defaultMessages, config);
|
|
}
|
|
|
|
function extractPlaceholders(value: string): string[] {
|
|
const matches = value.matchAll(placeholderRegex);
|
|
const placeholders = new Set<string>();
|
|
for (const match of matches) {
|
|
placeholders.add(match[1]);
|
|
}
|
|
return Array.from(placeholders);
|
|
}
|
|
|
|
function escapeTemplateString(value: string): string {
|
|
// Escape backticks and ${} that are not our placeholders
|
|
return value
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/`/g, '\\`');
|
|
}
|
|
|
|
function createMessageFunction(key: string, value: string): string {
|
|
const placeholders = extractPlaceholders(value);
|
|
const templateString = value.replace(placeholderRegex, '${params.$1}');
|
|
const escapedTemplate = escapeTemplateString(templateString);
|
|
|
|
if (placeholders.length > 0) {
|
|
const paramsType = placeholders.map(p => `${p}: string | number`).join(', ');
|
|
return `export const ${key} = (params: { ${paramsType} }): string => \`${escapedTemplate}\`;`;
|
|
}
|
|
return `export const ${key} = (): string => \`${escapedTemplate}\`;`;
|
|
}
|
|
|
|
function generateIndexFile(outDir: string, allKeys: string[], defaultMessages: Messages, config: GenerateConfig) {
|
|
const { enabledLanguages } = config;
|
|
|
|
const langImports = enabledLanguages
|
|
.map(lang => `import * as ${lang} from './${lang}.ts';`)
|
|
.join('\n');
|
|
|
|
const langObject = enabledLanguages.join(', ');
|
|
const languageType = enabledLanguages.map(l => `'${l}'`).join(' | ');
|
|
const messageExports: string[] = [];
|
|
|
|
for (const key of allKeys) {
|
|
const value = defaultMessages[key] || '';
|
|
const placeholders = extractPlaceholders(value);
|
|
const paramsType = placeholders.length > 0
|
|
? `params: { ${placeholders.map(p => `${p}: string | number`).join(', ')} }`
|
|
: '';
|
|
const paramsArg = paramsType ? 'params' : '';
|
|
|
|
messageExports.push(
|
|
`export const ${key} = (${paramsType}): string => allMessages[getLocale()].${key}(${paramsArg});`
|
|
);
|
|
}
|
|
|
|
const content = `${getFileHeader()}
|
|
import config from '@/config';
|
|
|
|
${langImports}
|
|
|
|
const allMessages = { ${langObject} } as const;
|
|
|
|
export type Language = ${languageType};
|
|
|
|
export const defaultLanguage: Language = '${config.defaultLanguage}';
|
|
export const enabledLanguages: readonly Language[] = ${JSON.stringify(config.enabledLanguages)} as const;
|
|
|
|
let _locale: Language = defaultLanguage;
|
|
|
|
export const getLocale = (): Language => _locale;
|
|
export const isValidLocale = (locale: unknown): locale is Language => enabledLanguages.includes(locale as Language);
|
|
export const setLocale = (locale: unknown): void => {
|
|
if (isValidLocale(locale)) {
|
|
_locale = locale as Language;
|
|
}
|
|
};
|
|
|
|
// Message functions
|
|
${messageExports.join('\n\n')}
|
|
`;
|
|
|
|
const filePath = path.join(outDir, 'index.ts');
|
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
console.log('✅ Generated messages/index.ts');
|
|
}
|