#!/usr/bin/env node /** * Sync translations with Weblate */ const axios = require('axios'); const fs = require('fs'); const path = require('path'); const WEBLATE_API_URL = process.env.WEBLATE_API_URL || 'https://hosted.weblate.org/api'; const WEBLATE_TOKEN = process.env.WEBLATE_TOKEN; const PROJECT_SLUG = process.env.WEBLATE_PROJECT || 'pocketpal-ai'; const COMPONENT_SLUG = process.env.WEBLATE_COMPONENT || 'translations'; if (!WEBLATE_TOKEN) { console.log('ℹ️ WEBLATE_TOKEN not set, skipping Weblate sync'); process.exit(0); } async function uploadSourceFile() { const enPath = path.join(__dirname, '../src/locales/en.json'); if (!fs.existsSync(enPath)) { console.error('Source file en.json not found'); process.exit(1); } try { const fileContent = fs.readFileSync(enPath); const formData = new FormData(); formData.append('file', new Blob([fileContent]), 'en.json'); formData.append('method', 'replace'); const response = await axios.post( `${WEBLATE_API_URL}/translations/${PROJECT_SLUG}/${COMPONENT_SLUG}/en/file/`, formData, { headers: { Authorization: `Token ${WEBLATE_TOKEN}`, }, }, ); console.log('Uploaded source file to Weblate:', response.status); } catch (error) { console.error( 'Failed to upload source file:', error.response?.data || error.message, ); } } async function downloadTranslations() { const languages = [ 'fa', 'he', 'id', 'ja', 'ko', 'ms', 'pt_BR', 'ru', 'uk', 'zh', 'zh_Hant', ]; for (const lang of languages) { try { const response = await axios.get( `${WEBLATE_API_URL}/translations/${PROJECT_SLUG}/${COMPONENT_SLUG}/${lang}/file/`, { headers: { Authorization: `Token ${WEBLATE_TOKEN}`, }, }, ); const filePath = path.join(__dirname, `../src/locales/${lang}.json`); fs.writeFileSync(filePath, JSON.stringify(response.data, null, 2)); console.log(`✅ Downloaded ${lang}.json`); } catch (error) { if (error.response?.status === 404) { console.log( `ℹ️ Translation for ${lang} not found in Weblate, keeping local version`, ); } else { console.error( `❌ Failed to download ${lang}.json:`, error.response?.data || error.message, ); } } } } async function getProjectStats() { try { const response = await axios.get( `${WEBLATE_API_URL}/projects/${PROJECT_SLUG}/statistics/`, { headers: { Authorization: `Token ${WEBLATE_TOKEN}`, }, }, ); console.log('\n📊 Translation Statistics:'); console.log(`Total strings: ${response.data.total}`); console.log( `Translated: ${response.data.translated} (${response.data.translated_percent}%)`, ); console.log(`Needs editing: ${response.data.fuzzy}`); console.log(`Failing checks: ${response.data.failing_checks}`); } catch (error) { console.error( '❌ Failed to get project stats:', error.response?.data || error.message, ); } } async function main() { const command = process.argv[2]; switch (command) { case 'upload': await uploadSourceFile(); break; case 'download': await downloadTranslations(); break; case 'sync': await uploadSourceFile(); await downloadTranslations(); break; case 'stats': await getProjectStats(); break; default: console.log(` Usage: node scripts/sync-weblate.js Commands: upload Upload source file (en.json) to Weblate download Download translated files from Weblate sync Upload source and download translations stats Show translation statistics Environment variables: WEBLATE_TOKEN - Your Weblate API token (required) WEBLATE_API_URL - Weblate API URL (default: https://hosted.weblate.org/api) WEBLATE_PROJECT - Project slug (default: pocketpal-ai) WEBLATE_COMPONENT - Component slug (default: translations) `); process.exit(1); } } main().catch(error => { console.error('❌ Script failed:', error); process.exit(1); });