diff --git a/public/js/i18n.js b/public/js/i18n.js new file mode 100644 index 0000000..404b932 --- /dev/null +++ b/public/js/i18n.js @@ -0,0 +1,194 @@ +const STRINGS = { + de: { + hint:'Menü → "Zum Startbildschirm" für App-Icon', + emptyLine1:'Noch keine Ziele.',emptyLine2:'Tippe auf + um zu starten.', + noEntry:'Noch kein Eintrag',log:'+', + noch:'Noch',dAbbr:'T',endet:'endet',todayShort:'heute',total:'total', + todayHeading:'Heute',done2:'Gemacht',dailyGoal:'Tagesziel',remaining:'Noch', + history:'Verlauf — heute & gestern bearbeitbar', + legBuf:'Puffer',legDone:'Erreicht',legPartial:'Teilweise',legMissed:'Verpasst', + delGoal:'Ziel löschen', + qbLabel:'Quick-Buchen',openLabel:'Offen',doneToday:'Heute erledigt', + hello:'Hallo {n}!', + confirmDelete:'Ziel wirklich löschen?',errDelete:'Fehler beim Löschen',errSave:'Speicherfehler', + loginTitle:'Anmelden',emailLabel:'E-Mail',passwordLabel:'Passwort',loginBtn:'Anmelden', + forgotPw:'Passwort vergessen?', + loginErrEmpty:'Bitte E-Mail und Passwort eingeben', + loginErrWrong:'Falsche E-Mail oder Passwort',loginErrRate:'Zu viele Versuche',loginErrConn:'Verbindungsfehler', + forgotTitle:'Passwort vergessen',forgotSub:'Wir schicken dir einen Reset-Link', + sendLink:'Link senden',back:'Zurück', + emailSentTitle:'E-Mail gesendet', + emailSentSub:'Falls die Adresse bekannt ist, erhältst du in Kürze einen Reset-Link.', + ok:'OK', + resetTitle:'Neues Passwort',newPwLabel:'Neues Passwort',setPw:'Passwort setzen',min8:'mind. 8 Zeichen', + pwChangedTitle:'Passwort geändert',pwChangedSub:'Du kannst dich jetzt anmelden.', + changeNameTitle:'Name ändern',yourName:'Dein Name',save:'Speichern',cancel:'Abbrechen', + errNameEmpty:'Name darf nicht leer sein',errNameSave:'Fehler beim Speichern',nameSaved:'Name gespeichert', + changePwTitle:'Passwort ändern',currentPwLabel:'Aktuelles Passwort', + newPwConfLabel:'Neues Passwort bestätigen',changePwBtn:'Ändern', + errPwMismatch:'Die neuen Passwörter stimmen nicht überein',pwChanged:'Passwort geändert', + registerTitle:'Konto erstellen',registerSub:'Du wurdest eingeladen', + namePlaceholder:'Wie sollen wir dich nennen?',pwConfLabel:'Passwort bestätigen', + pwPlaceholder:'Passwort wiederholen',registerBtn:'Registrieren', + errPwMismatch2:'Passwörter stimmen nicht überein',errFillAll:'Bitte alle Felder ausfüllen', + newGoalTitle:'Neues Ziel',exerciseLabel:'Übung / Gewohnheit',exercisePlaceholder:'Liegestütz, Plank …', + unitLabel:'Einheit',unitDefault:'Stück',daysLabel:'Dauer in Tagen', + dailyLabel:'Tagesziel',weeklyLabel:'Wochenziel',startGoal:'Ziel starten',errCreate:'Fehler beim Erstellen', + dataMenuTitle:'Daten verwalten',dataMenuSub:'Export, Import und Backup', + exportLabel:'Exportieren',exportSub:'Alle Ziele als JSON-Datei speichern', + importLabel:'Importieren',importSub:'Backup laden oder zusammenführen', + inviteLabel:'Freund einladen',inviteSub:'Einladungslink generieren', + inviteListLabel:'Meine Einladungen',inviteListSub:'Status aller gesendeten Einladungen', + changeName:'Name ändern',changePw:'Passwort ändern',logout:'Abmelden',close:'Schließen', + clearAll:'Alle Daten löschen',clearAllSub:'Kann nicht rückgängig gemacht werden', + confirmClear:'Alle Daten löschen?', + inviteFormTitle:'Freund einladen',inviteFormSub:'Link gilt 7 Tage und kann nur einmal verwendet werden', + inviteNameLabel:'Name (für deine Übersicht)',inviteNamePlaceholder:'z.B. Max', + generateLink:'Link generieren',inviteLinkTitle:'Einladungslink', + copyLink:'Link kopieren',linkCopied:'Link kopiert!',errGenerate:'Fehler beim Generieren', + noInvites:'Noch keine Einladungen verschickt', + statusPending:'Ausstehend',statusUsed:'Angenommen',statusExpired:'Abgelaufen', + errLoad:'Fehler beim Laden', + confirmImport:'{n} Ziel(e) importieren?',importDone:'{n} Ziel(e) importiert.',invalidFormat:'Ungültiges Format', + linkLabel:'Link',doneLabel:'gemacht',ofLabel:'von', + gestern:'Gestern',heute:'Heute', + expiresAt:'läuft ab:',acceptedBy:'→', + adminLabel:'Nutzer',adminColName:'Name',adminColEmail:'E-Mail',adminColRegistered:'Registriert', + }, + en: { + hint:'Menu → "Add to Home Screen" for app icon', + emptyLine1:'No goals yet.',emptyLine2:'Tap + to get started.', + noEntry:'No entries yet',log:'+', + noch:'Left',dAbbr:'d',endet:'ends',todayShort:'today',total:'total', + todayHeading:'Today',done2:'Done',dailyGoal:'Daily goal',remaining:'Left', + history:'History — today & yesterday editable', + legBuf:'Buffer',legDone:'Reached',legPartial:'Partial',legMissed:'Missed', + delGoal:'Delete goal', + qbLabel:'Quick-log',openLabel:'Open',doneToday:'Done today', + hello:'Hello {n}!', + confirmDelete:'Really delete this goal?',errDelete:'Delete failed',errSave:'Save failed', + loginTitle:'Sign in',emailLabel:'E-Mail',passwordLabel:'Password',loginBtn:'Sign in', + forgotPw:'Forgot password?', + loginErrEmpty:'Please enter email and password', + loginErrWrong:'Wrong email or password',loginErrRate:'Too many attempts',loginErrConn:'Connection error', + forgotTitle:'Forgot password',forgotSub:"We'll send you a reset link", + sendLink:'Send link',back:'Back', + emailSentTitle:'Email sent', + emailSentSub:"If the address is registered, you'll receive a reset link shortly.", + ok:'OK', + resetTitle:'New password',newPwLabel:'New password',setPw:'Set password',min8:'at least 8 characters', + pwChangedTitle:'Password changed',pwChangedSub:'You can now sign in.', + changeNameTitle:'Change name',yourName:'Your name',save:'Save',cancel:'Cancel', + errNameEmpty:'Name cannot be empty',errNameSave:'Save failed',nameSaved:'Name saved', + changePwTitle:'Change password',currentPwLabel:'Current password', + newPwConfLabel:'Confirm new password',changePwBtn:'Change', + errPwMismatch:"The new passwords don't match",pwChanged:'Password changed', + registerTitle:'Create account',registerSub:"You've been invited", + namePlaceholder:'What should we call you?',pwConfLabel:'Confirm password', + pwPlaceholder:'Repeat password',registerBtn:'Register', + errPwMismatch2:"Passwords don't match",errFillAll:'Please fill in all fields', + newGoalTitle:'New goal',exerciseLabel:'Exercise / Habit',exercisePlaceholder:'Push-ups, Plank …', + unitLabel:'Unit',unitDefault:'reps',daysLabel:'Duration (days)', + dailyLabel:'Daily goal',weeklyLabel:'Weekly goal',startGoal:'Start goal',errCreate:'Create failed', + dataMenuTitle:'Manage data',dataMenuSub:'Export, Import and Backup', + exportLabel:'Export',exportSub:'Save all goals as JSON file', + importLabel:'Import',importSub:'Load or merge backup', + inviteLabel:'Invite friend',inviteSub:'Generate invite link', + inviteListLabel:'My invitations',inviteListSub:'Status of all sent invitations', + changeName:'Change name',changePw:'Change password',logout:'Sign out',close:'Close', + clearAll:'Delete all data',clearAllSub:'Cannot be undone', + confirmClear:'Delete all data?', + inviteFormTitle:'Invite friend',inviteFormSub:'Link valid 7 days, single use', + inviteNameLabel:'Name (for your reference)',inviteNamePlaceholder:'e.g. Max', + generateLink:'Generate link',inviteLinkTitle:'Invite link', + copyLink:'Copy link',linkCopied:'Link copied!',errGenerate:'Generate failed', + noInvites:'No invitations sent yet', + statusPending:'Pending',statusUsed:'Accepted',statusExpired:'Expired', + errLoad:'Load failed', + confirmImport:'Import {n} goal(s)?',importDone:'{n} goal(s) imported.',invalidFormat:'Invalid format', + linkLabel:'Link',doneLabel:'done',ofLabel:'of', + gestern:'Yesterday',heute:'Today', + expiresAt:'expires:',acceptedBy:'→', + adminLabel:'Users',adminColName:'Name',adminColEmail:'Email',adminColRegistered:'Registered', + }, + pl: { + hint:'Menu → "Dodaj do ekranu głównego" aby zainstalować', + emptyLine1:'Brak celów.',emptyLine2:'Dotknij +, aby zacząć.', + noEntry:'Brak wpisów',log:'+', + noch:'Jeszcze',dAbbr:'d',endet:'kończy',todayShort:'dziś',total:'łącznie', + todayHeading:'Dziś',done2:'Wykonano',dailyGoal:'Cel dzienny',remaining:'Pozostało', + history:'Historia — dziś i wczoraj edytowalne', + legBuf:'Zapas',legDone:'Osiągnięto',legPartial:'Częściowo',legMissed:'Pominięto', + delGoal:'Usuń cel', + qbLabel:'Szybki wpis',openLabel:'Otwarte',doneToday:'Dziś ukończone', + hello:'Cześć {n}!', + confirmDelete:'Na pewno usunąć cel?',errDelete:'Błąd podczas usuwania',errSave:'Błąd zapisu', + loginTitle:'Zaloguj się',emailLabel:'E-Mail',passwordLabel:'Hasło',loginBtn:'Zaloguj', + forgotPw:'Zapomniałeś hasła?', + loginErrEmpty:'Podaj e-mail i hasło', + loginErrWrong:'Błędny e-mail lub hasło',loginErrRate:'Zbyt wiele prób',loginErrConn:'Błąd połączenia', + forgotTitle:'Zapomniane hasło',forgotSub:'Wyślemy Ci link do resetu', + sendLink:'Wyślij link',back:'Wróć', + emailSentTitle:'E-mail wysłany', + emailSentSub:'Jeśli adres jest zarejestrowany, wkrótce otrzymasz link resetujący.', + ok:'OK', + resetTitle:'Nowe hasło',newPwLabel:'Nowe hasło',setPw:'Ustaw hasło',min8:'min. 8 znaków', + pwChangedTitle:'Hasło zmienione',pwChangedSub:'Możesz się teraz zalogować.', + changeNameTitle:'Zmień nazwę',yourName:'Twoje imię',save:'Zapisz',cancel:'Anuluj', + errNameEmpty:'Imię nie może być puste',errNameSave:'Błąd zapisu',nameSaved:'Imię zapisano', + changePwTitle:'Zmień hasło',currentPwLabel:'Aktualne hasło', + newPwConfLabel:'Potwierdź nowe hasło',changePwBtn:'Zmień', + errPwMismatch:'Nowe hasła się nie zgadzają',pwChanged:'Hasło zmienione', + registerTitle:'Utwórz konto',registerSub:'Zostałeś zaproszony', + namePlaceholder:'Jak mamy Cię nazywać?',pwConfLabel:'Potwierdź hasło', + pwPlaceholder:'Powtórz hasło',registerBtn:'Zarejestruj', + errPwMismatch2:'Hasła się nie zgadzają',errFillAll:'Wypełnij wszystkie pola', + newGoalTitle:'Nowy cel',exerciseLabel:'Ćwiczenie / Nawyk',exercisePlaceholder:'Pompki, Plank …', + unitLabel:'Jednostka',unitDefault:'szt.',daysLabel:'Czas trwania (dni)', + dailyLabel:'Cel dzienny',weeklyLabel:'Cel tygodniowy',startGoal:'Rozpocznij cel',errCreate:'Błąd tworzenia', + dataMenuTitle:'Zarządzaj danymi',dataMenuSub:'Eksport, import i kopia zapasowa', + exportLabel:'Eksportuj',exportSub:'Zapisz wszystkie cele jako plik JSON', + importLabel:'Importuj',importSub:'Załaduj lub połącz kopię zapasową', + inviteLabel:'Zaproś znajomego',inviteSub:'Wygeneruj link zaproszenia', + inviteListLabel:'Moje zaproszenia',inviteListSub:'Status wszystkich wysłanych zaproszeń', + changeName:'Zmień nazwę',changePw:'Zmień hasło',logout:'Wyloguj',close:'Zamknij', + clearAll:'Usuń wszystkie dane',clearAllSub:'Nie można cofnąć', + confirmClear:'Usunąć wszystkie dane?', + inviteFormTitle:'Zaproś znajomego',inviteFormSub:'Link ważny 7 dni, jednorazowy', + inviteNameLabel:'Nazwa (dla Twojej ewidencji)',inviteNamePlaceholder:'np. Max', + generateLink:'Wygeneruj link',inviteLinkTitle:'Link zaproszenia', + copyLink:'Kopiuj link',linkCopied:'Link skopiowany!',errGenerate:'Błąd generowania', + noInvites:'Brak wysłanych zaproszeń', + statusPending:'Oczekujący',statusUsed:'Zaakceptowany',statusExpired:'Wygasły', + errLoad:'Błąd ładowania', + confirmImport:'Importować {n} cel(e)?',importDone:'Zaimportowano {n} cel(e).',invalidFormat:'Nieprawidłowy format', + linkLabel:'Link',doneLabel:'wykonano',ofLabel:'z', + gestern:'Wczoraj',heute:'Dziś', + expiresAt:'wygasa:',acceptedBy:'→', + adminLabel:'Użytkownicy',adminColName:'Nazwa',adminColEmail:'E-mail',adminColRegistered:'Rejestracja', + }, +}; + +export let LOCALE = 'de'; + +export function tr(key) { + return (STRINGS[LOCALE] || STRINGS.de)[key] || STRINGS.de[key] || key; +} + +export function ldoc() { + return LOCALE === 'de' ? 'de-DE' : LOCALE === 'pl' ? 'pl-PL' : 'en-GB'; +} + +export function setLocale(lang, save) { + LOCALE = lang; + if (save) localStorage.setItem('zt_locale', lang); +} + +export function applyLocale(userLocale) { + const lang = userLocale || localStorage.getItem('zt_locale'); + if (!lang) { + const nav = (navigator.language || '').slice(0, 2).toLowerCase(); + if (STRINGS[nav]) { LOCALE = nav; return; } + } + if (lang && STRINGS[lang]) LOCALE = lang; +}