#!/usr/bin/env node /** * Translate privacy.json from EN to DE and FR using DeepL API * * Usage: node scripts/translate-privacy-deepl.js * * Requires: DEEPL_API_KEY environment variable */ const fs = require('fs'); const path = require('path'); const https = require('https'); const DEEPL_API_KEY = process.env.DEEPL_API_KEY; const API_URL = 'api.deepl.com'; // Pro API endpoint if (!DEEPL_API_KEY) { console.error('āŒ ERROR: DEEPL_API_KEY environment variable not set'); console.error(' Set it with: export DEEPL_API_KEY="your-key-here"'); process.exit(1); } const EN_FILE = path.join(__dirname, '../public/locales/en/privacy.json'); const DE_FILE = path.join(__dirname, '../public/locales/de/privacy.json'); const FR_FILE = path.join(__dirname, '../public/locales/fr/privacy.json'); // Load JSON files const enData = JSON.parse(fs.readFileSync(EN_FILE, 'utf8')); const deData = JSON.parse(fs.readFileSync(DE_FILE, 'utf8')); const frData = JSON.parse(fs.readFileSync(FR_FILE, 'utf8')); // DeepL API request function function translateText(text, targetLang) { return new Promise((resolve, reject) => { const postData = new URLSearchParams({ auth_key: DEEPL_API_KEY, text: text, target_lang: targetLang, source_lang: 'EN', formality: 'default', preserve_formatting: '1', tag_handling: 'html' }).toString(); const options = { hostname: API_URL, port: 443, path: '/v2/translate', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { if (res.statusCode === 200) { const response = JSON.parse(data); resolve(response.translations[0].text); } else { reject(new Error(`DeepL API error: ${res.statusCode} - ${data}`)); } }); }); req.on('error', reject); req.write(postData); req.end(); }); } // Recursively translate object async function translateObject(obj, targetLang) { const result = {}; for (const [key, value] of Object.entries(obj)) { if (Array.isArray(value)) { result[key] = []; for (const item of value) { if (typeof item === 'string') { console.log(` Translating array item: ${item.substring(0, 50)}...`); result[key].push(await translateText(item, targetLang)); await new Promise(resolve => setTimeout(resolve, 200)); // Rate limit } else { result[key].push(item); } } } else if (typeof value === 'object' && value !== null) { result[key] = await translateObject(value, targetLang); } else if (typeof value === 'string') { console.log(` Translating: ${key}`); result[key] = await translateText(value, targetLang); await new Promise(resolve => setTimeout(resolve, 200)); // Rate limit } else { result[key] = value; } } return result; } async function main() { try { console.log('šŸŒ Starting privacy.json translation with DeepL\n'); // Translate Section 6 only (the new Umami content) console.log('šŸ“ Translating Section 6 to German (DE)...'); deData.section_6 = await translateObject(enData.section_6, 'DE'); deData.header.last_updated = await translateText(enData.header.last_updated, 'DE'); console.log('\nšŸ“ Translating Section 6 to French (FR)...'); frData.section_6 = await translateObject(enData.section_6, 'FR'); frData.header.last_updated = await translateText(enData.header.last_updated, 'FR'); // Save files fs.writeFileSync(DE_FILE, JSON.stringify(deData, null, 2) + '\n', 'utf8'); fs.writeFileSync(FR_FILE, JSON.stringify(frData, null, 2) + '\n', 'utf8'); console.log('\nāœ… Translation complete!'); console.log(` DE: ${DE_FILE}`); console.log(` FR: ${FR_FILE}`); } catch (error) { console.error('\nāŒ Translation failed:', error.message); process.exit(1); } } main();