Files
trainingstagebuch/scripts/generate-mobile-i18n.js
Torsten Schulz (local) 2f15827658
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 47s
feat(Calendar): update localization and enhance regression checklist
- Updated the regression checklist to reflect the completion of Phase 14.
- Incremented versionCode to 4 and versionName to 1.2.0 in build.gradle.kts for the new release.
- Added new localization keys for calendar features in multiple languages, improving user accessibility and clarity.
- Enhanced the generate-mobile-i18n script to support new calendar translations, ensuring comprehensive coverage across locales.
2026-05-15 07:55:32 +02:00

595 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Generates i18n maps for the native mobile app from the web source of truth.
*
* Source of truth: `frontend/src/i18n/locales/*.json`
*
* Output:
* - `mobile-app/shared/src/commonMain/kotlin/de/tsschulz/tt_tagebuch/shared/i18n/MobileStrings.kt`
*
* Notes:
* - German (`de`) is the canonical key set.
* - Missing locale keys fall back to German at generation time so every mobile locale is complete.
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const SOURCE_DIR = path.join(ROOT, 'frontend', 'src', 'i18n', 'locales');
const OUT = path.join(
ROOT,
'mobile-app',
'shared',
'src',
'commonMain',
'kotlin',
'de',
'tsschulz',
'tt_tagebuch',
'shared',
'i18n',
'MobileStrings.kt'
);
const LOCALE_LABELS = {
'de': 'Deutsch',
'de-CH': 'Deutsch (Schweiz)',
'de-extended': 'Deutsch (erweitert)',
'en-US': 'English (US)',
'en-GB': 'English (UK)',
'en-AU': 'English (AU)',
'es': 'Español',
'fr': 'Français',
'it': 'Italiano',
'pl': 'Polski',
'ja': '日本語',
'th': 'ไทย',
'tl': 'Tagalog',
'fil': 'Filipino',
'zh': '中文',
};
const MOBILE_STRINGS = {
de: {
'mobile.add': 'Hinzufügen',
'mobile.appLoading': 'App wird geladen',
'mobile.cancel': 'Abbrechen',
'mobile.clubRequest': 'Zugriff anfragen',
'mobile.clubRequested': 'Angefragt',
'mobile.createDiaryEntry': 'Eintrag erstellen',
'mobile.deleteNote': 'Notiz löschen',
'mobile.deleteTag': 'Tag entfernen',
'mobile.editTimes': 'Zeiten bearbeiten',
'mobile.emailAndPasswordRequired': 'E-Mail und Passwort sind erforderlich',
'mobile.entry': 'Eintrag',
'mobile.language': 'Sprache',
'mobile.lastTraining': 'Letztes Training',
'mobile.loginInProgress': 'Anmelden...',
'mobile.loginFailed': 'Login fehlgeschlagen',
'mobile.more': 'Mehr',
'mobile.new': 'Neu',
'mobile.newNote': 'Neue Notiz',
'mobile.newTag': 'Neuer Tag',
'mobile.noDiaryEntries': 'Noch keine Tagebuch-Einträge',
'mobile.noMembers': 'Keine Mitglieder gefunden',
'mobile.noResults': 'Keine Treffer',
'mobile.noTimes': 'Keine Zeiten',
'mobile.participationTop': 'Top Teilnahmen',
'mobile.refresh': 'Aktualisieren',
'mobile.requestedAccess': 'Angefragt',
'mobile.role': 'Rolle',
'mobile.saveEntry': 'Speichern',
'mobile.search': 'Suche',
'mobile.select': 'Auswählen',
'mobile.sessionCheck': 'Session prüfen',
'mobile.sessionCheckFailed': 'Session check fehlgeschlagen',
'mobile.sessionInvalid': 'Session ungültig',
'mobile.sessionValid': 'Session gültig',
'mobile.status': 'Status',
'mobile.active': 'Aktiv',
'mobile.inactive': 'Inaktiv',
'mobile.user': 'Benutzer',
},
en: {
'mobile.add': 'Add',
'mobile.appLoading': 'Loading app',
'mobile.cancel': 'Cancel',
'mobile.clubRequest': 'Request access',
'mobile.clubRequested': 'Requested',
'mobile.createDiaryEntry': 'Create entry',
'mobile.deleteNote': 'Delete note',
'mobile.deleteTag': 'Remove tag',
'mobile.editTimes': 'Edit times',
'mobile.emailAndPasswordRequired': 'Email and password are required',
'mobile.entry': 'Entry',
'mobile.language': 'Language',
'mobile.lastTraining': 'Last training',
'mobile.loginInProgress': 'Signing in...',
'mobile.loginFailed': 'Login failed',
'mobile.more': 'More',
'mobile.new': 'New',
'mobile.newNote': 'New note',
'mobile.newTag': 'New tag',
'mobile.noDiaryEntries': 'No diary entries yet',
'mobile.noMembers': 'No members found',
'mobile.noResults': 'No results',
'mobile.noTimes': 'No times',
'mobile.participationTop': 'Top attendance',
'mobile.refresh': 'Refresh',
'mobile.requestedAccess': 'Requested',
'mobile.role': 'Role',
'mobile.saveEntry': 'Save',
'mobile.search': 'Search',
'mobile.select': 'Select',
'mobile.sessionCheck': 'Check session',
'mobile.sessionCheckFailed': 'Session check failed',
'mobile.sessionInvalid': 'Session invalid',
'mobile.sessionValid': 'Session valid',
'mobile.status': 'Status',
'mobile.active': 'Active',
'mobile.inactive': 'Inactive',
'mobile.user': 'User',
},
};
/** Kalender-Tab (CalendarScreen.kt): eigene Übersetzungen pro Locale, damit nicht nur Deutsch aus dem Basis-Flat greift. */
const MOBILE_CALENDAR_I18N = {
de: {
'mobile.calendarSourceDiary': 'Tagebuch',
'mobile.calendarSourceTrainingTimes': 'Trainingszeiten',
'mobile.calendarSourceCancellations': 'Trainingsausfälle',
'mobile.calendarSourceTournaments': 'Turniere',
'mobile.calendarSourceMatches': 'Punktspiele',
'mobile.calendarSourceOfficial': 'Turnierteilnahmen',
'mobile.calendarSourceHolidays': 'Ferien/Feiertage',
'mobile.calendarLegendTraining': 'Training',
'mobile.calendarLegendTournament': 'Turnier',
'mobile.calendarLegendOfficial': 'Teilnahme',
'mobile.calendarLegendMatch': 'Punktspiel',
'mobile.calendarLegendHoliday': 'Feiertag',
'mobile.calendarLegendSchool': 'Ferien',
'mobile.calendarLegendCancellation': 'Ausfall',
'mobile.calendarTitle': 'Kalender',
'mobile.calendarSubtitle': 'Training, Turniere, Spiele und Feiertage im Monat.',
'mobile.calendarToday': 'Heute',
'mobile.calendarAllSourcesFailed': 'Kalenderdaten konnten nicht geladen werden.',
'mobile.calendarSourceFailed': 'nicht geladen',
'mobile.calendarLegend': 'Anzeige',
'mobile.calendarCancellationTitle': 'Training fällt aus',
'mobile.calendarCancellationHint': 'Blendet regelmäßige Trainingszeiten an den Tagen aus.',
'mobile.calendarCancellationStart': 'Datum (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': 'Bis optional',
'mobile.calendarCancellationReason': 'Grund',
'mobile.calendarCancellationSave': 'Eintragen',
'mobile.calendarCancellationInMonth': 'Ausfälle diesen Monat',
'mobile.calendarGridTitle': 'Monat',
'mobile.calendarAgendaTitle': 'Termine im Monat',
'mobile.calendarAgendaEmpty': 'Keine Termine in diesem Monat.',
},
en: {
'mobile.calendarSourceDiary': 'Diary',
'mobile.calendarSourceTrainingTimes': 'Training times',
'mobile.calendarSourceCancellations': 'Training cancellations',
'mobile.calendarSourceTournaments': 'Tournaments',
'mobile.calendarSourceMatches': 'League matches',
'mobile.calendarSourceOfficial': 'Official participations',
'mobile.calendarSourceHolidays': 'Holidays / school breaks',
'mobile.calendarLegendTraining': 'Training',
'mobile.calendarLegendTournament': 'Tournament',
'mobile.calendarLegendOfficial': 'Participation',
'mobile.calendarLegendMatch': 'League match',
'mobile.calendarLegendHoliday': 'Public holiday',
'mobile.calendarLegendSchool': 'School break',
'mobile.calendarLegendCancellation': 'Cancellation',
'mobile.calendarTitle': 'Calendar',
'mobile.calendarSubtitle': 'Training, tournaments, matches and holidays for the month.',
'mobile.calendarToday': 'Today',
'mobile.calendarAllSourcesFailed': 'Calendar data could not be loaded.',
'mobile.calendarSourceFailed': 'not loaded',
'mobile.calendarLegend': 'Display',
'mobile.calendarCancellationTitle': 'Training cancelled',
'mobile.calendarCancellationHint': 'Hides recurring training times on those days.',
'mobile.calendarCancellationStart': 'Date (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': 'Until (optional)',
'mobile.calendarCancellationReason': 'Reason',
'mobile.calendarCancellationSave': 'Save',
'mobile.calendarCancellationInMonth': 'Cancellations this month',
'mobile.calendarGridTitle': 'Month',
'mobile.calendarAgendaTitle': 'Events this month',
'mobile.calendarAgendaEmpty': 'No events in this month.',
},
es: {
'mobile.calendarSourceDiary': 'Diario',
'mobile.calendarSourceTrainingTimes': 'Horarios de entrenamiento',
'mobile.calendarSourceCancellations': 'Cancelaciones',
'mobile.calendarSourceTournaments': 'Torneos',
'mobile.calendarSourceMatches': 'Partidos de liga',
'mobile.calendarSourceOfficial': 'Participaciones oficiales',
'mobile.calendarSourceHolidays': 'Vacaciones / festivos',
'mobile.calendarLegendTraining': 'Entrenamiento',
'mobile.calendarLegendTournament': 'Torneo',
'mobile.calendarLegendOfficial': 'Participación',
'mobile.calendarLegendMatch': 'Partido de liga',
'mobile.calendarLegendHoliday': 'Festivo',
'mobile.calendarLegendSchool': 'Vacaciones escolares',
'mobile.calendarLegendCancellation': 'Cancelación',
'mobile.calendarTitle': 'Calendario',
'mobile.calendarSubtitle': 'Entrenamientos, torneos, partidos y festivos del mes.',
'mobile.calendarToday': 'Hoy',
'mobile.calendarAllSourcesFailed': 'No se pudieron cargar los datos del calendario.',
'mobile.calendarSourceFailed': 'no cargado',
'mobile.calendarLegend': 'Vista',
'mobile.calendarCancellationTitle': 'Entrenamiento cancelado',
'mobile.calendarCancellationHint': 'Oculta los horarios recurrentes en esos días.',
'mobile.calendarCancellationStart': 'Fecha (AAAA-MM-DD)',
'mobile.calendarCancellationEnd': 'Hasta (opcional)',
'mobile.calendarCancellationReason': 'Motivo',
'mobile.calendarCancellationSave': 'Guardar',
'mobile.calendarCancellationInMonth': 'Cancelaciones este mes',
'mobile.calendarGridTitle': 'Mes',
'mobile.calendarAgendaTitle': 'Citas del mes',
'mobile.calendarAgendaEmpty': 'No hay citas en este mes.',
},
fr: {
'mobile.calendarSourceDiary': 'Journal',
'mobile.calendarSourceTrainingTimes': 'Créneaux dentraînement',
'mobile.calendarSourceCancellations': 'Annulations',
'mobile.calendarSourceTournaments': 'Tournois',
'mobile.calendarSourceMatches': 'Matchs de championnat',
'mobile.calendarSourceOfficial': 'Participations officielles',
'mobile.calendarSourceHolidays': 'Vacances / jours fériés',
'mobile.calendarLegendTraining': 'Entraînement',
'mobile.calendarLegendTournament': 'Tournoi',
'mobile.calendarLegendOfficial': 'Participation',
'mobile.calendarLegendMatch': 'Match',
'mobile.calendarLegendHoliday': 'Jour férié',
'mobile.calendarLegendSchool': 'Vacances scolaires',
'mobile.calendarLegendCancellation': 'Annulation',
'mobile.calendarTitle': 'Calendrier',
'mobile.calendarSubtitle': 'Entraînements, tournois, matchs et vacances du mois.',
'mobile.calendarToday': 'Aujourdhui',
'mobile.calendarAllSourcesFailed': 'Impossible de charger le calendrier.',
'mobile.calendarSourceFailed': 'non chargé',
'mobile.calendarLegend': 'Affichage',
'mobile.calendarCancellationTitle': 'Entraînement annulé',
'mobile.calendarCancellationHint': 'Masque les créneaux récurrents ces jours-là.',
'mobile.calendarCancellationStart': 'Date (AAAA-MM-JJ)',
'mobile.calendarCancellationEnd': 'Jusquau (optionnel)',
'mobile.calendarCancellationReason': 'Motif',
'mobile.calendarCancellationSave': 'Enregistrer',
'mobile.calendarCancellationInMonth': 'Annulations ce mois-ci',
'mobile.calendarGridTitle': 'Mois',
'mobile.calendarAgendaTitle': 'Rendez-vous du mois',
'mobile.calendarAgendaEmpty': 'Aucun rendez-vous ce mois-ci.',
},
it: {
'mobile.calendarSourceDiary': 'Diario',
'mobile.calendarSourceTrainingTimes': 'Orari allenamento',
'mobile.calendarSourceCancellations': 'Cancellazioni',
'mobile.calendarSourceTournaments': 'Tornei',
'mobile.calendarSourceMatches': 'Partite campionato',
'mobile.calendarSourceOfficial': 'Partecipazioni ufficiali',
'mobile.calendarSourceHolidays': 'Vacanze / festività',
'mobile.calendarLegendTraining': 'Allenamento',
'mobile.calendarLegendTournament': 'Torneo',
'mobile.calendarLegendOfficial': 'Partecipazione',
'mobile.calendarLegendMatch': 'Partita',
'mobile.calendarLegendHoliday': 'Festività',
'mobile.calendarLegendSchool': 'Vacanze scolastiche',
'mobile.calendarLegendCancellation': 'Cancellazione',
'mobile.calendarTitle': 'Calendario',
'mobile.calendarSubtitle': 'Allenamenti, tornei, partite e festività del mese.',
'mobile.calendarToday': 'Oggi',
'mobile.calendarAllSourcesFailed': 'Impossibile caricare il calendario.',
'mobile.calendarSourceFailed': 'non caricato',
'mobile.calendarLegend': 'Vista',
'mobile.calendarCancellationTitle': 'Allenamento annullato',
'mobile.calendarCancellationHint': 'Nasconde gli orari ricorrenti in quei giorni.',
'mobile.calendarCancellationStart': 'Data (AAAA-MM-GG)',
'mobile.calendarCancellationEnd': 'Fino al (opzionale)',
'mobile.calendarCancellationReason': 'Motivo',
'mobile.calendarCancellationSave': 'Salva',
'mobile.calendarCancellationInMonth': 'Cancellazioni questo mese',
'mobile.calendarGridTitle': 'Mese',
'mobile.calendarAgendaTitle': 'Appuntamenti del mese',
'mobile.calendarAgendaEmpty': 'Nessun appuntamento in questo mese.',
},
pl: {
'mobile.calendarSourceDiary': 'Dziennik',
'mobile.calendarSourceTrainingTimes': 'Terminy treningów',
'mobile.calendarSourceCancellations': 'Odwołania',
'mobile.calendarSourceTournaments': 'Turnieje',
'mobile.calendarSourceMatches': 'Mecze ligowe',
'mobile.calendarSourceOfficial': 'Oficjalne starty',
'mobile.calendarSourceHolidays': 'Święta / ferie',
'mobile.calendarLegendTraining': 'Trening',
'mobile.calendarLegendTournament': 'Turniej',
'mobile.calendarLegendOfficial': 'Start',
'mobile.calendarLegendMatch': 'Mecz',
'mobile.calendarLegendHoliday': 'Święto',
'mobile.calendarLegendSchool': 'Ferie',
'mobile.calendarLegendCancellation': 'Odwołanie',
'mobile.calendarTitle': 'Kalendarz',
'mobile.calendarSubtitle': 'Treningi, turnieje, mecze i święta w miesiącu.',
'mobile.calendarToday': 'Dziś',
'mobile.calendarAllSourcesFailed': 'Nie udało się wczytać kalendarza.',
'mobile.calendarSourceFailed': 'nie wczytano',
'mobile.calendarLegend': 'Widok',
'mobile.calendarCancellationTitle': 'Trening odwołany',
'mobile.calendarCancellationHint': 'Ukrywa cykliczne terminy treningów w te dni.',
'mobile.calendarCancellationStart': 'Data (RRRR-MM-DD)',
'mobile.calendarCancellationEnd': 'Do (opcjonalnie)',
'mobile.calendarCancellationReason': 'Powód',
'mobile.calendarCancellationSave': 'Zapisz',
'mobile.calendarCancellationInMonth': 'Odwołania w tym miesiącu',
'mobile.calendarGridTitle': 'Miesiąc',
'mobile.calendarAgendaTitle': 'Terminy w miesiącu',
'mobile.calendarAgendaEmpty': 'Brak terminów w tym miesiącu.',
},
ja: {
'mobile.calendarSourceDiary': '日記',
'mobile.calendarSourceTrainingTimes': '練習時間',
'mobile.calendarSourceCancellations': '練習中止',
'mobile.calendarSourceTournaments': '大会',
'mobile.calendarSourceMatches': 'リーグ戦',
'mobile.calendarSourceOfficial': '公式参加',
'mobile.calendarSourceHolidays': '休日・学校休暇',
'mobile.calendarLegendTraining': '練習',
'mobile.calendarLegendTournament': '大会',
'mobile.calendarLegendOfficial': '参加',
'mobile.calendarLegendMatch': 'リーグ戦',
'mobile.calendarLegendHoliday': '祝日',
'mobile.calendarLegendSchool': '学校の休み',
'mobile.calendarLegendCancellation': '中止',
'mobile.calendarTitle': 'カレンダー',
'mobile.calendarSubtitle': '今月の練習・大会・試合・休日。',
'mobile.calendarToday': '今日',
'mobile.calendarAllSourcesFailed': 'カレンダーを読み込めませんでした。',
'mobile.calendarSourceFailed': '未読込',
'mobile.calendarLegend': '表示',
'mobile.calendarCancellationTitle': '練習中止',
'mobile.calendarCancellationHint': 'その日の定例練習時間を非表示にします。',
'mobile.calendarCancellationStart': '日付 (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': '終了日(任意)',
'mobile.calendarCancellationReason': '理由',
'mobile.calendarCancellationSave': '保存',
'mobile.calendarCancellationInMonth': '今月の中止',
'mobile.calendarGridTitle': '月',
'mobile.calendarAgendaTitle': '今月の予定',
'mobile.calendarAgendaEmpty': '今月の予定はありません。',
},
zh: {
'mobile.calendarSourceDiary': '日记',
'mobile.calendarSourceTrainingTimes': '训练时间',
'mobile.calendarSourceCancellations': '训练取消',
'mobile.calendarSourceTournaments': '比赛',
'mobile.calendarSourceMatches': '联赛',
'mobile.calendarSourceOfficial': '官方参赛',
'mobile.calendarSourceHolidays': '假期/节日',
'mobile.calendarLegendTraining': '训练',
'mobile.calendarLegendTournament': '锦标赛',
'mobile.calendarLegendOfficial': '参赛',
'mobile.calendarLegendMatch': '联赛',
'mobile.calendarLegendHoliday': '节日',
'mobile.calendarLegendSchool': '学校假期',
'mobile.calendarLegendCancellation': '取消',
'mobile.calendarTitle': '日历',
'mobile.calendarSubtitle': '本月训练、比赛与假期一览。',
'mobile.calendarToday': '今天',
'mobile.calendarAllSourcesFailed': '无法加载日历数据。',
'mobile.calendarSourceFailed': '未加载',
'mobile.calendarLegend': '显示',
'mobile.calendarCancellationTitle': '训练取消',
'mobile.calendarCancellationHint': '在这些日期隐藏定期训练时间。',
'mobile.calendarCancellationStart': '日期 (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': '结束(可选)',
'mobile.calendarCancellationReason': '原因',
'mobile.calendarCancellationSave': '保存',
'mobile.calendarCancellationInMonth': '本月取消',
'mobile.calendarGridTitle': '月份',
'mobile.calendarAgendaTitle': '本月日程',
'mobile.calendarAgendaEmpty': '本月没有日程。',
},
th: {
'mobile.calendarSourceDiary': 'ไดอารี่',
'mobile.calendarSourceTrainingTimes': 'เวลาฝึก',
'mobile.calendarSourceCancellations': 'ยกเลิกการฝึก',
'mobile.calendarSourceTournaments': 'การแข่งขัน',
'mobile.calendarSourceMatches': 'แมตช์ลีก',
'mobile.calendarSourceOfficial': 'การเข้าร่วมอย่างเป็นทางการ',
'mobile.calendarSourceHolidays': 'วันหยุด/ปิดเทอม',
'mobile.calendarLegendTraining': 'ฝึก',
'mobile.calendarLegendTournament': 'ทัวร์นาเมนต์',
'mobile.calendarLegendOfficial': 'เข้าร่วม',
'mobile.calendarLegendMatch': 'แมตช์',
'mobile.calendarLegendHoliday': 'วันหยุด',
'mobile.calendarLegendSchool': 'ปิดเทอม',
'mobile.calendarLegendCancellation': 'ยกเลิก',
'mobile.calendarTitle': 'ปฏิทิน',
'mobile.calendarSubtitle': 'การฝึก ทัวร์นาเมนต์ แมตช์ และวันหยุดในเดือนนี้',
'mobile.calendarToday': 'วันนี้',
'mobile.calendarAllSourcesFailed': 'โหลดข้อมูลปฏิทินไม่สำเร็จ',
'mobile.calendarSourceFailed': 'ไม่ได้โหลด',
'mobile.calendarLegend': 'แสดง',
'mobile.calendarCancellationTitle': 'ยกเลิกการฝึก',
'mobile.calendarCancellationHint': 'ซ่อนเวลาฝึกประจำในวันเหล่านั้น',
'mobile.calendarCancellationStart': 'วันที่ (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': 'ถึง (ไม่บังคับ)',
'mobile.calendarCancellationReason': 'เหตุผล',
'mobile.calendarCancellationSave': 'บันทึก',
'mobile.calendarCancellationInMonth': 'การยกเลิกในเดือนนี้',
'mobile.calendarGridTitle': 'เดือน',
'mobile.calendarAgendaTitle': 'นัดในเดือนนี้',
'mobile.calendarAgendaEmpty': 'ไม่มีนัดในเดือนนี้',
},
tl: {
'mobile.calendarSourceDiary': 'Diaryo',
'mobile.calendarSourceTrainingTimes': 'Mga oras ng training',
'mobile.calendarSourceCancellations': 'Mga pagkansela',
'mobile.calendarSourceTournaments': 'Mga torneo',
'mobile.calendarSourceMatches': 'Mga laban sa liga',
'mobile.calendarSourceOfficial': 'Opisyal na pakikilahok',
'mobile.calendarSourceHolidays': 'Holiday / bakasyon',
'mobile.calendarLegendTraining': 'Training',
'mobile.calendarLegendTournament': 'Torneo',
'mobile.calendarLegendOfficial': 'Pakikilahok',
'mobile.calendarLegendMatch': 'Laban',
'mobile.calendarLegendHoliday': 'Holiday',
'mobile.calendarLegendSchool': 'Bakasyon sa paaralan',
'mobile.calendarLegendCancellation': 'Kansela',
'mobile.calendarTitle': 'Kalendaryo',
'mobile.calendarSubtitle': 'Training, torneo, liga at holiday sa buwan.',
'mobile.calendarToday': 'Ngayon',
'mobile.calendarAllSourcesFailed': 'Hindi ma-load ang kalendaryo.',
'mobile.calendarSourceFailed': 'hindi na-load',
'mobile.calendarLegend': 'Ipakita',
'mobile.calendarCancellationTitle': 'Kanseladong training',
'mobile.calendarCancellationHint': 'Itinatago ang paulit-ulit na oras ng training sa mga araw na iyon.',
'mobile.calendarCancellationStart': 'Petsa (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': 'Hanggang (opsyonal)',
'mobile.calendarCancellationReason': 'Dahilan',
'mobile.calendarCancellationSave': 'I-save',
'mobile.calendarCancellationInMonth': 'Mga kansela ngayong buwan',
'mobile.calendarGridTitle': 'Buwan',
'mobile.calendarAgendaTitle': 'Mga appointment sa buwan',
'mobile.calendarAgendaEmpty': 'Walang appointment sa buwang ito.',
},
fil: {
'mobile.calendarSourceDiary': 'Tala-arawan',
'mobile.calendarSourceTrainingTimes': 'Mga oras ng pagsasanay',
'mobile.calendarSourceCancellations': 'Mga pagkansela',
'mobile.calendarSourceTournaments': 'Mga torneo',
'mobile.calendarSourceMatches': 'Mga laban sa liga',
'mobile.calendarSourceOfficial': 'Opisyal na pakikilahok',
'mobile.calendarSourceHolidays': 'Holiday / bakasyon',
'mobile.calendarLegendTraining': 'Pagsasanay',
'mobile.calendarLegendTournament': 'Torneo',
'mobile.calendarLegendOfficial': 'Pakikilahok',
'mobile.calendarLegendMatch': 'Laban',
'mobile.calendarLegendHoliday': 'Holiday',
'mobile.calendarLegendSchool': 'Bakasyon sa paaralan',
'mobile.calendarLegendCancellation': 'Kansela',
'mobile.calendarTitle': 'Kalendaryo',
'mobile.calendarSubtitle': 'Pagsasanay, torneo, liga at holiday sa buwan.',
'mobile.calendarToday': 'Ngayon',
'mobile.calendarAllSourcesFailed': 'Hindi ma-load ang kalendaryo.',
'mobile.calendarSourceFailed': 'hindi na-load',
'mobile.calendarLegend': 'Ipakita',
'mobile.calendarCancellationTitle': 'Kinanselang pagsasanay',
'mobile.calendarCancellationHint': 'Itinatago ang paulit-ulit na oras sa mga araw na iyon.',
'mobile.calendarCancellationStart': 'Petsa (YYYY-MM-DD)',
'mobile.calendarCancellationEnd': 'Hanggang (opsyonal)',
'mobile.calendarCancellationReason': 'Dahilan',
'mobile.calendarCancellationSave': 'I-save',
'mobile.calendarCancellationInMonth': 'Mga kansela ngayong buwan',
'mobile.calendarGridTitle': 'Buwan',
'mobile.calendarAgendaTitle': 'Mga appointment sa buwan',
'mobile.calendarAgendaEmpty': 'Walang appointment sa buwang ito.',
},
};
function calendarStringsForLocale(code) {
if (code === 'de' || code === 'de-CH' || code === 'de-extended') return MOBILE_CALENDAR_I18N.de;
if (code.startsWith('en-')) return MOBILE_CALENDAR_I18N.en;
if (code === 'es') return MOBILE_CALENDAR_I18N.es;
if (code === 'fr') return MOBILE_CALENDAR_I18N.fr;
if (code === 'it') return MOBILE_CALENDAR_I18N.it;
if (code === 'pl') return MOBILE_CALENDAR_I18N.pl;
if (code === 'ja') return MOBILE_CALENDAR_I18N.ja;
if (code === 'zh') return MOBILE_CALENDAR_I18N.zh;
if (code === 'th') return MOBILE_CALENDAR_I18N.th;
if (code === 'tl' || code === 'fil') return MOBILE_CALENDAR_I18N[code === 'fil' ? 'fil' : 'tl'];
return MOBILE_CALENDAR_I18N.en;
}
function flatten(obj, prefix = '', out = {}) {
for (const [key, value] of Object.entries(obj || {})) {
const nextKey = prefix ? `${prefix}.${key}` : key;
if (value && typeof value === 'object' && !Array.isArray(value)) {
flatten(value, nextKey, out);
} else if (typeof value === 'string') {
out[nextKey] = value;
}
}
return out;
}
function escapeKotlinString(value) {
return value
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');
}
function kotlinIdentifier(code) {
return code.replace(/[^A-Za-z0-9_]/g, '_');
}
function main() {
const baseJson = JSON.parse(fs.readFileSync(path.join(SOURCE_DIR, 'de.json'), 'utf8'));
const baseFlat = {
...flatten(baseJson),
...MOBILE_STRINGS.de,
};
const localeFiles = fs
.readdirSync(SOURCE_DIR)
.filter((file) => file.endsWith('.json') && !file.endsWith('.backup'))
.sort((a, b) => a.localeCompare(b));
const lines = [];
lines.push('package de.tsschulz.tt_tagebuch.shared.i18n');
lines.push('');
lines.push('data class SupportedLanguage(val code: String, val label: String)');
lines.push('');
lines.push('object MobileStrings {');
lines.push(' const val DEFAULT_LANGUAGE = "de"');
lines.push('');
lines.push(' val supportedLanguages: List<SupportedLanguage> = listOf(');
for (const file of localeFiles) {
const code = path.basename(file, '.json');
lines.push(` SupportedLanguage("${escapeKotlinString(code)}", "${escapeKotlinString(LOCALE_LABELS[code] || code)}"),`);
}
lines.push(' )');
lines.push('');
const baseEntries = Object.entries(baseFlat).sort(([a], [b]) => a.localeCompare(b));
for (const file of localeFiles) {
const code = path.basename(file, '.json');
const localeJson = JSON.parse(fs.readFileSync(path.join(SOURCE_DIR, file), 'utf8'));
const specificMobileStrings = MOBILE_STRINGS[code] || (code.startsWith('en-') ? MOBILE_STRINGS.en : {});
const flat = {
...baseFlat,
...flatten(localeJson),
...specificMobileStrings,
...calendarStringsForLocale(code),
};
lines.push(` private val ${kotlinIdentifier(code)}: Map<String, String> by lazy { mapOf(`);
for (const [key, value] of Object.entries(flat).sort(([a], [b]) => a.localeCompare(b))) {
lines.push(` "${escapeKotlinString(key)}" to "${escapeKotlinString(value)}",`);
}
lines.push(' ) }');
lines.push('');
}
lines.push(` private val fallback: Map<String, String> get() = ${kotlinIdentifier('de')}`);
lines.push('');
lines.push(' fun get(languageCode: String, key: String, fallbackValue: String): String =');
lines.push(' mapFor(languageCode)[key] ?: fallback[key] ?: fallbackValue');
lines.push('');
lines.push(' private fun mapFor(languageCode: String): Map<String, String> = when (languageCode) {');
for (const file of localeFiles) {
const code = path.basename(file, '.json');
lines.push(` "${escapeKotlinString(code)}" -> ${kotlinIdentifier(code)}`);
}
lines.push(' else -> fallback');
lines.push(' }');
lines.push('}');
lines.push('');
fs.mkdirSync(path.dirname(OUT), { recursive: true });
fs.writeFileSync(OUT, lines.join('\n'), 'utf8');
console.log(`Wrote ${OUT} (${localeFiles.length} locales, ${baseEntries.length} keys each)`);
}
main();