Files
hot-step-cpp-ROCm/ui/generate_translations.mjs
2026-08-16 18:24:52 +07:00

81 lines
2.9 KiB
JavaScript

/**
* generate_translations.mjs — Translate en.json to target locales
* Uses Google Translate free API via fetch (no package needed).
* Run: node generate_translations.mjs
*/
import { readFileSync, writeFileSync } from 'fs';
const LOCALES = ['ru', 'zh-CN', 'ja', 'ko'];
const LOCALE_FILES = { 'ru': 'ru', 'zh-CN': 'zh', 'ja': 'ja', 'ko': 'ko' };
// Technical terms that should NOT be translated
const KEEP_ENGLISH = new Set([
'DiT', 'VAE', 'LM', 'LoRA', 'LoKR', 'GGUF', 'BPM', 'APG', 'CFG', 'CFG++',
'DPM++', 'DDIM', 'SGM', 'ODE', 'NFE', 'RK4', 'CoT', 'HSLAT', 'PP-VAE',
'VST', 'VST3', 'VRAM', 'Opus', 'FLAC', 'WAV', 'MP3', 'RMS', 'DCW',
'Euler', 'Heun', 'HOT-Step', 'HOT-Step CPP', 'Genius', 'OpenAI', 'Gemini',
'Anthropic', 'Claude', 'GPT', 'Ollama', 'LM Studio', 'Unsloth',
'safetensors', '.safetensors', '.latent', 'HuggingFace',
'NoFSQ', 'FSQ', 'SNR-t', 'Log-SNR',
]);
async function translate(text, targetLang) {
// Skip empty or very short strings
if (!text || text.length < 2) return text;
// Skip if it's a technical placeholder
if (text.startsWith('{{') || text.startsWith('NO USER INPUT')) return text;
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
try {
const res = await fetch(url);
const data = await res.json();
// Response is nested arrays: [[["translated","original",null,null,x],...]]
let result = '';
if (Array.isArray(data) && Array.isArray(data[0])) {
for (const segment of data[0]) {
if (segment[0]) result += segment[0];
}
}
return result || text;
} catch (err) {
console.error(` ⚠ Failed to translate "${text.substring(0, 40)}..." to ${targetLang}:`, err.message);
return text; // fallback to English
}
}
// Rate limiter — Google blocks if we go too fast
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function main() {
const en = JSON.parse(readFileSync('./src/i18n/locales/en.json', 'utf-8'));
const entries = Object.entries(en.translation);
for (const locale of LOCALES) {
const outFile = LOCALE_FILES[locale];
console.log(`\n🌍 Translating to ${locale} (${entries.length} keys)...`);
const translated = {};
let done = 0;
for (const [key, value] of entries) {
done++;
if (done % 50 === 0) console.log(` ${done}/${entries.length}...`);
const result = await translate(value, locale);
translated[key] = result;
// Small delay to avoid rate limiting (50ms between requests)
await sleep(50);
}
const output = { translation: translated };
writeFileSync(`./src/i18n/locales/${outFile}.json`, JSON.stringify(output, null, 2) + '\n', 'utf-8');
console.log(`✅ Wrote ${outFile}.json (${Object.keys(translated).length} keys)`);
}
console.log('\n🎉 All translations complete!');
}
main().catch(console.error);