Teil 1 des "mehrsprachig variabel"-Umbaus (Speiseplan-Inhalte folgen
in einer späteren Version). Sprachen sind jetzt zur Laufzeit über eine
neue Admin-Seite "Sprachen" verwaltbar statt fest im Code zu stehen:
- Neue Tabellen languages/ui_translations (Migration 0004), bestehende
Englisch-/Polnisch-Texte per generiertem Seed automatisch übernommen
(Migration 0005) - keine manuelle Nacharbeit, nichts geht verloren.
- lib/i18n/flatten.ts: verschachtelte Dict-Objekte (inkl. Arrays wie
weekdays.long) <-> flache Schlüssel-Wert-Paare, Basis für Vorlage-
Export/-Import.
- lib/i18n/template.ts: Vollständigkeits-/Veraltet-Berechnung (Hash
über die DE-Schlüsselliste) und Import-Validierung (Platzhalter wie
{message} müssen erhalten bleiben, unbekannte Schlüssel werden
ignoriert statt Fehler zu werfen).
- getDictionary()/getLocale() (lib/i18n/index.ts) jetzt asynchron und
DB-gestützt für Nicht-Basis-Sprachen, mit Fallback auf Deutsch für
fehlende Schlüssel. Locale-Typ bewusst von festem TypeScript-Tupel
auf string generalisiert (Kompromiss: Compiler prüft Vollständigkeit
nicht mehr automatisch, das übernimmt jetzt die Veraltet-Erkennung
zur Laufzeit).
- LanguageSwitcher/LocaleProvider lesen aktive Sprachen jetzt aus dem
Context statt aus einer festen Konstante; blendet sich bei nur einer
aktiven Sprache automatisch aus.
- Neue Admin-Routen app/api/admin/languages/* (Liste inkl.
Vollständigkeit, Anlegen, Aktivieren/Deaktivieren mit
"mindestens eine muss aktiv bleiben"-Schutz, Löschen außer
Basis-Sprache, Vorlage-Download, Datei-Import) + LanguageManager.tsx.
Vollständig gegen echten DB-Zustand getestet: Vorlage-Export/-Import
inkl. Platzhalter-Validierung, neue Sprache anlegen, auf eine Sprache
reduzieren (Umschalter verschwindet, "letzte Sprache"-Schutz greift),
Veraltet-Erkennung nach simuliertem neuen Schlüssel, Cascade-Delete.
Version 1.10.0.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
575 lines
37 KiB
SQL
575 lines
37 KiB
SQL
-- Einmaliger Seed: bisherige fest im Code hinterlegte Englisch-/Polnisch-
|
||
-- Übersetzungen (lib/i18n/dictionaries.ts) als Sprachpakete in
|
||
-- ui_translations überführen, damit nach dem Umbau auf das dynamische
|
||
-- Sprachpaket-System (siehe 0004_languages.sql) nichts verloren geht.
|
||
|
||
insert into ui_translations (language_code, key, value) values
|
||
('en', 'common.save', 'Save'),
|
||
('en', 'common.cancel', 'Cancel'),
|
||
('en', 'common.delete', 'Delete'),
|
||
('en', 'common.edit', 'Edit'),
|
||
('en', 'common.add', 'Add'),
|
||
('en', 'common.back', 'Back'),
|
||
('en', 'common.login', 'Log in'),
|
||
('en', 'common.logout', 'Log out'),
|
||
('en', 'common.loading', 'Loading…'),
|
||
('en', 'common.you', '(you)'),
|
||
('en', 'common.name', 'Name'),
|
||
('en', 'common.email', 'Email address'),
|
||
('en', 'common.password', 'Password'),
|
||
('en', 'common.role', 'Role'),
|
||
('en', 'common.roleAdmin', 'Administrator'),
|
||
('en', 'common.roleStaff', 'Canteen staff'),
|
||
('en', 'weekdays.long.0', 'Monday'),
|
||
('en', 'weekdays.long.1', 'Tuesday'),
|
||
('en', 'weekdays.long.2', 'Wednesday'),
|
||
('en', 'weekdays.long.3', 'Thursday'),
|
||
('en', 'weekdays.long.4', 'Friday'),
|
||
('en', 'weekdays.short.0', 'Mon'),
|
||
('en', 'weekdays.short.1', 'Tue'),
|
||
('en', 'weekdays.short.2', 'Wed'),
|
||
('en', 'weekdays.short.3', 'Thu'),
|
||
('en', 'weekdays.short.4', 'Fri'),
|
||
('en', 'dateLocale', 'en-GB'),
|
||
('en', 'header.canteen', 'Canteen'),
|
||
('en', 'header.menuGroup', 'Menu'),
|
||
('en', 'header.weekPlan', 'Weekly plan'),
|
||
('en', 'header.sections', 'Meal times'),
|
||
('en', 'header.dishes', 'Dishes'),
|
||
('en', 'header.labels', 'Allergens & Additives'),
|
||
('en', 'header.adminGroup', 'Administration'),
|
||
('en', 'header.users', 'Users'),
|
||
('en', 'header.design', 'Design'),
|
||
('en', 'header.languages', 'Languages'),
|
||
('en', 'header.suggestions', 'Suggestions'),
|
||
('en', 'header.openSuggestions', '{count} open dish suggestions'),
|
||
('en', 'header.viewMenu', 'View menu'),
|
||
('en', 'header.myAccount', 'My account'),
|
||
('en', 'header.menuToggle', 'Open/close menu'),
|
||
('en', 'login.title', 'Sign in'),
|
||
('en', 'login.submit', 'Log in'),
|
||
('en', 'login.or', 'or'),
|
||
('en', 'login.passkeyButton', 'Sign in with passkey'),
|
||
('en', 'login.ssoButton', 'Sign in with Single Sign-On'),
|
||
('en', 'login.backToMenu', '← Back to menu'),
|
||
('en', 'login.errorInvalidCredentials', 'Email address or password is incorrect.'),
|
||
('en', 'login.errorPasskeyCancelled', 'Passkey sign-in was cancelled.'),
|
||
('en', 'login.errorGeneric', 'Sign-in failed: {message}'),
|
||
('en', 'login.errorPasswordDisabled', 'Password sign-in is disabled for this account. Please sign in with a passkey.'),
|
||
('en', 'login.errorOidcInvalidState', 'The SSO sign-in expired or was invalid. Please try again.'),
|
||
('en', 'login.errorOidcUnknownUser', 'No account exists for this email address. Please contact your administrator.'),
|
||
('en', 'login.errorOidcFailed', 'SSO sign-in failed. Please try again.'),
|
||
('en', 'publicMenu.prevWeek', '← Previous week'),
|
||
('en', 'publicMenu.nextWeek', 'Next week →'),
|
||
('en', 'publicMenu.prevWeekShort', '← Week'),
|
||
('en', 'publicMenu.nextWeekShort', 'Week →'),
|
||
('en', 'publicMenu.today', 'Today'),
|
||
('en', 'publicMenu.dayTab', 'Day'),
|
||
('en', 'publicMenu.weekTab', 'Week'),
|
||
('en', 'publicMenu.allergens', 'Allergens'),
|
||
('en', 'publicMenu.additives', 'Additives'),
|
||
('en', 'publicMenu.labelingNoticeTitle', 'Allergens & additives'),
|
||
('en', 'publicMenu.suggestDish', 'Suggest a dish'),
|
||
('en', 'publicMenu.noEntry', '–'),
|
||
('en', 'publicMenu.rssFeed', 'Subscribe to RSS feed'),
|
||
('en', 'weekEditor.title', 'Weekly plan'),
|
||
('en', 'weekEditor.loading', 'Loading weekly plan…'),
|
||
('en', 'weekEditor.changeDish', 'Choose a different dish'),
|
||
('en', 'weekEditor.addMeal', 'Add meal'),
|
||
('en', 'weekEditor.newDishesHint', 'Add new dishes under {link}.'),
|
||
('en', 'weekEditor.newDishesLinkLabel', 'Dishes'),
|
||
('en', 'weekEditor.loadError', 'Loading failed: {message}'),
|
||
('en', 'weekEditor.duplicateEntry', 'This dish is already assigned on this day.'),
|
||
('en', 'weekEditor.saveError', 'Saving failed: {message}'),
|
||
('en', 'weekEditor.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'dishManager.title', 'Dishes'),
|
||
('en', 'dishManager.newDish', 'New dish'),
|
||
('en', 'dishManager.editDish', 'Edit dish'),
|
||
('en', 'dishManager.nameLabel', 'Name'),
|
||
('en', 'dishManager.nameEnLabel', 'Name (English)'),
|
||
('en', 'dishManager.namePlLabel', 'Name (Polish)'),
|
||
('en', 'dishManager.autoTranslate', 'Auto-translate'),
|
||
('en', 'dishManager.translating', 'Translating…'),
|
||
('en', 'dishManager.translated', 'Translated.'),
|
||
('en', 'dishManager.priceLabel', 'Price in €'),
|
||
('en', 'dishManager.pricePlaceholder', '3.50'),
|
||
('en', 'dishManager.searchPlaceholder', 'Search dishes…'),
|
||
('en', 'dishManager.selectAll', 'Select all'),
|
||
('en', 'dishManager.selectedCount', '{count} selected'),
|
||
('en', 'dishManager.deleteSelected', 'Delete selected'),
|
||
('en', 'dishManager.deleteSelectedCount', 'Delete selected ({count})'),
|
||
('en', 'dishManager.empty', 'No dishes yet.'),
|
||
('en', 'dishManager.noResults', 'No results for “{query}”.'),
|
||
('en', 'dishManager.confirmDelete', 'Really delete “{name}”? The dish will also be removed from all weekly plans.'),
|
||
('en', 'dishManager.confirmDeleteSelected', 'Really delete {count} dish(es)? They will also be removed from all weekly plans.'),
|
||
('en', 'dishManager.invalidPrice', 'Please enter a valid price, e.g. 3.50'),
|
||
('en', 'dishManager.loadError', 'Loading failed: {message}'),
|
||
('en', 'dishManager.saveError', 'Saving failed: {message}'),
|
||
('en', 'dishManager.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'dishManager.exportButton', 'Export'),
|
||
('en', 'dishManager.importButton', 'Import'),
|
||
('en', 'dishManager.exportError', 'Export failed: {message}'),
|
||
('en', 'dishManager.importError', 'Import failed: {message}'),
|
||
('en', 'dishManager.importSuccess', '{created} created, {updated} updated.'),
|
||
('en', 'dishManager.importSkipped', '{count} row(s) skipped: {reasons}'),
|
||
('en', 'dishManager.importSkippedRow', 'Row {row}: {reason}'),
|
||
('en', 'dishManager.labelingDisabledHint', 'Allergens/additives are currently disabled (notice mode active, see "Allergens & additives").'),
|
||
('en', 'codeList.allergensTitle', 'Allergens'),
|
||
('en', 'codeList.additivesTitle', 'Additives'),
|
||
('en', 'codeList.allergenCodeLabel', 'Code (e.g. A)'),
|
||
('en', 'codeList.additiveCodeLabel', 'Code (e.g. 1)'),
|
||
('en', 'codeList.nameLabel', 'Name'),
|
||
('en', 'codeList.empty', 'No entries.'),
|
||
('en', 'codeList.confirmDelete', 'Really delete “{code} – {name}”?'),
|
||
('en', 'codeList.duplicateCode', 'The code “{code}” already exists.'),
|
||
('en', 'codeList.loadError', 'Loading failed: {message}'),
|
||
('en', 'codeList.saveError', 'Saving failed: {message}'),
|
||
('en', 'codeList.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'codeList.exportButton', 'Export'),
|
||
('en', 'codeList.importButton', 'Import'),
|
||
('en', 'codeList.exportError', 'Export failed: {message}'),
|
||
('en', 'codeList.importError', 'Import failed: {message}'),
|
||
('en', 'codeList.importSuccess', '{created} created, {updated} updated.'),
|
||
('en', 'codeList.importSkipped', '{count} row(s) skipped: {reasons}'),
|
||
('en', 'codeList.importSkippedRow', 'Row {row}: {reason}'),
|
||
('en', 'sectionManager.title', 'Meal times'),
|
||
('en', 'sectionManager.newSection', 'New meal time'),
|
||
('en', 'sectionManager.editSection', 'Edit meal time'),
|
||
('en', 'sectionManager.nameLabel', 'Name'),
|
||
('en', 'sectionManager.nameEnLabel', 'Name (English)'),
|
||
('en', 'sectionManager.namePlLabel', 'Name (Polish)'),
|
||
('en', 'sectionManager.timeFrom', 'From'),
|
||
('en', 'sectionManager.timeTo', 'To'),
|
||
('en', 'sectionManager.moveUp', 'Move up'),
|
||
('en', 'sectionManager.moveDown', 'Move down'),
|
||
('en', 'sectionManager.empty', 'No meal times yet.'),
|
||
('en', 'sectionManager.confirmDelete', 'Really delete “{name}”? Assigned dishes will also be removed from all weekly plans.'),
|
||
('en', 'sectionManager.loadError', 'Loading failed: {message}'),
|
||
('en', 'sectionManager.saveError', 'Saving failed: {message}'),
|
||
('en', 'sectionManager.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'userManager.title', 'Users'),
|
||
('en', 'userManager.newUser', 'New user'),
|
||
('en', 'userManager.editUser', 'Edit user'),
|
||
('en', 'userManager.newPasswordLabel', 'New password (optional)'),
|
||
('en', 'userManager.repeatPasswordLabel', 'Repeat password'),
|
||
('en', 'userManager.passwordMismatch', 'The passwords do not match.'),
|
||
('en', 'userManager.confirmDelete', 'Really delete user “{email}”? Access will be permanently removed.'),
|
||
('en', 'userManager.loadError', 'Loading failed: {message}'),
|
||
('en', 'designEditor.title', 'Design'),
|
||
('en', 'designEditor.logoTitle', 'Logo'),
|
||
('en', 'designEditor.logoHint', 'The logo is displayed centered above the menu.'),
|
||
('en', 'designEditor.logoNone', 'No logo set.'),
|
||
('en', 'designEditor.logoUpload', 'Upload logo'),
|
||
('en', 'designEditor.logoRemove', 'Remove logo'),
|
||
('en', 'designEditor.logoUpdated', 'Logo updated.'),
|
||
('en', 'designEditor.logoRemoved', 'Logo removed.'),
|
||
('en', 'designEditor.faviconTitle', 'Favicon'),
|
||
('en', 'designEditor.faviconHint', 'Shown as the icon in the browser tab. Without a custom favicon, the application''s default icon is used.'),
|
||
('en', 'designEditor.faviconNone', 'No custom favicon set.'),
|
||
('en', 'designEditor.faviconUpload', 'Upload favicon'),
|
||
('en', 'designEditor.faviconRemove', 'Remove favicon'),
|
||
('en', 'designEditor.faviconUpdated', 'Favicon updated.'),
|
||
('en', 'designEditor.faviconRemoved', 'Favicon removed.'),
|
||
('en', 'designEditor.invalidImage', 'Please choose an image file (PNG, JPG, SVG, WebP, ICO).'),
|
||
('en', 'designEditor.colorsTitle', 'Colors'),
|
||
('en', 'designEditor.colorsSave', 'Save colors'),
|
||
('en', 'designEditor.colorsReset', 'Reset to default'),
|
||
('en', 'designEditor.colorsSaved', 'Colors saved.'),
|
||
('en', 'designEditor.colorPrimary', 'Primary color (table header)'),
|
||
('en', 'designEditor.colorPrimaryDark', 'Primary color, dark (internal top bar)'),
|
||
('en', 'designEditor.colorAccent', 'Accent color'),
|
||
('en', 'designEditor.colorHeaderText', 'Table header text color'),
|
||
('en', 'designEditor.colorRowBg', 'Cell background'),
|
||
('en', 'designEditor.colorRowBgAlt', 'Cell background (alternating)'),
|
||
('en', 'designEditor.colorAddBtn', '“Add” button'),
|
||
('en', 'designEditor.colorDeleteBtn', '“Delete” button'),
|
||
('en', 'designEditor.loadError', 'Loading failed: {message}'),
|
||
('en', 'designEditor.saveError', 'Saving failed: {message}'),
|
||
('en', 'designEditor.uploadError', 'Upload failed: {message}'),
|
||
('en', 'designEditor.confirmRemoveLogo', 'Really remove the logo?'),
|
||
('en', 'designEditor.confirmRemoveFavicon', 'Really remove the favicon?'),
|
||
('en', 'labelingNotice.title', 'Allergen & additive display'),
|
||
('en', 'labelingNotice.hint', 'By default every guest sees a detailed allergen/additive selection per dish. Alternatively, only a notice text can be shown instead – the per-dish selection is then also hidden in the dish manager.'),
|
||
('en', 'labelingNotice.modeDetailed', 'Show detailed selection per dish'),
|
||
('en', 'labelingNotice.modeNotice', 'Show only notice text'),
|
||
('en', 'labelingNotice.noticeDeLabel', 'Notice text (German)'),
|
||
('en', 'labelingNotice.noticeEnLabel', 'Notice text (English)'),
|
||
('en', 'labelingNotice.noticePlLabel', 'Notice text (Polish)'),
|
||
('en', 'labelingNotice.save', 'Save'),
|
||
('en', 'labelingNotice.saved', 'Saved.'),
|
||
('en', 'labelingNotice.loadError', 'Loading failed: {message}'),
|
||
('en', 'labelingNotice.saveError', 'Saving failed: {message}'),
|
||
('en', 'passkeys.title', 'Passkeys'),
|
||
('en', 'passkeys.hint', 'With a passkey (fingerprint, face recognition, or a security key) you can sign in without a password in the future. Passkeys are bound to a domain – after a domain change they need to be registered again.'),
|
||
('en', 'passkeys.register', 'Register new passkey'),
|
||
('en', 'passkeys.empty', 'No passkeys registered yet.'),
|
||
('en', 'passkeys.createdOn', '– created on {date}'),
|
||
('en', 'passkeys.registered', 'Passkey registered successfully.'),
|
||
('en', 'passkeys.registerError', 'Registration failed: {message}'),
|
||
('en', 'passkeys.registerCancelled', 'Passkey registration was cancelled.'),
|
||
('en', 'passkeys.loadError', 'Could not load passkeys: {message}'),
|
||
('en', 'passkeys.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'passkeys.confirmDelete', 'Really delete this passkey?'),
|
||
('en', 'account.title', 'My account'),
|
||
('en', 'account.personalTitle', 'Personal details'),
|
||
('en', 'account.saveName', 'Save name'),
|
||
('en', 'account.nameSaved', 'Name saved.'),
|
||
('en', 'account.nameRequired', 'Please enter a name.'),
|
||
('en', 'account.emailTitle', 'Email address'),
|
||
('en', 'account.changeEmail', 'Change email address'),
|
||
('en', 'account.emailChanged', 'Email address changed.'),
|
||
('en', 'account.passwordTitle', 'Change password'),
|
||
('en', 'account.currentPassword', 'Current password (for verification)'),
|
||
('en', 'account.newPassword', 'New password'),
|
||
('en', 'account.newPasswordRepeat', 'Repeat new password'),
|
||
('en', 'account.changePassword', 'Change password'),
|
||
('en', 'account.passwordChanged', 'Password changed.'),
|
||
('en', 'account.passwordMismatch', 'The passwords do not match.'),
|
||
('en', 'account.passwordTooShort', 'The new password must be at least 8 characters long.'),
|
||
('en', 'account.passwordAuthTitle', 'Password sign-in'),
|
||
('en', 'account.passwordAuthHint', 'You can disable email and password sign-in for your account and sign in exclusively with a passkey in the future. At least one passkey must be registered for this.'),
|
||
('en', 'account.passwordAuthCheckbox', 'Email and password sign-in allowed'),
|
||
('en', 'account.passwordAuthEnabled', 'Password sign-in enabled.'),
|
||
('en', 'account.passwordAuthDisabled', 'Password sign-in disabled.'),
|
||
('en', 'account.passwordAuthNeedsPasskey', 'You can''t disable password sign-in without a registered passkey – you would lock yourself out. Register a passkey below first.'),
|
||
('en', 'account.confirmDisablePasswordAuth', 'Really disable email and password sign-in? Afterwards you can only sign in with a passkey.'),
|
||
('en', 'account.wrongCurrentPassword', 'The current password is incorrect.'),
|
||
('en', 'account.emailAlreadyUsed', 'This email address is already in use.'),
|
||
('en', 'account.saveError', 'Saving failed: {message}'),
|
||
('en', 'account.changeError', 'Change failed: {message}'),
|
||
('en', 'suggestions.formTitle', 'Suggest a dish'),
|
||
('en', 'suggestions.formHint', 'Missing a dish on the menu? Suggest it here – the canteen team will review your suggestion.'),
|
||
('en', 'suggestions.nameLabel', 'Dish name'),
|
||
('en', 'suggestions.descriptionLabel', 'Description / ingredients (optional)'),
|
||
('en', 'suggestions.submit', 'Submit suggestion'),
|
||
('en', 'suggestions.submitting', 'Submitting…'),
|
||
('en', 'suggestions.success', 'Thanks! Your suggestion has been submitted.'),
|
||
('en', 'suggestions.duplicate', 'This dish already exists on the menu.'),
|
||
('en', 'suggestions.genericError', 'Submission failed: {message}'),
|
||
('en', 'suggestions.nameRequired', 'Please enter a name.'),
|
||
('en', 'suggestions.backToMenu', '← Back to menu'),
|
||
('en', 'suggestions.reviewTitle', 'Dish suggestions'),
|
||
('en', 'suggestions.reviewEmpty', 'No suggestions yet.'),
|
||
('en', 'suggestions.accept', 'Add as dish'),
|
||
('en', 'suggestions.reject', 'Reject'),
|
||
('en', 'suggestions.confirmReject', 'Really reject and delete the suggestion “{name}”?'),
|
||
('en', 'suggestions.accepted', 'Added as dish.'),
|
||
('en', 'suggestions.rejected', 'Suggestion rejected.'),
|
||
('en', 'suggestions.loadError', 'Loading failed: {message}'),
|
||
('en', 'suggestions.actionError', 'Action failed: {message}'),
|
||
('en', 'suggestions.submittedOn', 'submitted on {date}'),
|
||
('en', 'languageManager.title', 'Languages'),
|
||
('en', 'languageManager.hint', 'German is the fixed base language and always complete. Additional languages are imported as a translation file and automatically filled in with German wherever a term is missing.'),
|
||
('en', 'languageManager.codeColumn', 'Code'),
|
||
('en', 'languageManager.labelColumn', 'Display name'),
|
||
('en', 'languageManager.activeColumn', 'Active'),
|
||
('en', 'languageManager.completenessColumn', 'Completeness'),
|
||
('en', 'languageManager.baseBadge', 'Base language'),
|
||
('en', 'languageManager.complete', 'Complete'),
|
||
('en', 'languageManager.completeness', '{percent}% translated'),
|
||
('en', 'languageManager.outdated', '{count} new term(s) since last import'),
|
||
('en', 'languageManager.downloadTemplate', 'Download template'),
|
||
('en', 'languageManager.uploadTranslation', 'Upload translation'),
|
||
('en', 'languageManager.newLanguageTitle', 'Add new language'),
|
||
('en', 'languageManager.newLanguageCodeLabel', 'Language code (e.g. fr)'),
|
||
('en', 'languageManager.newLanguageLabelLabel', 'Display name (e.g. Français)'),
|
||
('en', 'languageManager.create', 'Create'),
|
||
('en', 'languageManager.delete', 'Delete'),
|
||
('en', 'languageManager.confirmDelete', 'Really delete “{label}”? All translations for this language will be lost.'),
|
||
('en', 'languageManager.cannotDeleteBase', 'The base language cannot be deleted.'),
|
||
('en', 'languageManager.cannotDeactivateLast', 'At least one language must stay active.'),
|
||
('en', 'languageManager.loadError', 'Loading failed: {message}'),
|
||
('en', 'languageManager.saveError', 'Saving failed: {message}'),
|
||
('en', 'languageManager.deleteError', 'Deleting failed: {message}'),
|
||
('en', 'languageManager.importSuccess', '{count} terms imported.'),
|
||
('en', 'languageManager.importError', 'Import failed: {message}'),
|
||
('en', 'languageManager.importInvalidFile', 'The file could not be read (invalid JSON).'),
|
||
('en', 'languageManager.importMissingPlaceholder', '“{key}”: placeholder {placeholder} missing in the translation – row skipped.'),
|
||
('en', 'languageManager.duplicateCode', 'The language code “{code}” already exists.'),
|
||
('en', 'languageManager.invalidCode', 'Please enter a language code of 2-5 lowercase letters.');
|
||
|
||
insert into ui_translations (language_code, key, value) values
|
||
('pl', 'common.save', 'Zapisz'),
|
||
('pl', 'common.cancel', 'Anuluj'),
|
||
('pl', 'common.delete', 'Usuń'),
|
||
('pl', 'common.edit', 'Edytuj'),
|
||
('pl', 'common.add', 'Dodaj'),
|
||
('pl', 'common.back', 'Wstecz'),
|
||
('pl', 'common.login', 'Zaloguj się'),
|
||
('pl', 'common.logout', 'Wyloguj się'),
|
||
('pl', 'common.loading', 'Wczytywanie…'),
|
||
('pl', 'common.you', '(ty)'),
|
||
('pl', 'common.name', 'Nazwa'),
|
||
('pl', 'common.email', 'Adres e-mail'),
|
||
('pl', 'common.password', 'Hasło'),
|
||
('pl', 'common.role', 'Rola'),
|
||
('pl', 'common.roleAdmin', 'Administrator'),
|
||
('pl', 'common.roleStaff', 'Personel stołówki'),
|
||
('pl', 'weekdays.long.0', 'Poniedziałek'),
|
||
('pl', 'weekdays.long.1', 'Wtorek'),
|
||
('pl', 'weekdays.long.2', 'Środa'),
|
||
('pl', 'weekdays.long.3', 'Czwartek'),
|
||
('pl', 'weekdays.long.4', 'Piątek'),
|
||
('pl', 'weekdays.short.0', 'Pon'),
|
||
('pl', 'weekdays.short.1', 'Wto'),
|
||
('pl', 'weekdays.short.2', 'Śro'),
|
||
('pl', 'weekdays.short.3', 'Czw'),
|
||
('pl', 'weekdays.short.4', 'Pią'),
|
||
('pl', 'dateLocale', 'pl-PL'),
|
||
('pl', 'header.canteen', 'Stołówka'),
|
||
('pl', 'header.menuGroup', 'Jadłospis'),
|
||
('pl', 'header.weekPlan', 'Plan tygodnia'),
|
||
('pl', 'header.sections', 'Pory posiłków'),
|
||
('pl', 'header.dishes', 'Dania'),
|
||
('pl', 'header.labels', 'Alergeny i dodatki'),
|
||
('pl', 'header.adminGroup', 'Administracja'),
|
||
('pl', 'header.users', 'Użytkownicy'),
|
||
('pl', 'header.design', 'Wygląd'),
|
||
('pl', 'header.languages', 'Języki'),
|
||
('pl', 'header.suggestions', 'Propozycje'),
|
||
('pl', 'header.openSuggestions', '{count} otwartych propozycji dań'),
|
||
('pl', 'header.viewMenu', 'Zobacz jadłospis'),
|
||
('pl', 'header.myAccount', 'Moje konto'),
|
||
('pl', 'header.menuToggle', 'Otwórz/zamknij menu'),
|
||
('pl', 'login.title', 'Logowanie'),
|
||
('pl', 'login.submit', 'Zaloguj się'),
|
||
('pl', 'login.or', 'lub'),
|
||
('pl', 'login.passkeyButton', 'Zaloguj się kluczem dostępu'),
|
||
('pl', 'login.ssoButton', 'Zaloguj się przez Single Sign-On'),
|
||
('pl', 'login.backToMenu', '← Powrót do jadłospisu'),
|
||
('pl', 'login.errorInvalidCredentials', 'Nieprawidłowy adres e-mail lub hasło.'),
|
||
('pl', 'login.errorPasskeyCancelled', 'Logowanie kluczem dostępu zostało anulowane.'),
|
||
('pl', 'login.errorGeneric', 'Logowanie nie powiodło się: {message}'),
|
||
('pl', 'login.errorPasswordDisabled', 'Logowanie hasłem jest wyłączone dla tego konta. Zaloguj się kluczem dostępu.'),
|
||
('pl', 'login.errorOidcInvalidState', 'Logowanie SSO wygasło lub jest nieprawidłowe. Spróbuj ponownie.'),
|
||
('pl', 'login.errorOidcUnknownUser', 'Nie istnieje konto dla tego adresu e-mail. Skontaktuj się z administracją.'),
|
||
('pl', 'login.errorOidcFailed', 'Logowanie SSO nie powiodło się. Spróbuj ponownie.'),
|
||
('pl', 'publicMenu.prevWeek', '← Poprzedni tydzień'),
|
||
('pl', 'publicMenu.nextWeek', 'Następny tydzień →'),
|
||
('pl', 'publicMenu.prevWeekShort', '← Tydzień'),
|
||
('pl', 'publicMenu.nextWeekShort', 'Tydzień →'),
|
||
('pl', 'publicMenu.today', 'Dziś'),
|
||
('pl', 'publicMenu.dayTab', 'Dzień'),
|
||
('pl', 'publicMenu.weekTab', 'Tydzień'),
|
||
('pl', 'publicMenu.allergens', 'Alergeny'),
|
||
('pl', 'publicMenu.additives', 'Dodatki'),
|
||
('pl', 'publicMenu.labelingNoticeTitle', 'Alergeny i dodatki'),
|
||
('pl', 'publicMenu.suggestDish', 'Zaproponuj danie'),
|
||
('pl', 'publicMenu.noEntry', '–'),
|
||
('pl', 'publicMenu.rssFeed', 'Subskrybuj kanał RSS'),
|
||
('pl', 'weekEditor.title', 'Plan tygodnia'),
|
||
('pl', 'weekEditor.loading', 'Wczytywanie planu tygodnia…'),
|
||
('pl', 'weekEditor.changeDish', 'Wybierz inne danie'),
|
||
('pl', 'weekEditor.addMeal', 'Dodaj posiłek'),
|
||
('pl', 'weekEditor.newDishesHint', 'Nowe dania możesz dodać w zakładce {link}.'),
|
||
('pl', 'weekEditor.newDishesLinkLabel', 'Dania'),
|
||
('pl', 'weekEditor.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'weekEditor.duplicateEntry', 'To danie jest już przypisane na ten dzień.'),
|
||
('pl', 'weekEditor.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'weekEditor.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'dishManager.title', 'Dania'),
|
||
('pl', 'dishManager.newDish', 'Nowe danie'),
|
||
('pl', 'dishManager.editDish', 'Edytuj danie'),
|
||
('pl', 'dishManager.nameLabel', 'Nazwa'),
|
||
('pl', 'dishManager.nameEnLabel', 'Nazwa (angielski)'),
|
||
('pl', 'dishManager.namePlLabel', 'Nazwa (polski)'),
|
||
('pl', 'dishManager.autoTranslate', 'Przetłumacz automatycznie'),
|
||
('pl', 'dishManager.translating', 'Tłumaczenie…'),
|
||
('pl', 'dishManager.translated', 'Przetłumaczono.'),
|
||
('pl', 'dishManager.priceLabel', 'Cena w €'),
|
||
('pl', 'dishManager.pricePlaceholder', '3,50'),
|
||
('pl', 'dishManager.searchPlaceholder', 'Szukaj dania…'),
|
||
('pl', 'dishManager.selectAll', 'Zaznacz wszystko'),
|
||
('pl', 'dishManager.selectedCount', 'Zaznaczono: {count}'),
|
||
('pl', 'dishManager.deleteSelected', 'Usuń zaznaczone'),
|
||
('pl', 'dishManager.deleteSelectedCount', 'Usuń zaznaczone ({count})'),
|
||
('pl', 'dishManager.empty', 'Nie dodano jeszcze żadnych dań.'),
|
||
('pl', 'dishManager.noResults', 'Brak wyników dla „{query}”.'),
|
||
('pl', 'dishManager.confirmDelete', 'Czy na pewno usunąć „{name}”? Danie zostanie również usunięte ze wszystkich planów tygodniowych.'),
|
||
('pl', 'dishManager.confirmDeleteSelected', 'Czy na pewno usunąć {count} danie/dania? Zostaną również usunięte ze wszystkich planów tygodniowych.'),
|
||
('pl', 'dishManager.invalidPrice', 'Podaj prawidłową cenę, np. 3,50'),
|
||
('pl', 'dishManager.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'dishManager.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'dishManager.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'dishManager.exportButton', 'Eksportuj'),
|
||
('pl', 'dishManager.importButton', 'Importuj'),
|
||
('pl', 'dishManager.exportError', 'Eksport nie powiódł się: {message}'),
|
||
('pl', 'dishManager.importError', 'Import nie powiódł się: {message}'),
|
||
('pl', 'dishManager.importSuccess', 'Utworzono: {created}, zaktualizowano: {updated}.'),
|
||
('pl', 'dishManager.importSkipped', 'Pominięto wierszy: {count}: {reasons}'),
|
||
('pl', 'dishManager.importSkippedRow', 'Wiersz {row}: {reason}'),
|
||
('pl', 'dishManager.labelingDisabledHint', 'Alergeny/dodatki są obecnie wyłączone (aktywny tryb komunikatu, patrz „Alergeny i dodatki”).'),
|
||
('pl', 'codeList.allergensTitle', 'Alergeny'),
|
||
('pl', 'codeList.additivesTitle', 'Dodatki'),
|
||
('pl', 'codeList.allergenCodeLabel', 'Kod (np. A)'),
|
||
('pl', 'codeList.additiveCodeLabel', 'Kod (np. 1)'),
|
||
('pl', 'codeList.nameLabel', 'Nazwa'),
|
||
('pl', 'codeList.empty', 'Brak wpisów.'),
|
||
('pl', 'codeList.confirmDelete', 'Czy na pewno usunąć „{code} – {name}”?'),
|
||
('pl', 'codeList.duplicateCode', 'Kod „{code}” już istnieje.'),
|
||
('pl', 'codeList.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'codeList.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'codeList.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'codeList.exportButton', 'Eksportuj'),
|
||
('pl', 'codeList.importButton', 'Importuj'),
|
||
('pl', 'codeList.exportError', 'Eksport nie powiódł się: {message}'),
|
||
('pl', 'codeList.importError', 'Import nie powiódł się: {message}'),
|
||
('pl', 'codeList.importSuccess', 'Utworzono: {created}, zaktualizowano: {updated}.'),
|
||
('pl', 'codeList.importSkipped', 'Pominięto wierszy: {count}: {reasons}'),
|
||
('pl', 'codeList.importSkippedRow', 'Wiersz {row}: {reason}'),
|
||
('pl', 'sectionManager.title', 'Pory posiłków'),
|
||
('pl', 'sectionManager.newSection', 'Nowa pora posiłku'),
|
||
('pl', 'sectionManager.editSection', 'Edytuj porę posiłku'),
|
||
('pl', 'sectionManager.nameLabel', 'Nazwa'),
|
||
('pl', 'sectionManager.nameEnLabel', 'Nazwa (angielski)'),
|
||
('pl', 'sectionManager.namePlLabel', 'Nazwa (polski)'),
|
||
('pl', 'sectionManager.timeFrom', 'Od'),
|
||
('pl', 'sectionManager.timeTo', 'Do'),
|
||
('pl', 'sectionManager.moveUp', 'Przesuń w górę'),
|
||
('pl', 'sectionManager.moveDown', 'Przesuń w dół'),
|
||
('pl', 'sectionManager.empty', 'Nie dodano jeszcze żadnych pór posiłków.'),
|
||
('pl', 'sectionManager.confirmDelete', 'Czy na pewno usunąć „{name}”? Przypisane dania zostaną również usunięte ze wszystkich planów tygodniowych.'),
|
||
('pl', 'sectionManager.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'sectionManager.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'sectionManager.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'userManager.title', 'Użytkownicy'),
|
||
('pl', 'userManager.newUser', 'Nowy użytkownik'),
|
||
('pl', 'userManager.editUser', 'Edytuj użytkownika'),
|
||
('pl', 'userManager.newPasswordLabel', 'Nowe hasło (opcjonalnie)'),
|
||
('pl', 'userManager.repeatPasswordLabel', 'Powtórz hasło'),
|
||
('pl', 'userManager.passwordMismatch', 'Hasła nie są zgodne.'),
|
||
('pl', 'userManager.confirmDelete', 'Czy na pewno usunąć użytkownika „{email}”? Dostęp zostanie trwale usunięty.'),
|
||
('pl', 'userManager.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'designEditor.title', 'Wygląd'),
|
||
('pl', 'designEditor.logoTitle', 'Logo'),
|
||
('pl', 'designEditor.logoHint', 'Logo jest wyświetlane wyśrodkowane nad jadłospisem.'),
|
||
('pl', 'designEditor.logoNone', 'Nie ustawiono logo.'),
|
||
('pl', 'designEditor.logoUpload', 'Prześlij logo'),
|
||
('pl', 'designEditor.logoRemove', 'Usuń logo'),
|
||
('pl', 'designEditor.logoUpdated', 'Logo zaktualizowane.'),
|
||
('pl', 'designEditor.logoRemoved', 'Logo usunięte.'),
|
||
('pl', 'designEditor.faviconTitle', 'Favicon'),
|
||
('pl', 'designEditor.faviconHint', 'Wyświetlane jako ikona na karcie przeglądarki. Bez własnego favicona używana jest domyślna ikona aplikacji.'),
|
||
('pl', 'designEditor.faviconNone', 'Nie ustawiono własnego favicona.'),
|
||
('pl', 'designEditor.faviconUpload', 'Prześlij favicon'),
|
||
('pl', 'designEditor.faviconRemove', 'Usuń favicon'),
|
||
('pl', 'designEditor.faviconUpdated', 'Favicon zaktualizowany.'),
|
||
('pl', 'designEditor.faviconRemoved', 'Favicon usunięty.'),
|
||
('pl', 'designEditor.invalidImage', 'Wybierz plik graficzny (PNG, JPG, SVG, WebP, ICO).'),
|
||
('pl', 'designEditor.colorsTitle', 'Kolorystyka'),
|
||
('pl', 'designEditor.colorsSave', 'Zapisz kolory'),
|
||
('pl', 'designEditor.colorsReset', 'Przywróć domyślne'),
|
||
('pl', 'designEditor.colorsSaved', 'Kolory zapisane.'),
|
||
('pl', 'designEditor.colorPrimary', 'Kolor główny (nagłówek tabeli)'),
|
||
('pl', 'designEditor.colorPrimaryDark', 'Kolor główny, ciemny (górny pasek wewnętrzny)'),
|
||
('pl', 'designEditor.colorAccent', 'Kolor akcentu'),
|
||
('pl', 'designEditor.colorHeaderText', 'Kolor tekstu w nagłówku tabeli'),
|
||
('pl', 'designEditor.colorRowBg', 'Tło komórek'),
|
||
('pl', 'designEditor.colorRowBgAlt', 'Tło komórek (naprzemienne)'),
|
||
('pl', 'designEditor.colorAddBtn', 'Przycisk „Dodaj”'),
|
||
('pl', 'designEditor.colorDeleteBtn', 'Przycisk „Usuń”'),
|
||
('pl', 'designEditor.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'designEditor.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'designEditor.uploadError', 'Przesyłanie nie powiodło się: {message}'),
|
||
('pl', 'designEditor.confirmRemoveLogo', 'Czy na pewno usunąć logo?'),
|
||
('pl', 'designEditor.confirmRemoveFavicon', 'Czy na pewno usunąć favicon?'),
|
||
('pl', 'labelingNotice.title', 'Wyświetlanie alergenów i dodatków'),
|
||
('pl', 'labelingNotice.hint', 'Domyślnie każdy gość widzi szczegółowy wybór alergenów/dodatków dla każdego dania. Alternatywnie można zamiast tego wyświetlić tylko komunikat – wybór dla poszczególnych dań zostanie wtedy również ukryty w zarządzaniu daniami.'),
|
||
('pl', 'labelingNotice.modeDetailed', 'Pokaż szczegółowy wybór dla każdego dania'),
|
||
('pl', 'labelingNotice.modeNotice', 'Pokaż tylko komunikat'),
|
||
('pl', 'labelingNotice.noticeDeLabel', 'Tekst komunikatu (niemiecki)'),
|
||
('pl', 'labelingNotice.noticeEnLabel', 'Tekst komunikatu (angielski)'),
|
||
('pl', 'labelingNotice.noticePlLabel', 'Tekst komunikatu (polski)'),
|
||
('pl', 'labelingNotice.save', 'Zapisz'),
|
||
('pl', 'labelingNotice.saved', 'Zapisano.'),
|
||
('pl', 'labelingNotice.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'labelingNotice.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'passkeys.title', 'Klucze dostępu'),
|
||
('pl', 'passkeys.hint', 'Dzięki kluczowi dostępu (odcisk palca, rozpoznawanie twarzy lub klucz bezpieczeństwa) możesz w przyszłości logować się bez hasła. Klucze dostępu są przypisane do domeny – po zmianie domeny trzeba je zarejestrować ponownie.'),
|
||
('pl', 'passkeys.register', 'Zarejestruj nowy klucz dostępu'),
|
||
('pl', 'passkeys.empty', 'Nie zarejestrowano jeszcze żadnych kluczy dostępu.'),
|
||
('pl', 'passkeys.createdOn', '– utworzono {date}'),
|
||
('pl', 'passkeys.registered', 'Klucz dostępu został pomyślnie zarejestrowany.'),
|
||
('pl', 'passkeys.registerError', 'Rejestracja nie powiodła się: {message}'),
|
||
('pl', 'passkeys.registerCancelled', 'Rejestracja klucza dostępu została anulowana.'),
|
||
('pl', 'passkeys.loadError', 'Nie udało się wczytać kluczy dostępu: {message}'),
|
||
('pl', 'passkeys.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'passkeys.confirmDelete', 'Czy na pewno usunąć ten klucz dostępu?'),
|
||
('pl', 'account.title', 'Moje konto'),
|
||
('pl', 'account.personalTitle', 'Dane osobowe'),
|
||
('pl', 'account.saveName', 'Zapisz nazwę'),
|
||
('pl', 'account.nameSaved', 'Nazwa zapisana.'),
|
||
('pl', 'account.nameRequired', 'Podaj nazwę.'),
|
||
('pl', 'account.emailTitle', 'Adres e-mail'),
|
||
('pl', 'account.changeEmail', 'Zmień adres e-mail'),
|
||
('pl', 'account.emailChanged', 'Adres e-mail zmieniony.'),
|
||
('pl', 'account.passwordTitle', 'Zmień hasło'),
|
||
('pl', 'account.currentPassword', 'Obecne hasło (do weryfikacji)'),
|
||
('pl', 'account.newPassword', 'Nowe hasło'),
|
||
('pl', 'account.newPasswordRepeat', 'Powtórz nowe hasło'),
|
||
('pl', 'account.changePassword', 'Zmień hasło'),
|
||
('pl', 'account.passwordChanged', 'Hasło zmienione.'),
|
||
('pl', 'account.passwordMismatch', 'Hasła nie są zgodne.'),
|
||
('pl', 'account.passwordTooShort', 'Nowe hasło musi mieć co najmniej 8 znaków.'),
|
||
('pl', 'account.passwordAuthTitle', 'Logowanie hasłem'),
|
||
('pl', 'account.passwordAuthHint', 'Możesz wyłączyć logowanie e-mailem i hasłem dla swojego konta i logować się w przyszłości wyłącznie kluczem dostępu. Wymaga to zarejestrowania co najmniej jednego klucza dostępu.'),
|
||
('pl', 'account.passwordAuthCheckbox', 'Logowanie e-mailem i hasłem dozwolone'),
|
||
('pl', 'account.passwordAuthEnabled', 'Logowanie hasłem włączone.'),
|
||
('pl', 'account.passwordAuthDisabled', 'Logowanie hasłem wyłączone.'),
|
||
('pl', 'account.passwordAuthNeedsPasskey', 'Bez zarejestrowanego klucza dostępu nie możesz wyłączyć logowania hasłem – stracisz dostęp do konta. Najpierw zarejestruj klucz dostępu poniżej.'),
|
||
('pl', 'account.confirmDisablePasswordAuth', 'Czy na pewno wyłączyć logowanie e-mailem i hasłem? Potem będziesz mógł/mogła zalogować się tylko kluczem dostępu.'),
|
||
('pl', 'account.wrongCurrentPassword', 'Obecne hasło jest nieprawidłowe.'),
|
||
('pl', 'account.emailAlreadyUsed', 'Ten adres e-mail jest już używany.'),
|
||
('pl', 'account.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'account.changeError', 'Zmiana nie powiodła się: {message}'),
|
||
('pl', 'suggestions.formTitle', 'Zaproponuj danie'),
|
||
('pl', 'suggestions.formHint', 'Brakuje dania w jadłospisie? Zaproponuj je tutaj – zespół stołówki sprawdzi Twoją propozycję.'),
|
||
('pl', 'suggestions.nameLabel', 'Nazwa dania'),
|
||
('pl', 'suggestions.descriptionLabel', 'Opis / składniki (opcjonalnie)'),
|
||
('pl', 'suggestions.submit', 'Wyślij propozycję'),
|
||
('pl', 'suggestions.submitting', 'Wysyłanie…'),
|
||
('pl', 'suggestions.success', 'Dziękujemy! Twoja propozycja została przesłana.'),
|
||
('pl', 'suggestions.duplicate', 'To danie już istnieje w jadłospisie.'),
|
||
('pl', 'suggestions.genericError', 'Wysyłanie nie powiodło się: {message}'),
|
||
('pl', 'suggestions.nameRequired', 'Podaj nazwę.'),
|
||
('pl', 'suggestions.backToMenu', '← Powrót do jadłospisu'),
|
||
('pl', 'suggestions.reviewTitle', 'Propozycje dań'),
|
||
('pl', 'suggestions.reviewEmpty', 'Brak propozycji.'),
|
||
('pl', 'suggestions.accept', 'Dodaj jako danie'),
|
||
('pl', 'suggestions.reject', 'Odrzuć'),
|
||
('pl', 'suggestions.confirmReject', 'Czy na pewno odrzucić i usunąć propozycję „{name}”?'),
|
||
('pl', 'suggestions.accepted', 'Dodano jako danie.'),
|
||
('pl', 'suggestions.rejected', 'Propozycja odrzucona.'),
|
||
('pl', 'suggestions.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'suggestions.actionError', 'Czynność nie powiodła się: {message}'),
|
||
('pl', 'suggestions.submittedOn', 'przesłano {date}'),
|
||
('pl', 'languageManager.title', 'Języki'),
|
||
('pl', 'languageManager.hint', 'Niemiecki jest stałym językiem bazowym i zawsze kompletny. Kolejne języki są importowane jako plik tłumaczenia i automatycznie uzupełniane niemieckim tekstem tam, gdzie brakuje tłumaczenia.'),
|
||
('pl', 'languageManager.codeColumn', 'Kod'),
|
||
('pl', 'languageManager.labelColumn', 'Nazwa wyświetlana'),
|
||
('pl', 'languageManager.activeColumn', 'Aktywny'),
|
||
('pl', 'languageManager.completenessColumn', 'Kompletność'),
|
||
('pl', 'languageManager.baseBadge', 'Język bazowy'),
|
||
('pl', 'languageManager.complete', 'Kompletny'),
|
||
('pl', 'languageManager.completeness', 'Przetłumaczono {percent}%'),
|
||
('pl', 'languageManager.outdated', 'Nowe terminy od ostatniego importu: {count}'),
|
||
('pl', 'languageManager.downloadTemplate', 'Pobierz szablon'),
|
||
('pl', 'languageManager.uploadTranslation', 'Prześlij tłumaczenie'),
|
||
('pl', 'languageManager.newLanguageTitle', 'Dodaj nowy język'),
|
||
('pl', 'languageManager.newLanguageCodeLabel', 'Kod języka (np. fr)'),
|
||
('pl', 'languageManager.newLanguageLabelLabel', 'Nazwa wyświetlana (np. Français)'),
|
||
('pl', 'languageManager.create', 'Utwórz'),
|
||
('pl', 'languageManager.delete', 'Usuń'),
|
||
('pl', 'languageManager.confirmDelete', 'Czy na pewno usunąć „{label}”? Wszystkie tłumaczenia tego języka zostaną utracone.'),
|
||
('pl', 'languageManager.cannotDeleteBase', 'Nie można usunąć języka bazowego.'),
|
||
('pl', 'languageManager.cannotDeactivateLast', 'Co najmniej jeden język musi pozostać aktywny.'),
|
||
('pl', 'languageManager.loadError', 'Wczytywanie nie powiodło się: {message}'),
|
||
('pl', 'languageManager.saveError', 'Zapisywanie nie powiodło się: {message}'),
|
||
('pl', 'languageManager.deleteError', 'Usuwanie nie powiodło się: {message}'),
|
||
('pl', 'languageManager.importSuccess', 'Zaimportowano terminów: {count}.'),
|
||
('pl', 'languageManager.importError', 'Import nie powiódł się: {message}'),
|
||
('pl', 'languageManager.importInvalidFile', 'Nie można odczytać pliku (nieprawidłowy JSON).'),
|
||
('pl', 'languageManager.importMissingPlaceholder', '„{key}”: brak symbolu zastępczego {placeholder} w tłumaczeniu – wiersz pominięto.'),
|
||
('pl', 'languageManager.duplicateCode', 'Kod języka „{code}” już istnieje.'),
|
||
('pl', 'languageManager.invalidCode', 'Podaj kod języka złożony z 2–5 małych liter.');
|