#!/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 d’entraî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': 'Aujourd’hui', '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': 'Jusqu’au (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 = 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 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 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 = 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();