diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3cce93..b01151e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.16.3] - 2026-04-13
+
+### Added
+- i18n: five new UI languages - French (fr), Turkish (tr), Russian (ru), Greek (el), and Chinese Simplified (zh) with full translations of all keys
+- Budget: TRY (Turkish Lira) and RUB (Russian Ruble) added to the list of selectable currencies in Settings
+- i18n: Italian locale now includes the complete `rrule` section (was missing previously)
+
## [0.16.2] - 2026-04-13
### Added
diff --git a/README.md b/README.md
index 6f2f716..cc654fa 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@
**Calendar Sync:** Two-way sync with Google Calendar (OAuth) and Apple iCloud (CalDAV)
-**Budget Tracking:** Income and expenses, recurring entries, configurable currency (13 currencies), monthly trends, CSV export
+**Budget Tracking:** Income and expenses, recurring entries, configurable currency (15 currencies), monthly trends, CSV export
**Notes & Contacts:** Colored sticky notes with Markdown, contact directory with vCard import/export
@@ -61,7 +61,7 @@
**PWA Native Feel:** Installable on any device, works offline, dark mode, responsive from phone to desktop
-**Multilingual:** German, English, Italian, and Swedish UI with automatic locale detection
+**Multilingual:** German, English, Spanish, French, Italian, Swedish, Greek, Russian, Turkish, and Chinese UI with automatic locale detection
## Quick Start
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 536ccff..f90a7b2 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -234,7 +234,7 @@ User management and app configuration. Logged-in users only.
- **User management (admin):** create new users, edit/delete existing users, assign roles (admin/member)
- **Calendar integration:** connect/disconnect Google Calendar OAuth, store Apple Calendar (CalDAV) credentials, configure sync interval
- **Weather:** configure OpenWeatherMap location
-- **Language:** System (follows `navigator.language`), German, English, Italian, Swedish - via `oikos-locale-picker` web component; switch without page reload
+- **Language:** System (follows `navigator.language`), German, English, Spanish, French, Italian, Swedish, Greek, Russian, Turkish, Chinese - via `oikos-locale-picker` web component; switch without page reload
- **App info:** version, license
### Budget (`/budget`)
@@ -344,8 +344,14 @@ All UI strings are managed via `public/i18n.js`. No hardcoded text in JS files o
|------|----------|--------|
| `de` | German | Reference locale (all keys defined here) |
| `en` | English | Full translation |
+| `es` | Spanish | Full translation |
+| `fr` | French | Full translation (added v0.16.3) |
| `it` | Italian | Full translation (added v0.5.8) |
| `sv` | Swedish | Full translation (added v0.11.3) |
+| `el` | Greek | Full translation (added v0.16.3) |
+| `ru` | Russian | Full translation (added v0.16.3) |
+| `tr` | Turkish | Full translation (added v0.16.3) |
+| `zh` | Chinese (Simplified) | Full translation (added v0.16.3) |
### Adding a New Language
diff --git a/public/components/oikos-locale-picker.js b/public/components/oikos-locale-picker.js
index 40babe3..e1a6de0 100644
--- a/public/components/oikos-locale-picker.js
+++ b/public/components/oikos-locale-picker.js
@@ -11,8 +11,13 @@ const LOCALE_LABELS = {
de: 'Deutsch',
en: 'English',
es: 'Español',
+ fr: 'Français',
it: 'Italiano',
sv: 'Svenska',
+ el: 'Ελληνικά',
+ ru: 'Русский',
+ tr: 'Türkçe',
+ zh: '中文',
};
class OikosLocalePicker extends HTMLElement {
diff --git a/public/i18n.js b/public/i18n.js
index 7bc78fc..218cc67 100644
--- a/public/i18n.js
+++ b/public/i18n.js
@@ -5,7 +5,7 @@
* Dependencies: none (vanilla JS, Fetch API, Intl API)
*/
-const SUPPORTED_LOCALES = ['de', 'en', 'es', 'it', 'sv'];
+const SUPPORTED_LOCALES = ['de', 'en', 'es', 'fr', 'it', 'sv', 'el', 'ru', 'tr', 'zh'];
const DEFAULT_LOCALE = 'de';
const STORAGE_KEY = 'oikos-locale';
diff --git a/public/locales/el.json b/public/locales/el.json
new file mode 100644
index 0000000..9bc3acd
--- /dev/null
+++ b/public/locales/el.json
@@ -0,0 +1,591 @@
+{
+ "common": {
+ "save": "Αποθήκευση",
+ "cancel": "Ακύρωση",
+ "delete": "Διαγραφή",
+ "edit": "Επεξεργασία",
+ "close": "Κλείσιμο",
+ "create": "Δημιουργία",
+ "add": "Προσθήκη",
+ "back": "Πίσω",
+ "next": "Επόμενο",
+ "loading": "Φόρτωση…",
+ "saving": "Αποθήκευση…",
+ "required": "Αυτό το πεδίο είναι υποχρεωτικό.",
+ "error": "Σφάλμα",
+ "allFieldsRequired": "Παρακαλώ συμπληρώστε όλα τα πεδία.",
+ "today": "Σήμερα",
+ "tomorrow": "Αύριο",
+ "skipToContent": "Μετάβαση στο περιεχόμενο",
+ "reload": "Ανανέωση",
+ "errorOccurred": "Κάτι πήγε στραβά.",
+ "unexpectedError": "Παρουσιάστηκε απρόβλεπτο σφάλμα.",
+ "errorGeneric": "Παρουσιάστηκε σφάλμα.",
+ "updateAvailable": "Διαθέσιμη ενημέρωση - ανανεώστε τη σελίδα για την τελευταία έκδοση.",
+ "titleRequired": "Ο τίτλος είναι υποχρεωτικός",
+ "nameRequired": "Το όνομα είναι υποχρεωτικό",
+ "contentRequired": "Το περιεχόμενο είναι υποχρεωτικό",
+ "all": "Όλα",
+ "unknownError": "Άγνωστο σφάλμα",
+ "confirm": "Επιβεβαίωση",
+ "undo": "Αναίρεση"
+ },
+ "nav": {
+ "dashboard": "Επισκόπηση",
+ "tasks": "Εργασίες",
+ "calendar": "Ημερολόγιο",
+ "meals": "Γεύματα",
+ "shopping": "Αγορές",
+ "notes": "Σημειώσεις",
+ "contacts": "Επαφές",
+ "budget": "Προϋπολογισμός",
+ "settings": "Ρυθμίσεις",
+ "main": "Κύρια πλοήγηση",
+ "navigation": "Πλοήγηση",
+ "quickActions": "Γρήγορες ενέργειες"
+ },
+ "dashboard": {
+ "title": "Επισκόπηση",
+ "greetingMorning": "Καλημέρα, {{name}}",
+ "greetingDay": "Καλησπέρα, {{name}}",
+ "greetingEvening": "Καλό βράδυ, {{name}}",
+ "allDone": "Όλα έτοιμα",
+ "noEvents": "Δεν υπάρχουν εκδηλώσεις",
+ "noPinnedNotes": "Δεν υπάρχουν καρφιτσωμένες σημειώσεις",
+ "todayMeals": "Γεύματα σήμερα",
+ "allLink": "Όλα",
+ "weekLink": "Εβδομάδα",
+ "urgentTasksChip": "{{count}} επείγουσα εργασία",
+ "urgentTasksChipPlural": "{{count}} επείγουσες εργασίες",
+ "eventsChip": "{{count}} εκδήλωση σήμερα",
+ "eventsChipPlural": "{{count}} εκδηλώσεις σήμερα",
+ "todayMealChip": "Σήμερα: {{title}}",
+ "loadError": "Η επισκόπηση δεν φορτώθηκε πλήρως.",
+ "weatherRefresh": "Ανανέωση καιρού",
+ "weatherRefreshTitle": "Ανανέωση",
+ "weatherUpdated": "Καιρός ενημερώθηκε",
+ "weatherFeelsLike": "Αίσθηση {{temp}}° · {{humidity}}% · Άνεμος {{wind}} χλμ/ώ",
+ "fabTaskLabel": "Προσθήκη εργασίας",
+ "fabCalendarLabel": "Προσθήκη εκδήλωσης",
+ "fabShoppingLabel": "Προσθήκη αγοράς",
+ "fabNoteLabel": "Προσθήκη σημείωσης",
+ "fabTask": "Εργασία",
+ "fabCalendar": "Εκδήλωση",
+ "fabShopping": "Αγορά",
+ "fabNote": "Σημείωση",
+ "overdue": "Εκπρόθεσμο",
+ "dueSoon": "Λήγει σήμερα",
+ "dueTomorrow": "Λήγει αύριο",
+ "allDay": "Όλη μέρα",
+ "shoppingMore": "+{{count}} ακόμα"
+ },
+ "tasks": {
+ "title": "Εργασίες",
+ "newTask": "Νέα εργασία",
+ "editTask": "Επεξεργασία εργασίας",
+ "emptyTitle": "Δεν υπάρχουν εργασίες - όλα έτοιμα;",
+ "emptyDescription": "Δημιουργήστε νέες εργασίες με το κουμπί +.",
+ "titleLabel": "Τίτλος *",
+ "titlePlaceholder": "Τι πρέπει να γίνει;",
+ "descriptionLabel": "Σημείωση",
+ "descriptionPlaceholder": "Προαιρετικές λεπτομέρειες…",
+ "priorityLabel": "Προτεραιότητα",
+ "categoryLabel": "Κατηγορία",
+ "dueDateLabel": "Ημερομηνία λήξης",
+ "dueTimeLabel": "Ώρα",
+ "assignedLabel": "Ανατεθειμένο σε",
+ "assignedNobody": "- Κανέναν -",
+ "statusLabel": "Κατάσταση",
+ "priorityUrgent": "Επείγον",
+ "priorityHigh": "Υψηλή",
+ "priorityMedium": "Μέτρια",
+ "priorityLow": "Χαμηλή",
+ "priorityNone": "Καμία",
+ "statusOpen": "Ανοιχτό",
+ "statusInProgress": "Σε εξέλιξη",
+ "statusDone": "Ολοκληρώθηκε",
+ "categoryHousehold": "Οικιακά",
+ "categorySchool": "Σχολείο",
+ "categoryShopping": "Αγορές",
+ "categoryRepair": "Επισκευή",
+ "categoryHealth": "Υγεία",
+ "categoryFinance": "Οικονομικά",
+ "categoryLeisure": "Ελεύθερος χρόνος",
+ "categoryMisc": "Διάφορα",
+ "overdue": "Εκπρόθεσμο",
+ "overdueDay": "{{count}}μ εκπρόθεσμο",
+ "dueToday": "Λήγει σήμερα",
+ "dueTomorrow": "Λήγει αύριο",
+ "groupOverdue": "Εκπρόθεσμο",
+ "groupToday": "Σήμερα",
+ "groupThisWeek": "Αυτή την εβδομάδα",
+ "groupNextWeek": "Επόμενη εβδομάδα",
+ "groupLater": "Αργότερα",
+ "groupNoDate": "Χωρίς ημερομηνία",
+ "markDone": "Επισήμανση {{title}} ως ολοκληρωμένο",
+ "editButton": "Επεξεργασία εργασίας",
+ "swipeOpen": "Επαναφορά",
+ "swipeDone": "Ολοκληρώθηκε",
+ "swipeEdit": "Επεξεργασία",
+ "subtaskAdd": "+ Προσθήκη υποεργασίας",
+ "subtaskToggle": "Εμφάνιση υποεργασιών",
+ "subtaskMarkDone": "Επισήμανση {{title}} ως ολοκληρωμένο",
+ "deleteConfirm": "Διαγραφή εργασίας και όλων των υποεργασιών;",
+ "savedToast": "Η εργασία αποθηκεύτηκε.",
+ "createdToast": "Η εργασία δημιουργήθηκε.",
+ "deletedToast": "Η εργασία διαγράφηκε.",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση της εργασίας.",
+ "subtaskPrompt": "Υποεργασία:",
+ "kanbanOpen": "Ανοιχτό",
+ "kanbanInProgress": "Σε εξέλιξη",
+ "kanbanDone": "Ολοκληρώθηκε",
+ "kanbanMoveToInProgress": "Μετακίνηση σε εξέλιξη",
+ "kanbanMoveToDone": "Επισήμανση ως ολοκληρωμένο",
+ "kanbanMoveToOpen": "Επαναφορά",
+ "recurring": "Επαναλαμβανόμενο",
+ "listView": "Προβολή λίστας",
+ "kanbanView": "Προβολή Kanban"
+ },
+ "shopping": {
+ "title": "Αγορές",
+ "noLists": "Δεν υπάρχουν λίστες",
+ "noListsDescription": "Δημιουργήστε μια λίστα με το κουμπί +.",
+ "emptyList": "Η λίστα είναι κενή",
+ "emptyListDescription": "Προσθέστε είδη μέσω του πεδίου εισαγωγής πάνω.",
+ "newListPrompt": "Όνομα νέας λίστας:",
+ "newListButton": "Δημιουργία νέας λίστας",
+ "renameListPrompt": "Νέο όνομα λίστας:",
+ "deleteListConfirm": "Διαγραφή λίστας «{{name}}» και όλων των ειδών;",
+ "deletedListToast": "Η λίστα διαγράφηκε.",
+ "itemDeletedToast": "Το «{{name}}» αφαιρέθηκε.",
+ "itemsRemovedToast": "{{count}} είδη αφαιρέθηκαν.",
+ "clearChecked": "Αφαίρεση επιλεγμένων ({{count}})",
+ "itemNamePlaceholder": "Προσθήκη είδους…",
+ "itemQtyPlaceholder": "Ποσότητα",
+ "itemNameLabel": "Όνομα είδους",
+ "itemQtyLabel": "Ποσότητα",
+ "categoryLabel": "Κατηγορία",
+ "addItemLabel": "Προσθήκη είδους",
+ "renameListLabel": "Μετονομασία λίστας",
+ "deleteListLabel": "Διαγραφή λίστας",
+ "swipeBack": "Αναίρεση",
+ "swipeCheck": "Επισήμανση",
+ "swipeDelete": "Διαγραφή",
+ "markDoneLabel": "Επισήμανση {{name}}",
+ "markUndoneLabel": "Αφαίρεση επισήμανσης {{name}}",
+ "deleteItemLabel": "Διαγραφή {{name}}",
+ "listsLoadError": "Δεν ήταν δυνατή η φόρτωση λιστών.",
+ "itemsLoadError": "Δεν ήταν δυνατή η φόρτωση ειδών.",
+ "catFruitVeg": "Φρούτα και λαχανικά",
+ "catBakery": "Αρτοποιία",
+ "catDairy": "Γαλακτοκομικά",
+ "catMeatFish": "Κρέας και ψάρι",
+ "catFrozen": "Κατεψυγμένα",
+ "catDrinks": "Ποτά",
+ "catHousehold": "Οικιακά",
+ "catDrugstore": "Φαρμακείο",
+ "catMisc": "Διάφορα"
+ },
+ "meals": {
+ "title": "Πρόγραμμα γευμάτων",
+ "noMealPlanned": "Δεν έχει προγραμματιστεί γεύμα",
+ "addMeal": "Προσθήκη {{type}}",
+ "editMeal": "Επεξεργασία γεύματος",
+ "addMealTitle": "Προσθήκη γεύματος",
+ "deleteMeal": "Διαγραφή γεύματος",
+ "transferToShoppingList": "Προσθήκη υλικών στη λίστα αγορών",
+ "today": "Σήμερα",
+ "prevWeek": "Προηγούμενη εβδομάδα",
+ "nextWeek": "Επόμενη εβδομάδα",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση του προγράμματος γευμάτων.",
+ "typeBreakfast": "Πρωινό",
+ "typeLunch": "Μεσημεριανό",
+ "typeDinner": "Βραδινό",
+ "typeSnack": "Σνακ",
+ "dayMo": "Δευ",
+ "dayDi": "Τρί",
+ "dayMi": "Τετ",
+ "dayDo": "Πέμ",
+ "dayFr": "Παρ",
+ "daySa": "Σάβ",
+ "daySo": "Κυρ",
+ "dateLabel": "Ημερομηνία",
+ "mealTypeLabel": "Γεύμα",
+ "titleLabel": "Τίτλος *",
+ "titlePlaceholder": "π.χ. Μακαρονάδα μπολονέζ",
+ "notesLabel": "Σημειώσεις",
+ "notesPlaceholder": "Προαιρετικό…",
+ "ingredientsLabel": "Υλικά",
+ "addIngredient": "Προσθήκη υλικού",
+ "ingredientNamePlaceholder": "Υλικό",
+ "ingredientQtyPlaceholder": "Ποσότητα",
+ "removeIngredient": "Αφαίρεση υλικού",
+ "transferLabel": "Μεταφορά υλικών στη λίστα αγορών",
+ "transferNow": "Μεταφορά τώρα",
+ "noShoppingLists": "Δεν υπάρχουν λίστες αγορών",
+ "transferSuccess": "{{count}} υλικό μεταφέρθηκε",
+ "transferSuccessPlural": "{{count}} υλικά μεταφέρθηκαν",
+ "transferAlreadyDone": "Όλα τα υλικά έχουν ήδη μεταφερθεί",
+ "ingredientCount": "{{count}} υλικό",
+ "ingredientCountPlural": "{{count}} υλικά",
+ "titleRequired": "Ο τίτλος είναι υποχρεωτικός",
+ "loadingIndicator": "Φόρτωση…",
+ "recipeUrlLabel": "Σύνδεσμος συνταγής (προαιρετικό)",
+ "recipeUrlPlaceholder": "https://…",
+ "openRecipe": "Άνοιγμα συνταγής"
+ },
+ "calendar": {
+ "title": "Ημερολόγιο",
+ "newEvent": "Νέα εκδήλωση",
+ "editEvent": "Επεξεργασία εκδήλωσης",
+ "addEvent": "Προσθήκη εκδήλωσης",
+ "deleteEvent": "Διαγραφή εκδήλωσης",
+ "noEvents": "Δεν υπάρχουν εκδηλώσεις στην επιλεγμένη περίοδο.",
+ "today": "Σήμερα",
+ "back": "Πίσω",
+ "forward": "Μπροστά",
+ "viewMonth": "Μήνας",
+ "viewWeek": "Εβδομάδα",
+ "viewDay": "Ημέρα",
+ "viewAgenda": "Ατζέντα",
+ "allDay": "Όλη μέρα",
+ "allDayShort": "όλη μέρα",
+ "moreEvents": "+{{count}} ακόμα",
+ "weekNumberLabel": "Εβδ{{week}} · {{month}} {{year}}",
+ "agendaFrom": "Από {{date}}",
+ "titleLabel": "Τίτλος *",
+ "titlePlaceholder": "π.χ. Οδοντίατρος",
+ "allDayToggle": "Όλη μέρα",
+ "startDateLabel": "Ημερομηνία έναρξης",
+ "startTimeLabel": "Ώρα έναρξης",
+ "endDateLabel": "Ημερομηνία λήξης",
+ "endTimeLabel": "Ώρα λήξης",
+ "fromLabel": "Από",
+ "toLabel": "Έως",
+ "locationLabel": "Τοποθεσία",
+ "locationPlaceholder": "Προαιρετικό",
+ "assignedLabel": "Ανατεθειμένο σε",
+ "assignedNobody": "- Κανέναν -",
+ "colorLabel": "Χρώμα {{color}}",
+ "descriptionLabel": "Περιγραφή",
+ "descriptionPlaceholder": "Προαιρετικό…",
+ "popupEdit": "Επεξεργασία",
+ "deleteConfirm": "Διαγραφή «{{title}}»;",
+ "createdToast": "Η εκδήλωση δημιουργήθηκε",
+ "savedToast": "Η εκδήλωση αποθηκεύτηκε",
+ "deletedToast": "Η εκδήλωση διαγράφηκε",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση εκδηλώσεων.",
+ "saveError": "Σφάλμα αποθήκευσης",
+ "deleteError": "Σφάλμα διαγραφής",
+ "titleRequired": "Ο τίτλος είναι υποχρεωτικός",
+ "monthJanuary": "Ιανουάριος",
+ "monthFebruary": "Φεβρουάριος",
+ "monthMarch": "Μάρτιος",
+ "monthApril": "Απρίλιος",
+ "monthMay": "Μάιος",
+ "monthJune": "Ιούνιος",
+ "monthJuly": "Ιούλιος",
+ "monthAugust": "Αύγουστος",
+ "monthSeptember": "Σεπτέμβριος",
+ "monthOctober": "Οκτώβριος",
+ "monthNovember": "Νοέμβριος",
+ "monthDecember": "Δεκέμβριος",
+ "dayShortSunday": "Κυρ",
+ "dayShortMonday": "Δευ",
+ "dayShortTuesday": "Τρί",
+ "dayShortWednesday": "Τετ",
+ "dayShortThursday": "Πέμ",
+ "dayShortFriday": "Παρ",
+ "dayShortSaturday": "Σάβ",
+ "dayLongSunday": "Κυριακή",
+ "dayLongMonday": "Δευτέρα",
+ "dayLongTuesday": "Τρίτη",
+ "dayLongWednesday": "Τετάρτη",
+ "dayLongThursday": "Πέμπτη",
+ "dayLongFriday": "Παρασκευή",
+ "dayLongSaturday": "Σάββατο",
+ "timeSuffix": ""
+ },
+ "notes": {
+ "title": "Σημειώσεις",
+ "newNote": "Νέα σημείωση",
+ "editNote": "Επεξεργασία σημείωσης",
+ "addNoteLabel": "Νέα σημείωση",
+ "searchPlaceholder": "Αναζήτηση σημειώσεων…",
+ "emptyTitle": "Δεν υπάρχουν σημειώσεις",
+ "emptyDescription": "Δημιουργήστε νέα σημείωση με το κουμπί +.",
+ "noResultsTitle": "Δεν βρέθηκαν αποτελέσματα",
+ "noResultsDescription": "Καμία σημείωση δεν περιέχει «{{query}}».",
+ "titleLabel": "Τίτλος (προαιρετικό)",
+ "titlePlaceholder": "Χωρίς τίτλο",
+ "contentLabel": "Περιεχόμενο",
+ "contentMarkdownHint": "(Υποστηρίζεται μορφοποίηση Markdown)",
+ "contentPlaceholder": "Εισαγωγή σημείωσης…",
+ "colorLabel": "Χρώμα",
+ "pinnedLabel": "Καρφίτσωμα (εμφανίζεται στην επισκόπηση)",
+ "pinAction": "Καρφίτσωμα",
+ "unpinAction": "Αποκαρφίτσωμα",
+ "deleteLabel": "Διαγραφή σημείωσης",
+ "deleteConfirm": "Διαγραφή αυτής της σημείωσης;",
+ "createdToast": "Η σημείωση δημιουργήθηκε",
+ "savedToast": "Η σημείωση αποθηκεύτηκε",
+ "deletedToast": "Η σημείωση διαγράφηκε",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση σημειώσεων.",
+ "formatBold": "Έντονο (Ctrl+B)",
+ "formatItalic": "Πλάγιο (Ctrl+I)",
+ "formatUnderline": "Υπογράμμιση (Ctrl+U)",
+ "formatStrikethrough": "Διακριτική γραμμή",
+ "formatHeading": "Επικεφαλίδα",
+ "formatList": "Λίστα κουκκίδων",
+ "formatOrderedList": "Αριθμημένη λίστα",
+ "formatChecklist": "Λίστα ελέγχου",
+ "formatLink": "Σύνδεσμος",
+ "formatCode": "Κώδικας",
+ "formatQuote": "Παράθεση",
+ "formatDivider": "Διαχωριστής"
+ },
+ "contacts": {
+ "title": "Επαφές",
+ "newContact": "Νέα επαφή",
+ "editContact": "Επεξεργασία επαφής",
+ "addButton": "Νέο",
+ "newContactLabel": "Νέα επαφή",
+ "searchPlaceholder": "Αναζήτηση με όνομα, τηλέφωνο ή email…",
+ "importButton": "Εισαγωγή",
+ "importLabel": "Εισαγωγή επαφής από vCard",
+ "importTooltip": "Εισαγωγή vCard",
+ "emptyTitle": "Δεν υπάρχουν επαφές",
+ "emptyDescription": "Προσθέστε νέες επαφές με το κουμπί +.",
+ "filterAll": "Όλες",
+ "nameLabel": "Όνομα *",
+ "namePlaceholder": "Πλήρες όνομα",
+ "categoryLabel": "Κατηγορία",
+ "phoneLabel": "Τηλέφωνο",
+ "phonePlaceholder": "+30 …",
+ "emailLabel": "Email",
+ "emailPlaceholder": "ονομα@παραδειγμα.gr",
+ "addressLabel": "Διεύθυνση",
+ "addressPlaceholder": "Οδός, ΤΚ Πόλη",
+ "notesLabel": "Σημειώσεις",
+ "notesPlaceholder": "Προαιρετικό…",
+ "callLabel": "Κλήση",
+ "emailActionLabel": "Email",
+ "mapsLabel": "Άνοιγμα στους Χάρτες",
+ "exportLabel": "Εξαγωγή ως vCard",
+ "exportTooltip": "Εξαγωγή vCard",
+ "deleteLabel": "Διαγραφή επαφής",
+ "deleteConfirm": "Διαγραφή αυτής της επαφής;",
+ "deletePersonConfirm": "Διαγραφή «{{name}}»;",
+ "savedToast": "Η επαφή αποθηκεύτηκε",
+ "updatedToast": "Η επαφή ενημερώθηκε",
+ "deletedToast": "Η επαφή διαγράφηκε",
+ "importedToast": "{{name}} εισήχθη.",
+ "importError": "Αποτυχία εισαγωγής: {{error}}",
+ "vcardNoName": "Η vCard δεν περιέχει όνομα.",
+ "catDoctor": "Γιατρός",
+ "catSchool": "Σχολείο/Παιδικός σταθμός",
+ "catAuthority": "Δημόσια υπηρεσία",
+ "catInsurance": "Ασφάλεια",
+ "catCraftsman": "Τεχνίτης",
+ "catEmergency": "Έκτακτη ανάγκη",
+ "catMisc": "Διάφορα",
+ "categoryDoctor": "Γιατρός",
+ "categorySchool": "Σχολείο/Παιδικός σταθμός",
+ "categoryAuthority": "Δημόσια υπηρεσία",
+ "categoryInsurance": "Ασφάλεια",
+ "categoryCraftsman": "Τεχνίτης",
+ "categoryEmergency": "Έκτακτη ανάγκη",
+ "categoryOther": "Άλλο"
+ },
+ "budget": {
+ "title": "Προϋπολογισμός",
+ "newEntry": "Νέα εγγραφή",
+ "editEntry": "Επεξεργασία εγγραφής",
+ "addEntryLabel": "Προσθήκη εγγραφής",
+ "newEntryFabLabel": "Νέα εγγραφή",
+ "currentMonth": "Τρέχων",
+ "prevMonth": "Προηγούμενος μήνας",
+ "nextMonth": "Επόμενος μήνας",
+ "income": "Έσοδα",
+ "expenses": "Έξοδα",
+ "balance": "Υπόλοιπο",
+ "byCategory": "Ανά κατηγορία",
+ "transactions": "Συναλλαγές",
+ "emptyTitle": "Δεν υπάρχουν εγγραφές αυτόν τον μήνα",
+ "emptyDescription": "Προσθέστε εγγραφές προϋπολογισμού με το κουμπί +.",
+ "csvExport": "CSV",
+ "typeExpense": "Έξοδο",
+ "typeIncome": "Έσοδο",
+ "titleLabel": "Τίτλος *",
+ "titlePlaceholder": "π.χ. Σούπερ μάρκετ",
+ "amountLabel": "Ποσό *",
+ "amountPlaceholder": "0,00",
+ "categoryLabel": "Κατηγορία",
+ "dateLabel": "Ημερομηνία *",
+ "recurringLabel": "Επαναλαμβανόμενο",
+ "deleteLabel": "Διαγραφή εγγραφής",
+ "deleteConfirm": "Διαγραφή αυτής της εγγραφής;",
+ "deletePersonConfirm": "Διαγραφή «{{title}}»;",
+ "addedToast": "Η εγγραφή προστέθηκε",
+ "savedToast": "Η εγγραφή αποθηκεύτηκε",
+ "deletedToast": "Η εγγραφή διαγράφηκε",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση του προϋπολογισμού.",
+ "trendNeutral": "- ίδιο με {{month}}",
+ "validAmountRequired": "Παρακαλώ εισαγάγετε έγκυρο ποσό",
+ "dateRequired": "Η ημερομηνία είναι υποχρεωτική",
+ "catFood": "Τρόφιμα",
+ "catRent": "Ενοίκιο",
+ "catInsurance": "Ασφάλεια",
+ "catMobility": "Μεταφορές",
+ "catLeisure": "Ελεύθερος χρόνος",
+ "catClothing": "Ρουχισμός",
+ "catHealth": "Υγεία",
+ "catEducation": "Εκπαίδευση",
+ "catMisc": "Διάφορα",
+ "loadingIndicator": "Φόρτωση…"
+ },
+ "settings": {
+ "title": "Ρυθμίσεις",
+ "tabGeneral": "Γενικά",
+ "tabMeals": "Γεύματα",
+ "tabBudget": "Προϋπολογισμός",
+ "tabShopping": "Αγορές",
+ "tabCalendar": "Ημερολόγιο",
+ "tabAccount": "Λογαριασμός",
+ "tabsAriaLabel": "Τμήματα ρυθμίσεων",
+ "sectionDesign": "Εμφάνιση",
+ "sectionShopping": "Αγορές",
+ "shoppingCategoriesLabel": "Κατηγορίες αγορών",
+ "shoppingCategoriesHint": "Προσθέστε, μετονομάστε, διαγράψτε ή ταξινομήστε κατηγορίες.",
+ "shoppingCategoryPlaceholder": "Νέα κατηγορία…",
+ "shoppingCategoryRenameHint": "Κάντε κλικ για μετονομασία",
+ "shoppingCategoryRenamePrompt": "Νέο όνομα κατηγορίας:",
+ "shoppingCategoryMoveUp": "Μετακίνηση κατηγορίας πάνω",
+ "shoppingCategoryMoveDown": "Μετακίνηση κατηγορίας κάτω",
+ "shoppingCategoryDelete": "Διαγραφή κατηγορίας",
+ "shoppingCategoryDeleteConfirm": "Διαγραφή κατηγορίας «{{name}}»; Τα υπάρχοντα είδη θα μεταφερθούν στην επόμενη κατηγορία.",
+ "shoppingCategoryAdded": "Η κατηγορία προστέθηκε.",
+ "shoppingCategoryRenamed": "Η κατηγορία μετονομάστηκε.",
+ "shoppingCategoryDeleted": "Η κατηγορία διαγράφηκε.",
+ "sectionAccount": "Ο λογαριασμός μου",
+ "sectionCalendarSync": "Συγχρονισμός ημερολογίου",
+ "sectionFamily": "Μέλη οικογένειας",
+ "cardAppearance": "Εμφάνιση",
+ "themeSystem": "Σύστημα",
+ "themeSysLabel": "Χρήση ρύθμισης συστήματος",
+ "themeLight": "Ανοιχτό",
+ "themeLightLabel": "Ανοιχτό θέμα",
+ "themeDark": "Σκούρο",
+ "themeDarkLabel": "Σκούρο θέμα",
+ "changePassword": "Αλλαγή κωδικού",
+ "currentPasswordLabel": "Τρέχων κωδικός",
+ "newPasswordLabel": "Νέος κωδικός",
+ "confirmPasswordLabel": "Επιβεβαίωση νέου κωδικού",
+ "savePassword": "Αποθήκευση κωδικού",
+ "passwordMismatch": "Οι κωδικοί δεν ταιριάζουν.",
+ "passwordSavedToast": "Ο κωδικός άλλαξε επιτυχώς.",
+ "googleCalendar": "Google Calendar",
+ "appleCalendar": "Apple Calendar (iCloud)",
+ "syncNow": "Συγχρονισμός τώρα",
+ "disconnect": "Αποσύνδεση",
+ "connectGoogle": "Σύνδεση με Google",
+ "connected": "Συνδεδεμένο",
+ "connectedLastSync": "Συνδεδεμένο · Τελευταίο: {{date}}",
+ "notConnected": "Μη συνδεδεμένο",
+ "notConfigured": "Μη ρυθμισμένο (λείπουν μεταβλητές .env)",
+ "configured": "Ρυθμισμένο (μέσω .env)",
+ "configuredLastSync": "Ρυθμισμένο (μέσω .env) · Τελευταίο: {{date}}",
+ "syncSuccess": "{{provider}} συγχρονίστηκε.",
+ "disconnectedToast": "{{provider}} αποσυνδέθηκε.",
+ "googleOnlyAdmin": "Μόνο ο διαχειριστής μπορεί να συνδέσει το Google Calendar.",
+ "appleOnlyAdmin": "Μόνο ο διαχειριστής μπορεί να συνδέσει το Apple Calendar.",
+ "caldavUrlLabel": "URL διακομιστή CalDAV",
+ "caldavUrlPlaceholder": "https://caldav.icloud.com",
+ "appleIdLabel": "Apple ID (email)",
+ "applePasswordLabel": "Κωδικός για εφαρμογή",
+ "applePasswordHint": "Δημιουργήστε κωδικό στο appleid.apple.com → Ασφάλεια.",
+ "appleConnectBtn": "Σύνδεση και δοκιμή",
+ "appleConnecting": "Σύνδεση…",
+ "appleConnectedToast": "Το Apple Calendar συνδέθηκε.",
+ "syncSuccessGoogle": "Ο συγχρονισμός ημερολογίου με Google συνδέθηκε επιτυχώς.",
+ "syncSuccessApple": "Ο συγχρονισμός ημερολογίου με Apple συνδέθηκε επιτυχώς.",
+ "syncErrorGoogle": "Η σύνδεση με Google απέτυχε. Δοκιμάστε ξανά.",
+ "syncErrorApple": "Η σύνδεση με Apple απέτυχε. Δοκιμάστε ξανά.",
+ "addMember": "+ Προσθήκη μέλους",
+ "newMemberTitle": "Νέο μέλος οικογένειας",
+ "usernameLabel": "Όνομα χρήστη",
+ "displayNameLabel": "Εμφανιζόμενο όνομα",
+ "memberPasswordLabel": "Κωδικός",
+ "colorLabel": "Χρώμα",
+ "roleLabel": "Ρόλος",
+ "roleMember": "Μέλος",
+ "roleAdmin": "Διαχειριστής",
+ "createMember": "Δημιουργία",
+ "cancelAddMember": "Ακύρωση",
+ "memberAddedToast": "{{name}} προστέθηκε.",
+ "deleteMemberConfirm": "Διαγραφή {{name}};",
+ "memberDeletedToast": "{{name}} διαγράφηκε.",
+ "deleteMemberLabel": "Διαγραφή",
+ "logout": "Αποσύνδεση",
+ "synchronizing": "Συγχρονισμός…",
+ "googleDisconnectConfirm": "Αποσύνδεση Google Calendar;",
+ "appleDisconnectConfirm": "Αποσύνδεση Apple Calendar;",
+ "localeSystem": "Σύστημα",
+ "localeLabel": "Γλώσσα",
+ "languageTitle": "Γλώσσα",
+ "sectionMeals": "Πρόγραμμα γευμάτων",
+ "mealTypesLabel": "Ορατά γεύματα",
+ "mealTypesHint": "Στο πρόγραμμα γευμάτων εμφανίζονται μόνο οι επιλεγμένοι τύποι.",
+ "mealTypesSaved": "Οι ρυθμίσεις προγράμματος γευμάτων αποθηκεύτηκαν.",
+ "mealTypesMinOne": "Πρέπει να είναι ενεργός τουλάχιστον ένας τύπος γεύματος.",
+ "sectionBudget": "Προϋπολογισμός",
+ "currencyLabel": "Νόμισμα",
+ "currencyHint": "Ορίζει το νόμισμα για ολόκληρη την ενότητα προϋπολογισμού.",
+ "currencySaved": "Το νόμισμα αποθηκεύτηκε."
+ },
+ "login": {
+ "tagline": "Οικογενειακός προγραμματισμός. Ασφαλής. Φιλικός προς την ιδιωτικότητα. Ανοιχτός κώδικας.",
+ "usernameLabel": "Όνομα χρήστη",
+ "usernamePlaceholder": "χρήστης",
+ "passwordLabel": "Κωδικός",
+ "passwordPlaceholder": "••••••••",
+ "loginButton": "Σύνδεση",
+ "loggingIn": "Σύνδεση…",
+ "tooManyAttempts": "Πολλές προσπάθειες. Παρακαλώ περιμένετε λίγο.",
+ "invalidCredentials": "Λανθασμένα στοιχεία σύνδεσης."
+ },
+ "install": {
+ "title": "Εγκατάσταση Oikos",
+ "subtitle": "Προσθήκη στην αρχική οθόνη",
+ "iosTip1": "Πατήστε ",
+ "iosTip2": " → «Προσθήκη στην Αρχική»",
+ "installButton": "Εγκατάσταση",
+ "dismissLabel": "Κλείσιμο"
+ },
+ "modal": {
+ "closeLabel": "Κλείσιμο"
+ },
+ "rrule": {
+ "freqNone": "Χωρίς επανάληψη",
+ "freqDaily": "Ημερησίως",
+ "freqWeekly": "Εβδομαδιαίως",
+ "freqMonthly": "Μηνιαίως",
+ "dayMo": "Δε",
+ "dayTu": "Τρ",
+ "dayWe": "Τε",
+ "dayTh": "Πε",
+ "dayFr": "Πα",
+ "daySa": "Σα",
+ "daySu": "Κυ",
+ "labelRepeat": "Επανάληψη",
+ "labelEvery": "Κάθε",
+ "labelOnDays": "Αυτές τις ημέρες",
+ "labelUntil": "Λήγει (προαιρετικό)",
+ "unitDay": "ημέρα",
+ "unitDays": "ημέρες",
+ "unitWeek": "εβδομάδα",
+ "unitWeeks": "εβδομάδες",
+ "unitMonth": "μήνα",
+ "unitMonths": "μήνες"
+ }
+}
diff --git a/public/locales/fr.json b/public/locales/fr.json
new file mode 100644
index 0000000..d637876
--- /dev/null
+++ b/public/locales/fr.json
@@ -0,0 +1,591 @@
+{
+ "common": {
+ "save": "Enregistrer",
+ "cancel": "Annuler",
+ "delete": "Supprimer",
+ "edit": "Modifier",
+ "close": "Fermer",
+ "create": "Créer",
+ "add": "Ajouter",
+ "back": "Retour",
+ "next": "Suivant",
+ "loading": "Chargement…",
+ "saving": "Enregistrement…",
+ "required": "Ce champ est obligatoire.",
+ "error": "Erreur",
+ "allFieldsRequired": "Veuillez remplir tous les champs.",
+ "today": "Aujourd'hui",
+ "tomorrow": "Demain",
+ "skipToContent": "Aller au contenu",
+ "reload": "Recharger",
+ "errorOccurred": "Une erreur s'est produite.",
+ "unexpectedError": "Une erreur inattendue s'est produite.",
+ "errorGeneric": "Une erreur s'est produite.",
+ "updateAvailable": "Mise à jour disponible - rechargez la page pour obtenir la dernière version.",
+ "titleRequired": "Le titre est obligatoire",
+ "nameRequired": "Le nom est obligatoire",
+ "contentRequired": "Le contenu est obligatoire",
+ "all": "Tout",
+ "unknownError": "Erreur inconnue",
+ "confirm": "Confirmer",
+ "undo": "Annuler"
+ },
+ "nav": {
+ "dashboard": "Accueil",
+ "tasks": "Tâches",
+ "calendar": "Calendrier",
+ "meals": "Repas",
+ "shopping": "Courses",
+ "notes": "Notes",
+ "contacts": "Contacts",
+ "budget": "Budget",
+ "settings": "Paramètres",
+ "main": "Navigation principale",
+ "navigation": "Navigation",
+ "quickActions": "Actions rapides"
+ },
+ "dashboard": {
+ "title": "Accueil",
+ "greetingMorning": "Bonjour, {{name}}",
+ "greetingDay": "Bon après-midi, {{name}}",
+ "greetingEvening": "Bonsoir, {{name}}",
+ "allDone": "Tout est fait",
+ "noEvents": "Aucun événement",
+ "noPinnedNotes": "Aucune note épinglée",
+ "todayMeals": "Repas du jour",
+ "allLink": "Tout",
+ "weekLink": "Semaine",
+ "urgentTasksChip": "{{count}} tâche urgente",
+ "urgentTasksChipPlural": "{{count}} tâches urgentes",
+ "eventsChip": "{{count}} événement aujourd'hui",
+ "eventsChipPlural": "{{count}} événements aujourd'hui",
+ "todayMealChip": "Aujourd'hui : {{title}}",
+ "loadError": "Le tableau de bord n'a pas pu être chargé complètement.",
+ "weatherRefresh": "Actualiser la météo",
+ "weatherRefreshTitle": "Actualiser",
+ "weatherUpdated": "Météo mise à jour",
+ "weatherFeelsLike": "Ressenti {{temp}}° · {{humidity}}% · Vent {{wind}} km/h",
+ "fabTaskLabel": "Ajouter une tâche",
+ "fabCalendarLabel": "Ajouter un événement",
+ "fabShoppingLabel": "Ajouter une course",
+ "fabNoteLabel": "Ajouter une note",
+ "fabTask": "Tâche",
+ "fabCalendar": "Événement",
+ "fabShopping": "Course",
+ "fabNote": "Note",
+ "overdue": "En retard",
+ "dueSoon": "À rendre aujourd'hui",
+ "dueTomorrow": "À rendre demain",
+ "allDay": "Toute la journée",
+ "shoppingMore": "+{{count}} de plus"
+ },
+ "tasks": {
+ "title": "Tâches",
+ "newTask": "Nouvelle tâche",
+ "editTask": "Modifier la tâche",
+ "emptyTitle": "Aucune tâche - tout est fait ?",
+ "emptyDescription": "Créez de nouvelles tâches avec le bouton +.",
+ "titleLabel": "Titre *",
+ "titlePlaceholder": "Que faut-il faire ?",
+ "descriptionLabel": "Note",
+ "descriptionPlaceholder": "Détails optionnels…",
+ "priorityLabel": "Priorité",
+ "categoryLabel": "Catégorie",
+ "dueDateLabel": "Date d'échéance",
+ "dueTimeLabel": "Heure",
+ "assignedLabel": "Assigné à",
+ "assignedNobody": "- Personne -",
+ "statusLabel": "Statut",
+ "priorityUrgent": "Urgent",
+ "priorityHigh": "Élevée",
+ "priorityMedium": "Moyenne",
+ "priorityLow": "Faible",
+ "priorityNone": "Aucune",
+ "statusOpen": "Ouvert",
+ "statusInProgress": "En cours",
+ "statusDone": "Terminé",
+ "categoryHousehold": "Maison",
+ "categorySchool": "École",
+ "categoryShopping": "Courses",
+ "categoryRepair": "Réparation",
+ "categoryHealth": "Santé",
+ "categoryFinance": "Finances",
+ "categoryLeisure": "Loisirs",
+ "categoryMisc": "Divers",
+ "overdue": "En retard",
+ "overdueDay": "{{count}}j de retard",
+ "dueToday": "À rendre aujourd'hui",
+ "dueTomorrow": "À rendre demain",
+ "groupOverdue": "En retard",
+ "groupToday": "Aujourd'hui",
+ "groupThisWeek": "Cette semaine",
+ "groupNextWeek": "Semaine prochaine",
+ "groupLater": "Plus tard",
+ "groupNoDate": "Sans date",
+ "markDone": "Marquer {{title}} comme terminé",
+ "editButton": "Modifier la tâche",
+ "swipeOpen": "Rouvrir",
+ "swipeDone": "Terminé",
+ "swipeEdit": "Modifier",
+ "subtaskAdd": "+ Ajouter une sous-tâche",
+ "subtaskToggle": "Afficher les sous-tâches",
+ "subtaskMarkDone": "Marquer {{title}} comme terminé",
+ "deleteConfirm": "Supprimer la tâche et toutes ses sous-tâches ?",
+ "savedToast": "Tâche enregistrée.",
+ "createdToast": "Tâche créée.",
+ "deletedToast": "Tâche supprimée.",
+ "loadError": "La tâche n'a pas pu être chargée.",
+ "subtaskPrompt": "Sous-tâche :",
+ "kanbanOpen": "Ouvert",
+ "kanbanInProgress": "En cours",
+ "kanbanDone": "Terminé",
+ "kanbanMoveToInProgress": "Mettre en cours",
+ "kanbanMoveToDone": "Marquer comme terminé",
+ "kanbanMoveToOpen": "Rouvrir",
+ "recurring": "Récurrent",
+ "listView": "Vue liste",
+ "kanbanView": "Vue Kanban"
+ },
+ "shopping": {
+ "title": "Courses",
+ "noLists": "Aucune liste",
+ "noListsDescription": "Créez une liste avec le bouton +.",
+ "emptyList": "La liste est vide",
+ "emptyListDescription": "Ajoutez des articles via le champ de saisie ci-dessus.",
+ "newListPrompt": "Nom de la nouvelle liste :",
+ "newListButton": "Créer une nouvelle liste",
+ "renameListPrompt": "Nouveau nom de la liste :",
+ "deleteListConfirm": "Supprimer la liste « {{name}} » et tous ses articles ?",
+ "deletedListToast": "Liste supprimée.",
+ "itemDeletedToast": "« {{name}} » supprimé.",
+ "itemsRemovedToast": "{{count}} articles supprimés.",
+ "clearChecked": "Supprimer les cochés ({{count}})",
+ "itemNamePlaceholder": "Ajouter un article…",
+ "itemQtyPlaceholder": "Quantité",
+ "itemNameLabel": "Nom de l'article",
+ "itemQtyLabel": "Quantité",
+ "categoryLabel": "Catégorie",
+ "addItemLabel": "Ajouter un article",
+ "renameListLabel": "Renommer la liste",
+ "deleteListLabel": "Supprimer la liste",
+ "swipeBack": "Annuler",
+ "swipeCheck": "Cocher",
+ "swipeDelete": "Supprimer",
+ "markDoneLabel": "Cocher {{name}}",
+ "markUndoneLabel": "Décocher {{name}}",
+ "deleteItemLabel": "Supprimer {{name}}",
+ "listsLoadError": "Les listes n'ont pas pu être chargées.",
+ "itemsLoadError": "Les articles n'ont pas pu être chargés.",
+ "catFruitVeg": "Fruits et légumes",
+ "catBakery": "Boulangerie",
+ "catDairy": "Produits laitiers",
+ "catMeatFish": "Viande et poisson",
+ "catFrozen": "Surgelés",
+ "catDrinks": "Boissons",
+ "catHousehold": "Ménage",
+ "catDrugstore": "Pharmacie",
+ "catMisc": "Divers"
+ },
+ "meals": {
+ "title": "Plan de repas",
+ "noMealPlanned": "Aucun repas prévu",
+ "addMeal": "Ajouter {{type}}",
+ "editMeal": "Modifier le repas",
+ "addMealTitle": "Ajouter un repas",
+ "deleteMeal": "Supprimer le repas",
+ "transferToShoppingList": "Ajouter les ingrédients à la liste de courses",
+ "today": "Aujourd'hui",
+ "prevWeek": "Semaine précédente",
+ "nextWeek": "Semaine suivante",
+ "loadError": "Le plan de repas n'a pas pu être chargé.",
+ "typeBreakfast": "Petit-déjeuner",
+ "typeLunch": "Déjeuner",
+ "typeDinner": "Dîner",
+ "typeSnack": "Snack",
+ "dayMo": "Lun",
+ "dayDi": "Mar",
+ "dayMi": "Mer",
+ "dayDo": "Jeu",
+ "dayFr": "Ven",
+ "daySa": "Sam",
+ "daySo": "Dim",
+ "dateLabel": "Date",
+ "mealTypeLabel": "Repas",
+ "titleLabel": "Titre *",
+ "titlePlaceholder": "ex. Pâtes bolognaise",
+ "notesLabel": "Notes",
+ "notesPlaceholder": "Optionnel…",
+ "ingredientsLabel": "Ingrédients",
+ "addIngredient": "Ajouter un ingrédient",
+ "ingredientNamePlaceholder": "Ingrédient",
+ "ingredientQtyPlaceholder": "Quantité",
+ "removeIngredient": "Supprimer l'ingrédient",
+ "transferLabel": "Transférer les ingrédients vers la liste de courses",
+ "transferNow": "Transférer maintenant",
+ "noShoppingLists": "Aucune liste de courses disponible",
+ "transferSuccess": "{{count}} ingrédient transféré",
+ "transferSuccessPlural": "{{count}} ingrédients transférés",
+ "transferAlreadyDone": "Tous les ingrédients ont déjà été transférés",
+ "ingredientCount": "{{count}} ingrédient",
+ "ingredientCountPlural": "{{count}} ingrédients",
+ "titleRequired": "Le titre est obligatoire",
+ "loadingIndicator": "Chargement…",
+ "recipeUrlLabel": "Lien recette (optionnel)",
+ "recipeUrlPlaceholder": "https://…",
+ "openRecipe": "Ouvrir la recette"
+ },
+ "calendar": {
+ "title": "Calendrier",
+ "newEvent": "Nouvel événement",
+ "editEvent": "Modifier l'événement",
+ "addEvent": "Ajouter un événement",
+ "deleteEvent": "Supprimer l'événement",
+ "noEvents": "Aucun événement dans la période sélectionnée.",
+ "today": "Aujourd'hui",
+ "back": "Retour",
+ "forward": "Avancer",
+ "viewMonth": "Mois",
+ "viewWeek": "Semaine",
+ "viewDay": "Jour",
+ "viewAgenda": "Agenda",
+ "allDay": "Toute la journée",
+ "allDayShort": "tj.",
+ "moreEvents": "+{{count}} de plus",
+ "weekNumberLabel": "S{{week}} · {{month}} {{year}}",
+ "agendaFrom": "À partir du {{date}}",
+ "titleLabel": "Titre *",
+ "titlePlaceholder": "ex. Dentiste",
+ "allDayToggle": "Toute la journée",
+ "startDateLabel": "Date de début",
+ "startTimeLabel": "Heure de début",
+ "endDateLabel": "Date de fin",
+ "endTimeLabel": "Heure de fin",
+ "fromLabel": "De",
+ "toLabel": "À",
+ "locationLabel": "Lieu",
+ "locationPlaceholder": "Optionnel",
+ "assignedLabel": "Assigné à",
+ "assignedNobody": "- Personne -",
+ "colorLabel": "Couleur {{color}}",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "Optionnel…",
+ "popupEdit": "Modifier",
+ "deleteConfirm": "Supprimer « {{title}} » ?",
+ "createdToast": "Événement créé",
+ "savedToast": "Événement enregistré",
+ "deletedToast": "Événement supprimé",
+ "loadError": "Les événements n'ont pas pu être chargés.",
+ "saveError": "Erreur lors de l'enregistrement",
+ "deleteError": "Erreur lors de la suppression",
+ "titleRequired": "Le titre est obligatoire",
+ "monthJanuary": "Janvier",
+ "monthFebruary": "Février",
+ "monthMarch": "Mars",
+ "monthApril": "Avril",
+ "monthMay": "Mai",
+ "monthJune": "Juin",
+ "monthJuly": "Juillet",
+ "monthAugust": "Août",
+ "monthSeptember": "Septembre",
+ "monthOctober": "Octobre",
+ "monthNovember": "Novembre",
+ "monthDecember": "Décembre",
+ "dayShortSunday": "Dim",
+ "dayShortMonday": "Lun",
+ "dayShortTuesday": "Mar",
+ "dayShortWednesday": "Mer",
+ "dayShortThursday": "Jeu",
+ "dayShortFriday": "Ven",
+ "dayShortSaturday": "Sam",
+ "dayLongSunday": "Dimanche",
+ "dayLongMonday": "Lundi",
+ "dayLongTuesday": "Mardi",
+ "dayLongWednesday": "Mercredi",
+ "dayLongThursday": "Jeudi",
+ "dayLongFriday": "Vendredi",
+ "dayLongSaturday": "Samedi",
+ "timeSuffix": ""
+ },
+ "notes": {
+ "title": "Notes",
+ "newNote": "Nouvelle note",
+ "editNote": "Modifier la note",
+ "addNoteLabel": "Nouvelle note",
+ "searchPlaceholder": "Rechercher des notes…",
+ "emptyTitle": "Aucune note",
+ "emptyDescription": "Créez une nouvelle note avec le bouton +.",
+ "noResultsTitle": "Aucun résultat",
+ "noResultsDescription": "Aucune note ne contient « {{query}} ».",
+ "titleLabel": "Titre (optionnel)",
+ "titlePlaceholder": "Sans titre",
+ "contentLabel": "Contenu",
+ "contentMarkdownHint": "(Formatage Markdown supporté)",
+ "contentPlaceholder": "Saisir une note…",
+ "colorLabel": "Couleur",
+ "pinnedLabel": "Épingler (apparaît sur le tableau de bord)",
+ "pinAction": "Épingler",
+ "unpinAction": "Désépingler",
+ "deleteLabel": "Supprimer la note",
+ "deleteConfirm": "Supprimer cette note ?",
+ "createdToast": "Note créée",
+ "savedToast": "Note enregistrée",
+ "deletedToast": "Note supprimée",
+ "loadError": "Les notes n'ont pas pu être chargées.",
+ "formatBold": "Gras (Ctrl+B)",
+ "formatItalic": "Italique (Ctrl+I)",
+ "formatUnderline": "Souligné (Ctrl+U)",
+ "formatStrikethrough": "Barré",
+ "formatHeading": "Titre",
+ "formatList": "Liste à puces",
+ "formatOrderedList": "Liste numérotée",
+ "formatChecklist": "Liste de contrôle",
+ "formatLink": "Lien",
+ "formatCode": "Code",
+ "formatQuote": "Citation",
+ "formatDivider": "Séparateur"
+ },
+ "contacts": {
+ "title": "Contacts",
+ "newContact": "Nouveau contact",
+ "editContact": "Modifier le contact",
+ "addButton": "Nouveau",
+ "newContactLabel": "Nouveau contact",
+ "searchPlaceholder": "Rechercher par nom, téléphone ou e-mail…",
+ "importButton": "Importer",
+ "importLabel": "Importer un contact depuis vCard",
+ "importTooltip": "Importer vCard",
+ "emptyTitle": "Aucun contact",
+ "emptyDescription": "Ajoutez de nouveaux contacts avec le bouton +.",
+ "filterAll": "Tous",
+ "nameLabel": "Nom *",
+ "namePlaceholder": "Nom complet",
+ "categoryLabel": "Catégorie",
+ "phoneLabel": "Téléphone",
+ "phonePlaceholder": "+33 …",
+ "emailLabel": "E-mail",
+ "emailPlaceholder": "nom@exemple.fr",
+ "addressLabel": "Adresse",
+ "addressPlaceholder": "Rue, Code postal Ville",
+ "notesLabel": "Notes",
+ "notesPlaceholder": "Optionnel…",
+ "callLabel": "Appeler",
+ "emailActionLabel": "E-mail",
+ "mapsLabel": "Ouvrir dans Maps",
+ "exportLabel": "Exporter en vCard",
+ "exportTooltip": "Exporter vCard",
+ "deleteLabel": "Supprimer le contact",
+ "deleteConfirm": "Supprimer ce contact ?",
+ "deletePersonConfirm": "Supprimer « {{name}} » ?",
+ "savedToast": "Contact enregistré",
+ "updatedToast": "Contact mis à jour",
+ "deletedToast": "Contact supprimé",
+ "importedToast": "{{name}} importé.",
+ "importError": "Importation échouée : {{error}}",
+ "vcardNoName": "La vCard ne contient pas de nom.",
+ "catDoctor": "Médecin",
+ "catSchool": "École/Crèche",
+ "catAuthority": "Administration",
+ "catInsurance": "Assurance",
+ "catCraftsman": "Artisan",
+ "catEmergency": "Urgence",
+ "catMisc": "Divers",
+ "categoryDoctor": "Médecin",
+ "categorySchool": "École/Crèche",
+ "categoryAuthority": "Administration",
+ "categoryInsurance": "Assurance",
+ "categoryCraftsman": "Artisan",
+ "categoryEmergency": "Urgence",
+ "categoryOther": "Autre"
+ },
+ "budget": {
+ "title": "Budget",
+ "newEntry": "Nouvelle entrée",
+ "editEntry": "Modifier l'entrée",
+ "addEntryLabel": "Ajouter une entrée",
+ "newEntryFabLabel": "Nouvelle entrée",
+ "currentMonth": "Actuel",
+ "prevMonth": "Mois précédent",
+ "nextMonth": "Mois suivant",
+ "income": "Revenus",
+ "expenses": "Dépenses",
+ "balance": "Solde",
+ "byCategory": "Par catégorie",
+ "transactions": "Transactions",
+ "emptyTitle": "Aucune entrée ce mois-ci",
+ "emptyDescription": "Ajoutez des entrées budgétaires avec le bouton +.",
+ "csvExport": "CSV",
+ "typeExpense": "Dépense",
+ "typeIncome": "Revenu",
+ "titleLabel": "Titre *",
+ "titlePlaceholder": "ex. Supermarché",
+ "amountLabel": "Montant *",
+ "amountPlaceholder": "0,00",
+ "categoryLabel": "Catégorie",
+ "dateLabel": "Date *",
+ "recurringLabel": "Récurrent",
+ "deleteLabel": "Supprimer l'entrée",
+ "deleteConfirm": "Supprimer cette entrée ?",
+ "deletePersonConfirm": "Supprimer « {{title}} » ?",
+ "addedToast": "Entrée ajoutée",
+ "savedToast": "Entrée enregistrée",
+ "deletedToast": "Entrée supprimée",
+ "loadError": "Le budget n'a pas pu être chargé.",
+ "trendNeutral": "- identique à {{month}}",
+ "validAmountRequired": "Veuillez saisir un montant valide",
+ "dateRequired": "La date est obligatoire",
+ "catFood": "Alimentation",
+ "catRent": "Loyer",
+ "catInsurance": "Assurance",
+ "catMobility": "Transport",
+ "catLeisure": "Loisirs",
+ "catClothing": "Vêtements",
+ "catHealth": "Santé",
+ "catEducation": "Éducation",
+ "catMisc": "Divers",
+ "loadingIndicator": "Chargement…"
+ },
+ "settings": {
+ "title": "Paramètres",
+ "tabGeneral": "Général",
+ "tabMeals": "Repas",
+ "tabBudget": "Budget",
+ "tabShopping": "Courses",
+ "tabCalendar": "Calendrier",
+ "tabAccount": "Compte",
+ "tabsAriaLabel": "Sections des paramètres",
+ "sectionDesign": "Apparence",
+ "sectionShopping": "Courses",
+ "shoppingCategoriesLabel": "Catégories de courses",
+ "shoppingCategoriesHint": "Ajoutez, renommez, supprimez ou réorganisez les catégories.",
+ "shoppingCategoryPlaceholder": "Nouvelle catégorie…",
+ "shoppingCategoryRenameHint": "Cliquez pour renommer",
+ "shoppingCategoryRenamePrompt": "Nouveau nom de catégorie :",
+ "shoppingCategoryMoveUp": "Déplacer la catégorie vers le haut",
+ "shoppingCategoryMoveDown": "Déplacer la catégorie vers le bas",
+ "shoppingCategoryDelete": "Supprimer la catégorie",
+ "shoppingCategoryDeleteConfirm": "Supprimer la catégorie « {{name}} » ? Les articles existants seront déplacés vers la catégorie suivante.",
+ "shoppingCategoryAdded": "Catégorie ajoutée.",
+ "shoppingCategoryRenamed": "Catégorie renommée.",
+ "shoppingCategoryDeleted": "Catégorie supprimée.",
+ "sectionAccount": "Mon compte",
+ "sectionCalendarSync": "Synchronisation du calendrier",
+ "sectionFamily": "Membres de la famille",
+ "cardAppearance": "Affichage",
+ "themeSystem": "Système",
+ "themeSysLabel": "Utiliser le paramètre système",
+ "themeLight": "Clair",
+ "themeLightLabel": "Mode clair",
+ "themeDark": "Sombre",
+ "themeDarkLabel": "Mode sombre",
+ "changePassword": "Changer le mot de passe",
+ "currentPasswordLabel": "Mot de passe actuel",
+ "newPasswordLabel": "Nouveau mot de passe",
+ "confirmPasswordLabel": "Confirmer le nouveau mot de passe",
+ "savePassword": "Enregistrer le mot de passe",
+ "passwordMismatch": "Les mots de passe ne correspondent pas.",
+ "passwordSavedToast": "Mot de passe modifié avec succès.",
+ "googleCalendar": "Google Agenda",
+ "appleCalendar": "Apple Calendar (iCloud)",
+ "syncNow": "Synchroniser maintenant",
+ "disconnect": "Déconnecter",
+ "connectGoogle": "Connecter avec Google",
+ "connected": "Connecté",
+ "connectedLastSync": "Connecté · Dernière sync. : {{date}}",
+ "notConnected": "Non connecté",
+ "notConfigured": "Non configuré (variables .env manquantes)",
+ "configured": "Configuré (via .env)",
+ "configuredLastSync": "Configuré (via .env) · Dernière sync. : {{date}}",
+ "syncSuccess": "{{provider}} synchronisé.",
+ "disconnectedToast": "{{provider}} déconnecté.",
+ "googleOnlyAdmin": "Seul l'administrateur peut connecter Google Agenda.",
+ "appleOnlyAdmin": "Seul l'administrateur peut connecter Apple Calendar.",
+ "caldavUrlLabel": "URL du serveur CalDAV",
+ "caldavUrlPlaceholder": "https://caldav.icloud.com",
+ "appleIdLabel": "Identifiant Apple (e-mail)",
+ "applePasswordLabel": "Mot de passe spécifique à l'app",
+ "applePasswordHint": "Créez le mot de passe sur appleid.apple.com → Sécurité.",
+ "appleConnectBtn": "Connecter et tester",
+ "appleConnecting": "Connexion…",
+ "appleConnectedToast": "Apple Calendar connecté.",
+ "syncSuccessGoogle": "Synchronisation du calendrier avec Google réussie.",
+ "syncSuccessApple": "Synchronisation du calendrier avec Apple réussie.",
+ "syncErrorGoogle": "La connexion à Google a échoué. Veuillez réessayer.",
+ "syncErrorApple": "La connexion à Apple a échoué. Veuillez réessayer.",
+ "addMember": "+ Ajouter un membre",
+ "newMemberTitle": "Nouveau membre de la famille",
+ "usernameLabel": "Nom d'utilisateur",
+ "displayNameLabel": "Nom affiché",
+ "memberPasswordLabel": "Mot de passe",
+ "colorLabel": "Couleur",
+ "roleLabel": "Rôle",
+ "roleMember": "Membre",
+ "roleAdmin": "Admin",
+ "createMember": "Créer",
+ "cancelAddMember": "Annuler",
+ "memberAddedToast": "{{name}} ajouté.",
+ "deleteMemberConfirm": "Supprimer {{name}} ?",
+ "memberDeletedToast": "{{name}} supprimé.",
+ "deleteMemberLabel": "Supprimer",
+ "logout": "Se déconnecter",
+ "synchronizing": "Synchronisation…",
+ "googleDisconnectConfirm": "Déconnecter Google Agenda ?",
+ "appleDisconnectConfirm": "Déconnecter Apple Calendar ?",
+ "localeSystem": "Système",
+ "localeLabel": "Langue",
+ "languageTitle": "Langue",
+ "sectionMeals": "Plan de repas",
+ "mealTypesLabel": "Repas visibles",
+ "mealTypesHint": "Seuls les types de repas sélectionnés s'affichent dans le planificateur.",
+ "mealTypesSaved": "Paramètres du plan de repas enregistrés.",
+ "mealTypesMinOne": "Au moins un type de repas doit être actif.",
+ "sectionBudget": "Budget",
+ "currencyLabel": "Devise",
+ "currencyHint": "Définit la devise utilisée dans toute la section budget.",
+ "currencySaved": "Devise enregistrée."
+ },
+ "login": {
+ "tagline": "Planification familiale. Sécurisée. Respectueuse de la vie privée. Open source.",
+ "usernameLabel": "Nom d'utilisateur",
+ "usernamePlaceholder": "identifiant",
+ "passwordLabel": "Mot de passe",
+ "passwordPlaceholder": "••••••••",
+ "loginButton": "Se connecter",
+ "loggingIn": "Connexion…",
+ "tooManyAttempts": "Trop de tentatives. Veuillez patienter un moment.",
+ "invalidCredentials": "Identifiants invalides."
+ },
+ "install": {
+ "title": "Installer Oikos",
+ "subtitle": "Ajouter à l'écran d'accueil",
+ "iosTip1": "Appuyez sur ",
+ "iosTip2": " → « Ajouter à l'écran d'accueil »",
+ "installButton": "Installer",
+ "dismissLabel": "Fermer"
+ },
+ "modal": {
+ "closeLabel": "Fermer"
+ },
+ "rrule": {
+ "freqNone": "Pas de répétition",
+ "freqDaily": "Quotidien",
+ "freqWeekly": "Hebdomadaire",
+ "freqMonthly": "Mensuel",
+ "dayMo": "Lu",
+ "dayTu": "Ma",
+ "dayWe": "Me",
+ "dayTh": "Je",
+ "dayFr": "Ve",
+ "daySa": "Sa",
+ "daySu": "Di",
+ "labelRepeat": "Répétition",
+ "labelEvery": "Tous les",
+ "labelOnDays": "Ces jours-ci",
+ "labelUntil": "Se termine le (optionnel)",
+ "unitDay": "jour",
+ "unitDays": "jours",
+ "unitWeek": "semaine",
+ "unitWeeks": "semaines",
+ "unitMonth": "mois",
+ "unitMonths": "mois"
+ }
+}
diff --git a/public/locales/it.json b/public/locales/it.json
index 649e728..0b1abdf 100644
--- a/public/locales/it.json
+++ b/public/locales/it.json
@@ -564,5 +564,28 @@
},
"modal": {
"closeLabel": "Chiudi"
+ },
+ "rrule": {
+ "freqNone": "Nessuna ripetizione",
+ "freqDaily": "Ogni giorno",
+ "freqWeekly": "Ogni settimana",
+ "freqMonthly": "Ogni mese",
+ "dayMo": "Lu",
+ "dayTu": "Ma",
+ "dayWe": "Me",
+ "dayTh": "Gi",
+ "dayFr": "Ve",
+ "daySa": "Sa",
+ "daySu": "Do",
+ "labelRepeat": "Ripetizione",
+ "labelEvery": "Ogni",
+ "labelOnDays": "In questi giorni",
+ "labelUntil": "Termina il (opzionale)",
+ "unitDay": "giorno",
+ "unitDays": "giorni",
+ "unitWeek": "settimana",
+ "unitWeeks": "settimane",
+ "unitMonth": "mese",
+ "unitMonths": "mesi"
}
}
\ No newline at end of file
diff --git a/public/locales/ru.json b/public/locales/ru.json
new file mode 100644
index 0000000..6dba6ef
--- /dev/null
+++ b/public/locales/ru.json
@@ -0,0 +1,591 @@
+{
+ "common": {
+ "save": "Сохранить",
+ "cancel": "Отмена",
+ "delete": "Удалить",
+ "edit": "Редактировать",
+ "close": "Закрыть",
+ "create": "Создать",
+ "add": "Добавить",
+ "back": "Назад",
+ "next": "Далее",
+ "loading": "Загрузка…",
+ "saving": "Сохранение…",
+ "required": "Это поле обязательно.",
+ "error": "Ошибка",
+ "allFieldsRequired": "Пожалуйста, заполните все поля.",
+ "today": "Сегодня",
+ "tomorrow": "Завтра",
+ "skipToContent": "Перейти к содержимому",
+ "reload": "Обновить",
+ "errorOccurred": "Что-то пошло не так.",
+ "unexpectedError": "Произошла непредвиденная ошибка.",
+ "errorGeneric": "Произошла ошибка.",
+ "updateAvailable": "Доступно обновление — перезагрузите страницу для получения последней версии.",
+ "titleRequired": "Заголовок обязателен",
+ "nameRequired": "Имя обязательно",
+ "contentRequired": "Содержимое обязательно",
+ "all": "Все",
+ "unknownError": "Неизвестная ошибка",
+ "confirm": "Подтвердить",
+ "undo": "Отменить"
+ },
+ "nav": {
+ "dashboard": "Обзор",
+ "tasks": "Задачи",
+ "calendar": "Календарь",
+ "meals": "Питание",
+ "shopping": "Покупки",
+ "notes": "Заметки",
+ "contacts": "Контакты",
+ "budget": "Бюджет",
+ "settings": "Настройки",
+ "main": "Главная навигация",
+ "navigation": "Навигация",
+ "quickActions": "Быстрые действия"
+ },
+ "dashboard": {
+ "title": "Обзор",
+ "greetingMorning": "Доброе утро, {{name}}",
+ "greetingDay": "Добрый день, {{name}}",
+ "greetingEvening": "Добрый вечер, {{name}}",
+ "allDone": "Всё готово",
+ "noEvents": "Нет событий",
+ "noPinnedNotes": "Нет закреплённых заметок",
+ "todayMeals": "Питание сегодня",
+ "allLink": "Все",
+ "weekLink": "Неделя",
+ "urgentTasksChip": "{{count}} срочная задача",
+ "urgentTasksChipPlural": "{{count}} срочных задач",
+ "eventsChip": "{{count}} событие сегодня",
+ "eventsChipPlural": "{{count}} событий сегодня",
+ "todayMealChip": "Сегодня: {{title}}",
+ "loadError": "Не удалось полностью загрузить панель управления.",
+ "weatherRefresh": "Обновить погоду",
+ "weatherRefreshTitle": "Обновить",
+ "weatherUpdated": "Погода обновлена",
+ "weatherFeelsLike": "Ощущается как {{temp}}° · {{humidity}}% · Ветер {{wind}} км/ч",
+ "fabTaskLabel": "Добавить задачу",
+ "fabCalendarLabel": "Добавить событие",
+ "fabShoppingLabel": "Добавить покупку",
+ "fabNoteLabel": "Добавить заметку",
+ "fabTask": "Задача",
+ "fabCalendar": "Событие",
+ "fabShopping": "Покупка",
+ "fabNote": "Заметка",
+ "overdue": "Просрочено",
+ "dueSoon": "Сегодня",
+ "dueTomorrow": "Завтра",
+ "allDay": "Весь день",
+ "shoppingMore": "+{{count}} ещё"
+ },
+ "tasks": {
+ "title": "Задачи",
+ "newTask": "Новая задача",
+ "editTask": "Редактировать задачу",
+ "emptyTitle": "Нет задач — всё готово?",
+ "emptyDescription": "Создайте новые задачи с помощью кнопки +.",
+ "titleLabel": "Заголовок *",
+ "titlePlaceholder": "Что нужно сделать?",
+ "descriptionLabel": "Заметка",
+ "descriptionPlaceholder": "Дополнительные сведения…",
+ "priorityLabel": "Приоритет",
+ "categoryLabel": "Категория",
+ "dueDateLabel": "Срок выполнения",
+ "dueTimeLabel": "Время",
+ "assignedLabel": "Назначено",
+ "assignedNobody": "- Никому -",
+ "statusLabel": "Статус",
+ "priorityUrgent": "Срочно",
+ "priorityHigh": "Высокий",
+ "priorityMedium": "Средний",
+ "priorityLow": "Низкий",
+ "priorityNone": "Нет",
+ "statusOpen": "Открыто",
+ "statusInProgress": "В процессе",
+ "statusDone": "Выполнено",
+ "categoryHousehold": "Домашнее хозяйство",
+ "categorySchool": "Школа",
+ "categoryShopping": "Покупки",
+ "categoryRepair": "Ремонт",
+ "categoryHealth": "Здоровье",
+ "categoryFinance": "Финансы",
+ "categoryLeisure": "Досуг",
+ "categoryMisc": "Разное",
+ "overdue": "Просрочено",
+ "overdueDay": "Просрочено на {{count}}д",
+ "dueToday": "Срок сегодня",
+ "dueTomorrow": "Срок завтра",
+ "groupOverdue": "Просрочено",
+ "groupToday": "Сегодня",
+ "groupThisWeek": "На этой неделе",
+ "groupNextWeek": "На следующей неделе",
+ "groupLater": "Позже",
+ "groupNoDate": "Без даты",
+ "markDone": "Отметить {{title}} как выполненное",
+ "editButton": "Редактировать задачу",
+ "swipeOpen": "Открыть снова",
+ "swipeDone": "Выполнено",
+ "swipeEdit": "Редактировать",
+ "subtaskAdd": "+ Добавить подзадачу",
+ "subtaskToggle": "Показать подзадачи",
+ "subtaskMarkDone": "Отметить {{title}} как выполненное",
+ "deleteConfirm": "Удалить задачу и все подзадачи?",
+ "savedToast": "Задача сохранена.",
+ "createdToast": "Задача создана.",
+ "deletedToast": "Задача удалена.",
+ "loadError": "Не удалось загрузить задачу.",
+ "subtaskPrompt": "Подзадача:",
+ "kanbanOpen": "Открыто",
+ "kanbanInProgress": "В процессе",
+ "kanbanDone": "Выполнено",
+ "kanbanMoveToInProgress": "Перевести в работу",
+ "kanbanMoveToDone": "Отметить как выполненное",
+ "kanbanMoveToOpen": "Открыть снова",
+ "recurring": "Повторяющееся",
+ "listView": "Список",
+ "kanbanView": "Канбан"
+ },
+ "shopping": {
+ "title": "Покупки",
+ "noLists": "Нет списков",
+ "noListsDescription": "Создайте список с помощью кнопки +.",
+ "emptyList": "Список пуст",
+ "emptyListDescription": "Добавьте товары через поле ввода выше.",
+ "newListPrompt": "Название нового списка:",
+ "newListButton": "Создать новый список",
+ "renameListPrompt": "Новое название списка:",
+ "deleteListConfirm": "Удалить список «{{name}}» и все товары?",
+ "deletedListToast": "Список удалён.",
+ "itemDeletedToast": "«{{name}}» удалено.",
+ "itemsRemovedToast": "{{count}} товаров удалено.",
+ "clearChecked": "Удалить отмеченные ({{count}})",
+ "itemNamePlaceholder": "Добавить товар…",
+ "itemQtyPlaceholder": "Количество",
+ "itemNameLabel": "Название товара",
+ "itemQtyLabel": "Количество",
+ "categoryLabel": "Категория",
+ "addItemLabel": "Добавить товар",
+ "renameListLabel": "Переименовать список",
+ "deleteListLabel": "Удалить список",
+ "swipeBack": "Отменить",
+ "swipeCheck": "Отметить",
+ "swipeDelete": "Удалить",
+ "markDoneLabel": "Отметить {{name}}",
+ "markUndoneLabel": "Снять отметку с {{name}}",
+ "deleteItemLabel": "Удалить {{name}}",
+ "listsLoadError": "Не удалось загрузить списки.",
+ "itemsLoadError": "Не удалось загрузить товары.",
+ "catFruitVeg": "Фрукты и овощи",
+ "catBakery": "Выпечка",
+ "catDairy": "Молочные продукты",
+ "catMeatFish": "Мясо и рыба",
+ "catFrozen": "Заморозка",
+ "catDrinks": "Напитки",
+ "catHousehold": "Хозтовары",
+ "catDrugstore": "Аптека",
+ "catMisc": "Разное"
+ },
+ "meals": {
+ "title": "План питания",
+ "noMealPlanned": "Питание не запланировано",
+ "addMeal": "Добавить {{type}}",
+ "editMeal": "Редактировать приём пищи",
+ "addMealTitle": "Добавить приём пищи",
+ "deleteMeal": "Удалить приём пищи",
+ "transferToShoppingList": "Добавить ингредиенты в список покупок",
+ "today": "Сегодня",
+ "prevWeek": "Предыдущая неделя",
+ "nextWeek": "Следующая неделя",
+ "loadError": "Не удалось загрузить план питания.",
+ "typeBreakfast": "Завтрак",
+ "typeLunch": "Обед",
+ "typeDinner": "Ужин",
+ "typeSnack": "Перекус",
+ "dayMo": "Пн",
+ "dayDi": "Вт",
+ "dayMi": "Ср",
+ "dayDo": "Чт",
+ "dayFr": "Пт",
+ "daySa": "Сб",
+ "daySo": "Вс",
+ "dateLabel": "Дата",
+ "mealTypeLabel": "Приём пищи",
+ "titleLabel": "Название *",
+ "titlePlaceholder": "напр. Борщ",
+ "notesLabel": "Заметки",
+ "notesPlaceholder": "Необязательно…",
+ "ingredientsLabel": "Ингредиенты",
+ "addIngredient": "Добавить ингредиент",
+ "ingredientNamePlaceholder": "Ингредиент",
+ "ingredientQtyPlaceholder": "Количество",
+ "removeIngredient": "Удалить ингредиент",
+ "transferLabel": "Перенести ингредиенты в список покупок",
+ "transferNow": "Перенести сейчас",
+ "noShoppingLists": "Нет списков покупок",
+ "transferSuccess": "{{count}} ингредиент перенесён",
+ "transferSuccessPlural": "{{count}} ингредиентов перенесено",
+ "transferAlreadyDone": "Все ингредиенты уже перенесены",
+ "ingredientCount": "{{count}} ингредиент",
+ "ingredientCountPlural": "{{count}} ингредиентов",
+ "titleRequired": "Название обязательно",
+ "loadingIndicator": "Загрузка…",
+ "recipeUrlLabel": "Ссылка на рецепт (необязательно)",
+ "recipeUrlPlaceholder": "https://…",
+ "openRecipe": "Открыть рецепт"
+ },
+ "calendar": {
+ "title": "Календарь",
+ "newEvent": "Новое событие",
+ "editEvent": "Редактировать событие",
+ "addEvent": "Добавить событие",
+ "deleteEvent": "Удалить событие",
+ "noEvents": "Нет событий в выбранном периоде.",
+ "today": "Сегодня",
+ "back": "Назад",
+ "forward": "Вперёд",
+ "viewMonth": "Месяц",
+ "viewWeek": "Неделя",
+ "viewDay": "День",
+ "viewAgenda": "Повестка",
+ "allDay": "Весь день",
+ "allDayShort": "весь день",
+ "moreEvents": "+{{count}} ещё",
+ "weekNumberLabel": "Н{{week}} · {{month}} {{year}}",
+ "agendaFrom": "С {{date}}",
+ "titleLabel": "Заголовок *",
+ "titlePlaceholder": "напр. Врач",
+ "allDayToggle": "Весь день",
+ "startDateLabel": "Дата начала",
+ "startTimeLabel": "Время начала",
+ "endDateLabel": "Дата окончания",
+ "endTimeLabel": "Время окончания",
+ "fromLabel": "С",
+ "toLabel": "По",
+ "locationLabel": "Место",
+ "locationPlaceholder": "Необязательно",
+ "assignedLabel": "Назначено",
+ "assignedNobody": "- Никому -",
+ "colorLabel": "Цвет {{color}}",
+ "descriptionLabel": "Описание",
+ "descriptionPlaceholder": "Необязательно…",
+ "popupEdit": "Редактировать",
+ "deleteConfirm": "Удалить «{{title}}»?",
+ "createdToast": "Событие создано",
+ "savedToast": "Событие сохранено",
+ "deletedToast": "Событие удалено",
+ "loadError": "Не удалось загрузить события.",
+ "saveError": "Ошибка сохранения",
+ "deleteError": "Ошибка удаления",
+ "titleRequired": "Заголовок обязателен",
+ "monthJanuary": "Январь",
+ "monthFebruary": "Февраль",
+ "monthMarch": "Март",
+ "monthApril": "Апрель",
+ "monthMay": "Май",
+ "monthJune": "Июнь",
+ "monthJuly": "Июль",
+ "monthAugust": "Август",
+ "monthSeptember": "Сентябрь",
+ "monthOctober": "Октябрь",
+ "monthNovember": "Ноябрь",
+ "monthDecember": "Декабрь",
+ "dayShortSunday": "Вс",
+ "dayShortMonday": "Пн",
+ "dayShortTuesday": "Вт",
+ "dayShortWednesday": "Ср",
+ "dayShortThursday": "Чт",
+ "dayShortFriday": "Пт",
+ "dayShortSaturday": "Сб",
+ "dayLongSunday": "Воскресенье",
+ "dayLongMonday": "Понедельник",
+ "dayLongTuesday": "Вторник",
+ "dayLongWednesday": "Среда",
+ "dayLongThursday": "Четверг",
+ "dayLongFriday": "Пятница",
+ "dayLongSaturday": "Суббота",
+ "timeSuffix": ""
+ },
+ "notes": {
+ "title": "Заметки",
+ "newNote": "Новая заметка",
+ "editNote": "Редактировать заметку",
+ "addNoteLabel": "Новая заметка",
+ "searchPlaceholder": "Поиск по заметкам…",
+ "emptyTitle": "Нет заметок",
+ "emptyDescription": "Создайте новую заметку с помощью кнопки +.",
+ "noResultsTitle": "Нет результатов",
+ "noResultsDescription": "Ни одна заметка не содержит «{{query}}».",
+ "titleLabel": "Заголовок (необязательно)",
+ "titlePlaceholder": "Без заголовка",
+ "contentLabel": "Содержимое",
+ "contentMarkdownHint": "(Поддерживается Markdown-форматирование)",
+ "contentPlaceholder": "Введите заметку…",
+ "colorLabel": "Цвет",
+ "pinnedLabel": "Закрепить (отображается на панели)",
+ "pinAction": "Закрепить",
+ "unpinAction": "Открепить",
+ "deleteLabel": "Удалить заметку",
+ "deleteConfirm": "Удалить эту заметку?",
+ "createdToast": "Заметка создана",
+ "savedToast": "Заметка сохранена",
+ "deletedToast": "Заметка удалена",
+ "loadError": "Не удалось загрузить заметки.",
+ "formatBold": "Жирный (Ctrl+B)",
+ "formatItalic": "Курсив (Ctrl+I)",
+ "formatUnderline": "Подчёркнутый (Ctrl+U)",
+ "formatStrikethrough": "Зачёркнутый",
+ "formatHeading": "Заголовок",
+ "formatList": "Маркированный список",
+ "formatOrderedList": "Нумерованный список",
+ "formatChecklist": "Список задач",
+ "formatLink": "Ссылка",
+ "formatCode": "Код",
+ "formatQuote": "Цитата",
+ "formatDivider": "Разделитель"
+ },
+ "contacts": {
+ "title": "Контакты",
+ "newContact": "Новый контакт",
+ "editContact": "Редактировать контакт",
+ "addButton": "Новый",
+ "newContactLabel": "Новый контакт",
+ "searchPlaceholder": "Поиск по имени, телефону или email…",
+ "importButton": "Импорт",
+ "importLabel": "Импортировать контакт из vCard",
+ "importTooltip": "Импортировать vCard",
+ "emptyTitle": "Нет контактов",
+ "emptyDescription": "Добавьте новые контакты с помощью кнопки +.",
+ "filterAll": "Все",
+ "nameLabel": "Имя *",
+ "namePlaceholder": "Полное имя",
+ "categoryLabel": "Категория",
+ "phoneLabel": "Телефон",
+ "phonePlaceholder": "+7 …",
+ "emailLabel": "Email",
+ "emailPlaceholder": "имя@пример.ру",
+ "addressLabel": "Адрес",
+ "addressPlaceholder": "Улица, индекс Город",
+ "notesLabel": "Заметки",
+ "notesPlaceholder": "Необязательно…",
+ "callLabel": "Позвонить",
+ "emailActionLabel": "Email",
+ "mapsLabel": "Открыть в Картах",
+ "exportLabel": "Экспортировать в vCard",
+ "exportTooltip": "Экспорт vCard",
+ "deleteLabel": "Удалить контакт",
+ "deleteConfirm": "Удалить этот контакт?",
+ "deletePersonConfirm": "Удалить «{{name}}»?",
+ "savedToast": "Контакт сохранён",
+ "updatedToast": "Контакт обновлён",
+ "deletedToast": "Контакт удалён",
+ "importedToast": "{{name}} импортирован.",
+ "importError": "Ошибка импорта: {{error}}",
+ "vcardNoName": "В vCard нет имени.",
+ "catDoctor": "Врач",
+ "catSchool": "Школа/Детсад",
+ "catAuthority": "Гос. орган",
+ "catInsurance": "Страховая",
+ "catCraftsman": "Мастер",
+ "catEmergency": "Экстренная помощь",
+ "catMisc": "Разное",
+ "categoryDoctor": "Врач",
+ "categorySchool": "Школа/Детсад",
+ "categoryAuthority": "Гос. орган",
+ "categoryInsurance": "Страховая",
+ "categoryCraftsman": "Мастер",
+ "categoryEmergency": "Экстренная помощь",
+ "categoryOther": "Другое"
+ },
+ "budget": {
+ "title": "Бюджет",
+ "newEntry": "Новая запись",
+ "editEntry": "Редактировать запись",
+ "addEntryLabel": "Добавить запись",
+ "newEntryFabLabel": "Новая запись",
+ "currentMonth": "Текущий",
+ "prevMonth": "Предыдущий месяц",
+ "nextMonth": "Следующий месяц",
+ "income": "Доходы",
+ "expenses": "Расходы",
+ "balance": "Баланс",
+ "byCategory": "По категориям",
+ "transactions": "Транзакции",
+ "emptyTitle": "Нет записей за этот месяц",
+ "emptyDescription": "Добавьте записи бюджета с помощью кнопки +.",
+ "csvExport": "CSV",
+ "typeExpense": "Расход",
+ "typeIncome": "Доход",
+ "titleLabel": "Заголовок *",
+ "titlePlaceholder": "напр. Продукты",
+ "amountLabel": "Сумма *",
+ "amountPlaceholder": "0,00",
+ "categoryLabel": "Категория",
+ "dateLabel": "Дата *",
+ "recurringLabel": "Повторяющееся",
+ "deleteLabel": "Удалить запись",
+ "deleteConfirm": "Удалить эту запись?",
+ "deletePersonConfirm": "Удалить «{{title}}»?",
+ "addedToast": "Запись добавлена",
+ "savedToast": "Запись сохранена",
+ "deletedToast": "Запись удалена",
+ "loadError": "Не удалось загрузить бюджет.",
+ "trendNeutral": "— как в {{month}}",
+ "validAmountRequired": "Введите корректную сумму",
+ "dateRequired": "Дата обязательна",
+ "catFood": "Продукты",
+ "catRent": "Аренда",
+ "catInsurance": "Страховка",
+ "catMobility": "Транспорт",
+ "catLeisure": "Досуг",
+ "catClothing": "Одежда",
+ "catHealth": "Здоровье",
+ "catEducation": "Образование",
+ "catMisc": "Разное",
+ "loadingIndicator": "Загрузка…"
+ },
+ "settings": {
+ "title": "Настройки",
+ "tabGeneral": "Общие",
+ "tabMeals": "Питание",
+ "tabBudget": "Бюджет",
+ "tabShopping": "Покупки",
+ "tabCalendar": "Календарь",
+ "tabAccount": "Аккаунт",
+ "tabsAriaLabel": "Разделы настроек",
+ "sectionDesign": "Внешний вид",
+ "sectionShopping": "Покупки",
+ "shoppingCategoriesLabel": "Категории покупок",
+ "shoppingCategoriesHint": "Добавляйте, переименовывайте, удаляйте или сортируйте категории.",
+ "shoppingCategoryPlaceholder": "Новая категория…",
+ "shoppingCategoryRenameHint": "Нажмите для переименования",
+ "shoppingCategoryRenamePrompt": "Новое название категории:",
+ "shoppingCategoryMoveUp": "Переместить категорию вверх",
+ "shoppingCategoryMoveDown": "Переместить категорию вниз",
+ "shoppingCategoryDelete": "Удалить категорию",
+ "shoppingCategoryDeleteConfirm": "Удалить категорию «{{name}}»? Существующие товары будут перемещены в следующую категорию.",
+ "shoppingCategoryAdded": "Категория добавлена.",
+ "shoppingCategoryRenamed": "Категория переименована.",
+ "shoppingCategoryDeleted": "Категория удалена.",
+ "sectionAccount": "Мой аккаунт",
+ "sectionCalendarSync": "Синхронизация календаря",
+ "sectionFamily": "Члены семьи",
+ "cardAppearance": "Отображение",
+ "themeSystem": "Система",
+ "themeSysLabel": "Использовать системную настройку",
+ "themeLight": "Светлая",
+ "themeLightLabel": "Светлая тема",
+ "themeDark": "Тёмная",
+ "themeDarkLabel": "Тёмная тема",
+ "changePassword": "Изменить пароль",
+ "currentPasswordLabel": "Текущий пароль",
+ "newPasswordLabel": "Новый пароль",
+ "confirmPasswordLabel": "Подтвердить новый пароль",
+ "savePassword": "Сохранить пароль",
+ "passwordMismatch": "Пароли не совпадают.",
+ "passwordSavedToast": "Пароль успешно изменён.",
+ "googleCalendar": "Google Календарь",
+ "appleCalendar": "Apple Calendar (iCloud)",
+ "syncNow": "Синхронизировать сейчас",
+ "disconnect": "Отключить",
+ "connectGoogle": "Подключить Google",
+ "connected": "Подключено",
+ "connectedLastSync": "Подключено · Последняя: {{date}}",
+ "notConnected": "Не подключено",
+ "notConfigured": "Не настроено (отсутствуют переменные .env)",
+ "configured": "Настроено (через .env)",
+ "configuredLastSync": "Настроено (через .env) · Последняя: {{date}}",
+ "syncSuccess": "{{provider}} синхронизирован.",
+ "disconnectedToast": "{{provider}} отключён.",
+ "googleOnlyAdmin": "Только администратор может подключить Google Календарь.",
+ "appleOnlyAdmin": "Только администратор может подключить Apple Calendar.",
+ "caldavUrlLabel": "URL сервера CalDAV",
+ "caldavUrlPlaceholder": "https://caldav.icloud.com",
+ "appleIdLabel": "Apple ID (email)",
+ "applePasswordLabel": "Пароль для приложения",
+ "applePasswordHint": "Создайте пароль на appleid.apple.com → Безопасность.",
+ "appleConnectBtn": "Подключить и протестировать",
+ "appleConnecting": "Подключение…",
+ "appleConnectedToast": "Apple Calendar подключён.",
+ "syncSuccessGoogle": "Синхронизация календаря с Google успешно подключена.",
+ "syncSuccessApple": "Синхронизация календаря с Apple успешно подключена.",
+ "syncErrorGoogle": "Не удалось подключиться к Google. Попробуйте ещё раз.",
+ "syncErrorApple": "Не удалось подключиться к Apple. Попробуйте ещё раз.",
+ "addMember": "+ Добавить участника",
+ "newMemberTitle": "Новый член семьи",
+ "usernameLabel": "Имя пользователя",
+ "displayNameLabel": "Отображаемое имя",
+ "memberPasswordLabel": "Пароль",
+ "colorLabel": "Цвет",
+ "roleLabel": "Роль",
+ "roleMember": "Участник",
+ "roleAdmin": "Администратор",
+ "createMember": "Создать",
+ "cancelAddMember": "Отмена",
+ "memberAddedToast": "{{name}} добавлен.",
+ "deleteMemberConfirm": "Удалить {{name}}?",
+ "memberDeletedToast": "{{name}} удалён.",
+ "deleteMemberLabel": "Удалить",
+ "logout": "Выйти",
+ "synchronizing": "Синхронизация…",
+ "googleDisconnectConfirm": "Отключить Google Календарь?",
+ "appleDisconnectConfirm": "Отключить Apple Calendar?",
+ "localeSystem": "Система",
+ "localeLabel": "Язык",
+ "languageTitle": "Язык",
+ "sectionMeals": "План питания",
+ "mealTypesLabel": "Видимые приёмы пищи",
+ "mealTypesHint": "В плане питания отображаются только выбранные типы приёмов пищи.",
+ "mealTypesSaved": "Настройки плана питания сохранены.",
+ "mealTypesMinOne": "Должен быть активен хотя бы один тип приёма пищи.",
+ "sectionBudget": "Бюджет",
+ "currencyLabel": "Валюта",
+ "currencyHint": "Устанавливает валюту для всего раздела бюджета.",
+ "currencySaved": "Валюта сохранена."
+ },
+ "login": {
+ "tagline": "Семейное планирование. Безопасно. С уважением к приватности. Открытый исходный код.",
+ "usernameLabel": "Имя пользователя",
+ "usernamePlaceholder": "имяпользователя",
+ "passwordLabel": "Пароль",
+ "passwordPlaceholder": "••••••••",
+ "loginButton": "Войти",
+ "loggingIn": "Вход…",
+ "tooManyAttempts": "Слишком много попыток. Подождите немного.",
+ "invalidCredentials": "Неверные данные для входа."
+ },
+ "install": {
+ "title": "Установить Oikos",
+ "subtitle": "Добавить на главный экран",
+ "iosTip1": "Нажмите ",
+ "iosTip2": " → «На экран Домой»",
+ "installButton": "Установить",
+ "dismissLabel": "Закрыть"
+ },
+ "modal": {
+ "closeLabel": "Закрыть"
+ },
+ "rrule": {
+ "freqNone": "Без повтора",
+ "freqDaily": "Ежедневно",
+ "freqWeekly": "Еженедельно",
+ "freqMonthly": "Ежемесячно",
+ "dayMo": "Пн",
+ "dayTu": "Вт",
+ "dayWe": "Ср",
+ "dayTh": "Чт",
+ "dayFr": "Пт",
+ "daySa": "Сб",
+ "daySu": "Вс",
+ "labelRepeat": "Повтор",
+ "labelEvery": "Каждые",
+ "labelOnDays": "В эти дни",
+ "labelUntil": "Заканчивается (необязательно)",
+ "unitDay": "день",
+ "unitDays": "дней",
+ "unitWeek": "неделю",
+ "unitWeeks": "недель",
+ "unitMonth": "месяц",
+ "unitMonths": "месяцев"
+ }
+}
diff --git a/public/locales/tr.json b/public/locales/tr.json
new file mode 100644
index 0000000..75f2789
--- /dev/null
+++ b/public/locales/tr.json
@@ -0,0 +1,591 @@
+{
+ "common": {
+ "save": "Kaydet",
+ "cancel": "İptal",
+ "delete": "Sil",
+ "edit": "Düzenle",
+ "close": "Kapat",
+ "create": "Oluştur",
+ "add": "Ekle",
+ "back": "Geri",
+ "next": "İleri",
+ "loading": "Yükleniyor…",
+ "saving": "Kaydediliyor…",
+ "required": "Bu alan zorunludur.",
+ "error": "Hata",
+ "allFieldsRequired": "Lütfen tüm alanları doldurun.",
+ "today": "Bugün",
+ "tomorrow": "Yarın",
+ "skipToContent": "İçeriğe geç",
+ "reload": "Yenile",
+ "errorOccurred": "Bir şeyler ters gitti.",
+ "unexpectedError": "Beklenmeyen bir hata oluştu.",
+ "errorGeneric": "Bir hata oluştu.",
+ "updateAvailable": "Güncelleme mevcut - en son sürümü almak için sayfayı yenileyin.",
+ "titleRequired": "Başlık zorunludur",
+ "nameRequired": "Ad zorunludur",
+ "contentRequired": "İçerik zorunludur",
+ "all": "Tümü",
+ "unknownError": "Bilinmeyen hata",
+ "confirm": "Onayla",
+ "undo": "Geri al"
+ },
+ "nav": {
+ "dashboard": "Genel Bakış",
+ "tasks": "Görevler",
+ "calendar": "Takvim",
+ "meals": "Yemekler",
+ "shopping": "Alışveriş",
+ "notes": "Notlar",
+ "contacts": "Kişiler",
+ "budget": "Bütçe",
+ "settings": "Ayarlar",
+ "main": "Ana gezinme",
+ "navigation": "Gezinme",
+ "quickActions": "Hızlı işlemler"
+ },
+ "dashboard": {
+ "title": "Genel Bakış",
+ "greetingMorning": "Günaydın, {{name}}",
+ "greetingDay": "İyi günler, {{name}}",
+ "greetingEvening": "İyi akşamlar, {{name}}",
+ "allDone": "Her şey tamam",
+ "noEvents": "Etkinlik yok",
+ "noPinnedNotes": "Sabitlenmiş not yok",
+ "todayMeals": "Bugünün yemekleri",
+ "allLink": "Tümü",
+ "weekLink": "Hafta",
+ "urgentTasksChip": "{{count}} acil görev",
+ "urgentTasksChipPlural": "{{count}} acil görev",
+ "eventsChip": "Bugün {{count}} etkinlik",
+ "eventsChipPlural": "Bugün {{count}} etkinlik",
+ "todayMealChip": "Bugün: {{title}}",
+ "loadError": "Gösterge paneli tam olarak yüklenemedi.",
+ "weatherRefresh": "Hava durumunu yenile",
+ "weatherRefreshTitle": "Yenile",
+ "weatherUpdated": "Hava durumu güncellendi",
+ "weatherFeelsLike": "Hissedilen {{temp}}° · {{humidity}}% · Rüzgar {{wind}} km/s",
+ "fabTaskLabel": "Görev ekle",
+ "fabCalendarLabel": "Etkinlik ekle",
+ "fabShoppingLabel": "Alışveriş ekle",
+ "fabNoteLabel": "Not ekle",
+ "fabTask": "Görev",
+ "fabCalendar": "Etkinlik",
+ "fabShopping": "Alışveriş",
+ "fabNote": "Not",
+ "overdue": "Gecikmiş",
+ "dueSoon": "Bugün bitiyor",
+ "dueTomorrow": "Yarın bitiyor",
+ "allDay": "Tüm gün",
+ "shoppingMore": "+{{count}} daha"
+ },
+ "tasks": {
+ "title": "Görevler",
+ "newTask": "Yeni Görev",
+ "editTask": "Görevi Düzenle",
+ "emptyTitle": "Görev yok - her şey tamam mı?",
+ "emptyDescription": "+ düğmesiyle yeni görevler oluşturun.",
+ "titleLabel": "Başlık *",
+ "titlePlaceholder": "Ne yapılması gerekiyor?",
+ "descriptionLabel": "Not",
+ "descriptionPlaceholder": "İsteğe bağlı ayrıntılar…",
+ "priorityLabel": "Öncelik",
+ "categoryLabel": "Kategori",
+ "dueDateLabel": "Bitiş tarihi",
+ "dueTimeLabel": "Saat",
+ "assignedLabel": "Atanan",
+ "assignedNobody": "- Kimse -",
+ "statusLabel": "Durum",
+ "priorityUrgent": "Acil",
+ "priorityHigh": "Yüksek",
+ "priorityMedium": "Orta",
+ "priorityLow": "Düşük",
+ "priorityNone": "Yok",
+ "statusOpen": "Açık",
+ "statusInProgress": "Devam Ediyor",
+ "statusDone": "Tamamlandı",
+ "categoryHousehold": "Ev İşleri",
+ "categorySchool": "Okul",
+ "categoryShopping": "Alışveriş",
+ "categoryRepair": "Tamir",
+ "categoryHealth": "Sağlık",
+ "categoryFinance": "Finans",
+ "categoryLeisure": "Boş Zaman",
+ "categoryMisc": "Diğer",
+ "overdue": "Gecikmiş",
+ "overdueDay": "{{count}}g gecikmiş",
+ "dueToday": "Bugün bitiyor",
+ "dueTomorrow": "Yarın bitiyor",
+ "groupOverdue": "Gecikmiş",
+ "groupToday": "Bugün",
+ "groupThisWeek": "Bu hafta",
+ "groupNextWeek": "Gelecek hafta",
+ "groupLater": "Sonra",
+ "groupNoDate": "Tarih yok",
+ "markDone": "{{title}} tamamlandı olarak işaretle",
+ "editButton": "Görevi düzenle",
+ "swipeOpen": "Yeniden aç",
+ "swipeDone": "Tamamlandı",
+ "swipeEdit": "Düzenle",
+ "subtaskAdd": "+ Alt görev ekle",
+ "subtaskToggle": "Alt görevleri göster",
+ "subtaskMarkDone": "{{title}} tamamlandı olarak işaretle",
+ "deleteConfirm": "Görev ve tüm alt görevler silinsin mi?",
+ "savedToast": "Görev kaydedildi.",
+ "createdToast": "Görev oluşturuldu.",
+ "deletedToast": "Görev silindi.",
+ "loadError": "Görev yüklenemedi.",
+ "subtaskPrompt": "Alt görev:",
+ "kanbanOpen": "Açık",
+ "kanbanInProgress": "Devam Ediyor",
+ "kanbanDone": "Tamamlandı",
+ "kanbanMoveToInProgress": "Devam ediyor olarak ayarla",
+ "kanbanMoveToDone": "Tamamlandı olarak işaretle",
+ "kanbanMoveToOpen": "Yeniden aç",
+ "recurring": "Yinelenen",
+ "listView": "Liste görünümü",
+ "kanbanView": "Kanban görünümü"
+ },
+ "shopping": {
+ "title": "Alışveriş",
+ "noLists": "Liste yok",
+ "noListsDescription": "+ düğmesiyle bir liste oluşturun.",
+ "emptyList": "Liste boş",
+ "emptyListDescription": "Yukarıdaki giriş alanını kullanarak ürün ekleyin.",
+ "newListPrompt": "Yeni liste adı:",
+ "newListButton": "Yeni liste oluştur",
+ "renameListPrompt": "Yeni liste adı:",
+ "deleteListConfirm": "\"{{name}}\" listesi ve tüm ürünleri silinsin mi?",
+ "deletedListToast": "Liste silindi.",
+ "itemDeletedToast": "\"{{name}}\" kaldırıldı.",
+ "itemsRemovedToast": "{{count}} ürün kaldırıldı.",
+ "clearChecked": "İşaretlileri sil ({{count}})",
+ "itemNamePlaceholder": "Ürün ekle…",
+ "itemQtyPlaceholder": "Miktar",
+ "itemNameLabel": "Ürün adı",
+ "itemQtyLabel": "Miktar",
+ "categoryLabel": "Kategori",
+ "addItemLabel": "Ürün ekle",
+ "renameListLabel": "Listeyi yeniden adlandır",
+ "deleteListLabel": "Listeyi sil",
+ "swipeBack": "Geri al",
+ "swipeCheck": "İşaretle",
+ "swipeDelete": "Sil",
+ "markDoneLabel": "{{name}} işaretle",
+ "markUndoneLabel": "{{name}} işaretini kaldır",
+ "deleteItemLabel": "{{name}} sil",
+ "listsLoadError": "Listeler yüklenemedi.",
+ "itemsLoadError": "Ürünler yüklenemedi.",
+ "catFruitVeg": "Meyve ve Sebze",
+ "catBakery": "Fırın",
+ "catDairy": "Süt Ürünleri",
+ "catMeatFish": "Et ve Balık",
+ "catFrozen": "Dondurulmuş",
+ "catDrinks": "İçecekler",
+ "catHousehold": "Ev Gereçleri",
+ "catDrugstore": "Eczane",
+ "catMisc": "Diğer"
+ },
+ "meals": {
+ "title": "Yemek Planı",
+ "noMealPlanned": "Yemek planlanmadı",
+ "addMeal": "{{type}} ekle",
+ "editMeal": "Yemeği düzenle",
+ "addMealTitle": "Yemek ekle",
+ "deleteMeal": "Yemeği sil",
+ "transferToShoppingList": "Malzemeleri alışveriş listesine ekle",
+ "today": "Bugün",
+ "prevWeek": "Önceki hafta",
+ "nextWeek": "Gelecek hafta",
+ "loadError": "Yemek planı yüklenemedi.",
+ "typeBreakfast": "Kahvaltı",
+ "typeLunch": "Öğle yemeği",
+ "typeDinner": "Akşam yemeği",
+ "typeSnack": "Atıştırmalık",
+ "dayMo": "Pzt",
+ "dayDi": "Sal",
+ "dayMi": "Çar",
+ "dayDo": "Per",
+ "dayFr": "Cum",
+ "daySa": "Cmt",
+ "daySo": "Paz",
+ "dateLabel": "Tarih",
+ "mealTypeLabel": "Öğün",
+ "titleLabel": "Başlık *",
+ "titlePlaceholder": "örn. Mercimek çorbası",
+ "notesLabel": "Notlar",
+ "notesPlaceholder": "İsteğe bağlı…",
+ "ingredientsLabel": "Malzemeler",
+ "addIngredient": "Malzeme ekle",
+ "ingredientNamePlaceholder": "Malzeme",
+ "ingredientQtyPlaceholder": "Miktar",
+ "removeIngredient": "Malzemeyi kaldır",
+ "transferLabel": "Malzemeleri alışveriş listesine aktar",
+ "transferNow": "Şimdi aktar",
+ "noShoppingLists": "Alışveriş listesi yok",
+ "transferSuccess": "{{count}} malzeme aktarıldı",
+ "transferSuccessPlural": "{{count}} malzeme aktarıldı",
+ "transferAlreadyDone": "Tüm malzemeler zaten aktarıldı",
+ "ingredientCount": "{{count}} malzeme",
+ "ingredientCountPlural": "{{count}} malzeme",
+ "titleRequired": "Başlık zorunludur",
+ "loadingIndicator": "Yükleniyor…",
+ "recipeUrlLabel": "Tarif bağlantısı (isteğe bağlı)",
+ "recipeUrlPlaceholder": "https://…",
+ "openRecipe": "Tarifi aç"
+ },
+ "calendar": {
+ "title": "Takvim",
+ "newEvent": "Yeni Etkinlik",
+ "editEvent": "Etkinliği Düzenle",
+ "addEvent": "Etkinlik ekle",
+ "deleteEvent": "Etkinliği sil",
+ "noEvents": "Seçilen dönemde etkinlik yok.",
+ "today": "Bugün",
+ "back": "Geri",
+ "forward": "İleri",
+ "viewMonth": "Ay",
+ "viewWeek": "Hafta",
+ "viewDay": "Gün",
+ "viewAgenda": "Ajanda",
+ "allDay": "Tüm gün",
+ "allDayShort": "tüm gün",
+ "moreEvents": "+{{count}} daha",
+ "weekNumberLabel": "H{{week}} · {{month}} {{year}}",
+ "agendaFrom": "{{date}} tarihinden itibaren",
+ "titleLabel": "Başlık *",
+ "titlePlaceholder": "örn. Diş hekimi",
+ "allDayToggle": "Tüm gün",
+ "startDateLabel": "Başlangıç tarihi",
+ "startTimeLabel": "Başlangıç saati",
+ "endDateLabel": "Bitiş tarihi",
+ "endTimeLabel": "Bitiş saati",
+ "fromLabel": "Başlangıç",
+ "toLabel": "Bitiş",
+ "locationLabel": "Konum",
+ "locationPlaceholder": "İsteğe bağlı",
+ "assignedLabel": "Atanan",
+ "assignedNobody": "- Kimse -",
+ "colorLabel": "Renk {{color}}",
+ "descriptionLabel": "Açıklama",
+ "descriptionPlaceholder": "İsteğe bağlı…",
+ "popupEdit": "Düzenle",
+ "deleteConfirm": "\"{{title}}\" silinsin mi?",
+ "createdToast": "Etkinlik oluşturuldu",
+ "savedToast": "Etkinlik kaydedildi",
+ "deletedToast": "Etkinlik silindi",
+ "loadError": "Etkinlikler yüklenemedi.",
+ "saveError": "Kaydetme hatası",
+ "deleteError": "Silme hatası",
+ "titleRequired": "Başlık zorunludur",
+ "monthJanuary": "Ocak",
+ "monthFebruary": "Şubat",
+ "monthMarch": "Mart",
+ "monthApril": "Nisan",
+ "monthMay": "Mayıs",
+ "monthJune": "Haziran",
+ "monthJuly": "Temmuz",
+ "monthAugust": "Ağustos",
+ "monthSeptember": "Eylül",
+ "monthOctober": "Ekim",
+ "monthNovember": "Kasım",
+ "monthDecember": "Aralık",
+ "dayShortSunday": "Paz",
+ "dayShortMonday": "Pzt",
+ "dayShortTuesday": "Sal",
+ "dayShortWednesday": "Çar",
+ "dayShortThursday": "Per",
+ "dayShortFriday": "Cum",
+ "dayShortSaturday": "Cmt",
+ "dayLongSunday": "Pazar",
+ "dayLongMonday": "Pazartesi",
+ "dayLongTuesday": "Salı",
+ "dayLongWednesday": "Çarşamba",
+ "dayLongThursday": "Perşembe",
+ "dayLongFriday": "Cuma",
+ "dayLongSaturday": "Cumartesi",
+ "timeSuffix": ""
+ },
+ "notes": {
+ "title": "Notlar",
+ "newNote": "Yeni Not",
+ "editNote": "Notu Düzenle",
+ "addNoteLabel": "Yeni Not",
+ "searchPlaceholder": "Notlarda ara…",
+ "emptyTitle": "Henüz not yok",
+ "emptyDescription": "+ düğmesiyle yeni bir not oluşturun.",
+ "noResultsTitle": "Sonuç yok",
+ "noResultsDescription": "Hiçbir not \"{{query}}\" içermiyor.",
+ "titleLabel": "Başlık (isteğe bağlı)",
+ "titlePlaceholder": "Başlıksız",
+ "contentLabel": "İçerik",
+ "contentMarkdownHint": "(Markdown biçimlendirmesi desteklenir)",
+ "contentPlaceholder": "Not girin…",
+ "colorLabel": "Renk",
+ "pinnedLabel": "Sabitle (gösterge panelinde görünür)",
+ "pinAction": "Sabitle",
+ "unpinAction": "Sabitlemeyi kaldır",
+ "deleteLabel": "Notu sil",
+ "deleteConfirm": "Bu not silinsin mi?",
+ "createdToast": "Not oluşturuldu",
+ "savedToast": "Not kaydedildi",
+ "deletedToast": "Not silindi",
+ "loadError": "Notlar yüklenemedi.",
+ "formatBold": "Kalın (Ctrl+B)",
+ "formatItalic": "İtalik (Ctrl+I)",
+ "formatUnderline": "Altı çizili (Ctrl+U)",
+ "formatStrikethrough": "Üstü çizili",
+ "formatHeading": "Başlık",
+ "formatList": "Madde listesi",
+ "formatOrderedList": "Numaralı liste",
+ "formatChecklist": "Kontrol listesi",
+ "formatLink": "Bağlantı",
+ "formatCode": "Kod",
+ "formatQuote": "Alıntı",
+ "formatDivider": "Ayırıcı"
+ },
+ "contacts": {
+ "title": "Kişiler",
+ "newContact": "Yeni Kişi",
+ "editContact": "Kişiyi Düzenle",
+ "addButton": "Yeni",
+ "newContactLabel": "Yeni Kişi",
+ "searchPlaceholder": "Ad, telefon veya e-posta ara…",
+ "importButton": "İçe aktar",
+ "importLabel": "vCard'dan kişi içe aktar",
+ "importTooltip": "vCard içe aktar",
+ "emptyTitle": "Henüz kişi yok",
+ "emptyDescription": "+ düğmesiyle yeni kişiler ekleyin.",
+ "filterAll": "Tümü",
+ "nameLabel": "Ad *",
+ "namePlaceholder": "Tam ad",
+ "categoryLabel": "Kategori",
+ "phoneLabel": "Telefon",
+ "phonePlaceholder": "+90 …",
+ "emailLabel": "E-posta",
+ "emailPlaceholder": "ad@ornek.com.tr",
+ "addressLabel": "Adres",
+ "addressPlaceholder": "Sokak, Posta Kodu Şehir",
+ "notesLabel": "Notlar",
+ "notesPlaceholder": "İsteğe bağlı…",
+ "callLabel": "Ara",
+ "emailActionLabel": "E-posta",
+ "mapsLabel": "Haritada aç",
+ "exportLabel": "vCard olarak dışa aktar",
+ "exportTooltip": "vCard dışa aktar",
+ "deleteLabel": "Kişiyi sil",
+ "deleteConfirm": "Bu kişi silinsin mi?",
+ "deletePersonConfirm": "\"{{name}}\" silinsin mi?",
+ "savedToast": "Kişi kaydedildi",
+ "updatedToast": "Kişi güncellendi",
+ "deletedToast": "Kişi silindi",
+ "importedToast": "{{name}} içe aktarıldı.",
+ "importError": "İçe aktarma başarısız: {{error}}",
+ "vcardNoName": "vCard'da ad yok.",
+ "catDoctor": "Doktor",
+ "catSchool": "Okul/Kreş",
+ "catAuthority": "Resmi Kurum",
+ "catInsurance": "Sigorta",
+ "catCraftsman": "Usta",
+ "catEmergency": "Acil",
+ "catMisc": "Diğer",
+ "categoryDoctor": "Doktor",
+ "categorySchool": "Okul/Kreş",
+ "categoryAuthority": "Resmi Kurum",
+ "categoryInsurance": "Sigorta",
+ "categoryCraftsman": "Usta",
+ "categoryEmergency": "Acil",
+ "categoryOther": "Diğer"
+ },
+ "budget": {
+ "title": "Bütçe",
+ "newEntry": "Yeni Kayıt",
+ "editEntry": "Kaydı Düzenle",
+ "addEntryLabel": "Kayıt ekle",
+ "newEntryFabLabel": "Yeni Kayıt",
+ "currentMonth": "Bu Ay",
+ "prevMonth": "Önceki ay",
+ "nextMonth": "Sonraki ay",
+ "income": "Gelir",
+ "expenses": "Gider",
+ "balance": "Bakiye",
+ "byCategory": "Kategoriye göre",
+ "transactions": "İşlemler",
+ "emptyTitle": "Bu ay kayıt yok",
+ "emptyDescription": "+ düğmesiyle bütçe kayıtları ekleyin.",
+ "csvExport": "CSV",
+ "typeExpense": "Gider",
+ "typeIncome": "Gelir",
+ "titleLabel": "Başlık *",
+ "titlePlaceholder": "örn. Market alışverişi",
+ "amountLabel": "Tutar *",
+ "amountPlaceholder": "0,00",
+ "categoryLabel": "Kategori",
+ "dateLabel": "Tarih *",
+ "recurringLabel": "Yinelenen",
+ "deleteLabel": "Kaydı sil",
+ "deleteConfirm": "Bu kayıt silinsin mi?",
+ "deletePersonConfirm": "\"{{title}}\" silinsin mi?",
+ "addedToast": "Kayıt eklendi",
+ "savedToast": "Kayıt kaydedildi",
+ "deletedToast": "Kayıt silindi",
+ "loadError": "Bütçe yüklenemedi.",
+ "trendNeutral": "- {{month}} ile aynı",
+ "validAmountRequired": "Lütfen geçerli bir tutar girin",
+ "dateRequired": "Tarih zorunludur",
+ "catFood": "Market",
+ "catRent": "Kira",
+ "catInsurance": "Sigorta",
+ "catMobility": "Ulaşım",
+ "catLeisure": "Eğlence",
+ "catClothing": "Giyim",
+ "catHealth": "Sağlık",
+ "catEducation": "Eğitim",
+ "catMisc": "Diğer",
+ "loadingIndicator": "Yükleniyor…"
+ },
+ "settings": {
+ "title": "Ayarlar",
+ "tabGeneral": "Genel",
+ "tabMeals": "Yemekler",
+ "tabBudget": "Bütçe",
+ "tabShopping": "Alışveriş",
+ "tabCalendar": "Takvim",
+ "tabAccount": "Hesap",
+ "tabsAriaLabel": "Ayar bölümleri",
+ "sectionDesign": "Görünüm",
+ "sectionShopping": "Alışveriş",
+ "shoppingCategoriesLabel": "Alışveriş Kategorileri",
+ "shoppingCategoriesHint": "Kategorileri ekleyin, yeniden adlandırın, silin veya sıralayın.",
+ "shoppingCategoryPlaceholder": "Yeni kategori…",
+ "shoppingCategoryRenameHint": "Yeniden adlandırmak için tıklayın",
+ "shoppingCategoryRenamePrompt": "Yeni kategori adı:",
+ "shoppingCategoryMoveUp": "Kategoriyi yukarı taşı",
+ "shoppingCategoryMoveDown": "Kategoriyi aşağı taşı",
+ "shoppingCategoryDelete": "Kategoriyi sil",
+ "shoppingCategoryDeleteConfirm": "\"{{name}}\" kategorisi silinsin mi? Mevcut ürünler bir sonraki kategoriye taşınacak.",
+ "shoppingCategoryAdded": "Kategori eklendi.",
+ "shoppingCategoryRenamed": "Kategori yeniden adlandırıldı.",
+ "shoppingCategoryDeleted": "Kategori silindi.",
+ "sectionAccount": "Hesabım",
+ "sectionCalendarSync": "Takvim Senkronizasyonu",
+ "sectionFamily": "Aile Üyeleri",
+ "cardAppearance": "Görünüm",
+ "themeSystem": "Sistem",
+ "themeSysLabel": "Sistem ayarını kullan",
+ "themeLight": "Açık",
+ "themeLightLabel": "Açık mod",
+ "themeDark": "Koyu",
+ "themeDarkLabel": "Koyu mod",
+ "changePassword": "Şifre değiştir",
+ "currentPasswordLabel": "Mevcut şifre",
+ "newPasswordLabel": "Yeni şifre",
+ "confirmPasswordLabel": "Yeni şifreyi onayla",
+ "savePassword": "Şifreyi kaydet",
+ "passwordMismatch": "Şifreler eşleşmiyor.",
+ "passwordSavedToast": "Şifre başarıyla değiştirildi.",
+ "googleCalendar": "Google Takvim",
+ "appleCalendar": "Apple Takvim (iCloud)",
+ "syncNow": "Şimdi senkronize et",
+ "disconnect": "Bağlantıyı kes",
+ "connectGoogle": "Google ile bağlan",
+ "connected": "Bağlı",
+ "connectedLastSync": "Bağlı · Son: {{date}}",
+ "notConnected": "Bağlı değil",
+ "notConfigured": "Yapılandırılmadı (.env değişkenleri eksik)",
+ "configured": "Yapılandırıldı (via .env)",
+ "configuredLastSync": "Yapılandırıldı (via .env) · Son: {{date}}",
+ "syncSuccess": "{{provider}} senkronize edildi.",
+ "disconnectedToast": "{{provider}} bağlantısı kesildi.",
+ "googleOnlyAdmin": "Yalnızca yönetici Google Takvim'i bağlayabilir.",
+ "appleOnlyAdmin": "Yalnızca yönetici Apple Takvim'i bağlayabilir.",
+ "caldavUrlLabel": "CalDAV Sunucu URL'si",
+ "caldavUrlPlaceholder": "https://caldav.icloud.com",
+ "appleIdLabel": "Apple Kimliği (e-posta)",
+ "applePasswordLabel": "Uygulamaya özel şifre",
+ "applePasswordHint": "appleid.apple.com → Güvenlik adresinden şifre oluşturun.",
+ "appleConnectBtn": "Bağlan ve test et",
+ "appleConnecting": "Bağlanıyor…",
+ "appleConnectedToast": "Apple Takvim bağlandı.",
+ "syncSuccessGoogle": "Google ile takvim senkronizasyonu başarıyla bağlandı.",
+ "syncSuccessApple": "Apple ile takvim senkronizasyonu başarıyla bağlandı.",
+ "syncErrorGoogle": "Google'a bağlanılamadı. Lütfen tekrar deneyin.",
+ "syncErrorApple": "Apple'a bağlanılamadı. Lütfen tekrar deneyin.",
+ "addMember": "+ Üye ekle",
+ "newMemberTitle": "Yeni Aile Üyesi",
+ "usernameLabel": "Kullanıcı adı",
+ "displayNameLabel": "Görünen ad",
+ "memberPasswordLabel": "Şifre",
+ "colorLabel": "Renk",
+ "roleLabel": "Rol",
+ "roleMember": "Üye",
+ "roleAdmin": "Yönetici",
+ "createMember": "Oluştur",
+ "cancelAddMember": "İptal",
+ "memberAddedToast": "{{name}} eklendi.",
+ "deleteMemberConfirm": "{{name}} silinsin mi?",
+ "memberDeletedToast": "{{name}} silindi.",
+ "deleteMemberLabel": "Sil",
+ "logout": "Çıkış yap",
+ "synchronizing": "Senkronize ediliyor…",
+ "googleDisconnectConfirm": "Google Takvim bağlantısı kesilsin mi?",
+ "appleDisconnectConfirm": "Apple Takvim bağlantısı kesilsin mi?",
+ "localeSystem": "Sistem",
+ "localeLabel": "Dil",
+ "languageTitle": "Dil",
+ "sectionMeals": "Yemek Planı",
+ "mealTypesLabel": "Görünür öğünler",
+ "mealTypesHint": "Yalnızca seçili öğün türleri yemek planında gösterilir.",
+ "mealTypesSaved": "Yemek planı ayarları kaydedildi.",
+ "mealTypesMinOne": "En az bir öğün türü aktif olmalıdır.",
+ "sectionBudget": "Bütçe",
+ "currencyLabel": "Para birimi",
+ "currencyHint": "Bütçe bölümünde kullanılan para birimini belirler.",
+ "currencySaved": "Para birimi kaydedildi."
+ },
+ "login": {
+ "tagline": "Aile planlaması. Güvenli. Gizlilik dostu. Açık kaynak.",
+ "usernameLabel": "Kullanıcı adı",
+ "usernamePlaceholder": "kullaniciadi",
+ "passwordLabel": "Şifre",
+ "passwordPlaceholder": "••••••••",
+ "loginButton": "Giriş yap",
+ "loggingIn": "Giriş yapılıyor…",
+ "tooManyAttempts": "Çok fazla deneme. Lütfen bir süre bekleyin.",
+ "invalidCredentials": "Geçersiz kimlik bilgileri."
+ },
+ "install": {
+ "title": "Oikos'u Yükle",
+ "subtitle": "Ana ekrana ekle",
+ "iosTip1": "Dokunun ",
+ "iosTip2": " → \"Ana Ekrana Ekle\"",
+ "installButton": "Yükle",
+ "dismissLabel": "Kapat"
+ },
+ "modal": {
+ "closeLabel": "Kapat"
+ },
+ "rrule": {
+ "freqNone": "Tekrar yok",
+ "freqDaily": "Günlük",
+ "freqWeekly": "Haftalık",
+ "freqMonthly": "Aylık",
+ "dayMo": "Pt",
+ "dayTu": "Sa",
+ "dayWe": "Ça",
+ "dayTh": "Pe",
+ "dayFr": "Cu",
+ "daySa": "Ct",
+ "daySu": "Pz",
+ "labelRepeat": "Tekrar",
+ "labelEvery": "Her",
+ "labelOnDays": "Bu günlerde",
+ "labelUntil": "Bitiş tarihi (isteğe bağlı)",
+ "unitDay": "gün",
+ "unitDays": "gün",
+ "unitWeek": "hafta",
+ "unitWeeks": "hafta",
+ "unitMonth": "ay",
+ "unitMonths": "ay"
+ }
+}
diff --git a/public/locales/zh.json b/public/locales/zh.json
new file mode 100644
index 0000000..313aefc
--- /dev/null
+++ b/public/locales/zh.json
@@ -0,0 +1,591 @@
+{
+ "common": {
+ "save": "保存",
+ "cancel": "取消",
+ "delete": "删除",
+ "edit": "编辑",
+ "close": "关闭",
+ "create": "创建",
+ "add": "添加",
+ "back": "返回",
+ "next": "下一步",
+ "loading": "加载中…",
+ "saving": "保存中…",
+ "required": "此字段为必填项。",
+ "error": "错误",
+ "allFieldsRequired": "请填写所有字段。",
+ "today": "今天",
+ "tomorrow": "明天",
+ "skipToContent": "跳转到内容",
+ "reload": "重新加载",
+ "errorOccurred": "出现了问题。",
+ "unexpectedError": "发生了意外错误。",
+ "errorGeneric": "发生了错误。",
+ "updateAvailable": "有更新 - 重新加载页面以获取最新版本。",
+ "titleRequired": "标题为必填项",
+ "nameRequired": "姓名为必填项",
+ "contentRequired": "内容为必填项",
+ "all": "全部",
+ "unknownError": "未知错误",
+ "confirm": "确认",
+ "undo": "撤销"
+ },
+ "nav": {
+ "dashboard": "概览",
+ "tasks": "任务",
+ "calendar": "日历",
+ "meals": "饮食",
+ "shopping": "购物",
+ "notes": "便签",
+ "contacts": "联系人",
+ "budget": "预算",
+ "settings": "设置",
+ "main": "主导航",
+ "navigation": "导航",
+ "quickActions": "快捷操作"
+ },
+ "dashboard": {
+ "title": "概览",
+ "greetingMorning": "早上好,{{name}}",
+ "greetingDay": "下午好,{{name}}",
+ "greetingEvening": "晚上好,{{name}}",
+ "allDone": "全部完成",
+ "noEvents": "暂无日程",
+ "noPinnedNotes": "暂无置顶便签",
+ "todayMeals": "今日饮食",
+ "allLink": "全部",
+ "weekLink": "本周",
+ "urgentTasksChip": "{{count}} 个紧急任务",
+ "urgentTasksChipPlural": "{{count}} 个紧急任务",
+ "eventsChip": "今天 {{count}} 个日程",
+ "eventsChipPlural": "今天 {{count}} 个日程",
+ "todayMealChip": "今天:{{title}}",
+ "loadError": "仪表板加载失败。",
+ "weatherRefresh": "刷新天气",
+ "weatherRefreshTitle": "刷新",
+ "weatherUpdated": "天气已更新",
+ "weatherFeelsLike": "体感 {{temp}}° · {{humidity}}% · 风速 {{wind}} km/h",
+ "fabTaskLabel": "添加任务",
+ "fabCalendarLabel": "添加日程",
+ "fabShoppingLabel": "添加购物",
+ "fabNoteLabel": "添加便签",
+ "fabTask": "任务",
+ "fabCalendar": "日程",
+ "fabShopping": "购物",
+ "fabNote": "便签",
+ "overdue": "已逾期",
+ "dueSoon": "今天到期",
+ "dueTomorrow": "明天到期",
+ "allDay": "全天",
+ "shoppingMore": "+{{count}} 更多"
+ },
+ "tasks": {
+ "title": "任务",
+ "newTask": "新建任务",
+ "editTask": "编辑任务",
+ "emptyTitle": "暂无任务 - 全部完成了?",
+ "emptyDescription": "通过 + 按钮创建新任务。",
+ "titleLabel": "标题 *",
+ "titlePlaceholder": "需要做什么?",
+ "descriptionLabel": "备注",
+ "descriptionPlaceholder": "可选详情…",
+ "priorityLabel": "优先级",
+ "categoryLabel": "分类",
+ "dueDateLabel": "截止日期",
+ "dueTimeLabel": "时间",
+ "assignedLabel": "分配给",
+ "assignedNobody": "- 无人 -",
+ "statusLabel": "状态",
+ "priorityUrgent": "紧急",
+ "priorityHigh": "高",
+ "priorityMedium": "中",
+ "priorityLow": "低",
+ "priorityNone": "无",
+ "statusOpen": "待处理",
+ "statusInProgress": "进行中",
+ "statusDone": "已完成",
+ "categoryHousehold": "家务",
+ "categorySchool": "学校",
+ "categoryShopping": "购物",
+ "categoryRepair": "维修",
+ "categoryHealth": "健康",
+ "categoryFinance": "财务",
+ "categoryLeisure": "休闲",
+ "categoryMisc": "其他",
+ "overdue": "已逾期",
+ "overdueDay": "逾期 {{count}} 天",
+ "dueToday": "今天到期",
+ "dueTomorrow": "明天到期",
+ "groupOverdue": "已逾期",
+ "groupToday": "今天",
+ "groupThisWeek": "本周",
+ "groupNextWeek": "下周",
+ "groupLater": "以后",
+ "groupNoDate": "无日期",
+ "markDone": "将 {{title}} 标记为完成",
+ "editButton": "编辑任务",
+ "swipeOpen": "打开",
+ "swipeDone": "完成",
+ "swipeEdit": "编辑",
+ "subtaskAdd": "+ 添加子任务",
+ "subtaskToggle": "显示子任务",
+ "subtaskMarkDone": "将 {{title}} 标记为完成",
+ "deleteConfirm": "删除任务及所有子任务?",
+ "savedToast": "任务已保存。",
+ "createdToast": "任务已创建。",
+ "deletedToast": "任务已删除。",
+ "loadError": "任务加载失败。",
+ "subtaskPrompt": "子任务:",
+ "kanbanOpen": "待处理",
+ "kanbanInProgress": "进行中",
+ "kanbanDone": "已完成",
+ "kanbanMoveToInProgress": "移至进行中",
+ "kanbanMoveToDone": "标记为完成",
+ "kanbanMoveToOpen": "重新打开",
+ "recurring": "重复",
+ "listView": "列表视图",
+ "kanbanView": "看板视图"
+ },
+ "shopping": {
+ "title": "购物",
+ "noLists": "暂无清单",
+ "noListsDescription": "通过 + 按钮创建清单。",
+ "emptyList": "清单为空",
+ "emptyListDescription": "通过上方输入框添加商品。",
+ "newListPrompt": "新清单名称:",
+ "newListButton": "新建清单",
+ "renameListPrompt": "新清单名称:",
+ "deleteListConfirm": "删除清单 \"{{name}}\" 及所有商品?",
+ "deletedListToast": "清单已删除。",
+ "itemDeletedToast": "\"{{name}}\" 已移除。",
+ "itemsRemovedToast": "已移除 {{count}} 件商品。",
+ "clearChecked": "删除已勾选 ({{count}})",
+ "itemNamePlaceholder": "添加商品…",
+ "itemQtyPlaceholder": "数量",
+ "itemNameLabel": "商品名称",
+ "itemQtyLabel": "数量",
+ "categoryLabel": "分类",
+ "addItemLabel": "添加商品",
+ "renameListLabel": "重命名清单",
+ "deleteListLabel": "删除清单",
+ "swipeBack": "返回",
+ "swipeCheck": "勾选",
+ "swipeDelete": "删除",
+ "markDoneLabel": "勾选 {{name}}",
+ "markUndoneLabel": "取消勾选 {{name}}",
+ "deleteItemLabel": "删除 {{name}}",
+ "listsLoadError": "清单加载失败。",
+ "itemsLoadError": "商品加载失败。",
+ "catFruitVeg": "蔬果",
+ "catBakery": "烘焙",
+ "catDairy": "乳制品",
+ "catMeatFish": "肉类&海鲜",
+ "catFrozen": "冷冻食品",
+ "catDrinks": "饮料",
+ "catHousehold": "家居",
+ "catDrugstore": "日化",
+ "catMisc": "其他"
+ },
+ "meals": {
+ "title": "饮食计划",
+ "noMealPlanned": "暂无饮食计划",
+ "addMeal": "添加{{type}}",
+ "editMeal": "编辑餐食",
+ "addMealTitle": "添加餐食",
+ "deleteMeal": "删除餐食",
+ "transferToShoppingList": "食材添加到购物清单",
+ "today": "今天",
+ "prevWeek": "上一周",
+ "nextWeek": "下一周",
+ "loadError": "饮食计划加载失败。",
+ "typeBreakfast": "早餐",
+ "typeLunch": "午餐",
+ "typeDinner": "晚餐",
+ "typeSnack": "零食",
+ "dayMo": "一",
+ "dayDi": "二",
+ "dayMi": "三",
+ "dayDo": "四",
+ "dayFr": "五",
+ "daySa": "六",
+ "daySo": "日",
+ "dateLabel": "日期",
+ "mealTypeLabel": "餐食",
+ "titleLabel": "标题 *",
+ "titlePlaceholder": "例如:红烧肉",
+ "notesLabel": "备注",
+ "notesPlaceholder": "可选…",
+ "ingredientsLabel": "食材",
+ "addIngredient": "添加食材",
+ "ingredientNamePlaceholder": "食材",
+ "ingredientQtyPlaceholder": "数量",
+ "removeIngredient": "移除食材",
+ "transferLabel": "将食材添加到购物清单",
+ "transferNow": "立即添加",
+ "noShoppingLists": "暂无购物清单",
+ "transferSuccess": "已添加 {{count}} 种食材",
+ "transferSuccessPlural": "已添加 {{count}} 种食材",
+ "transferAlreadyDone": "所有食材已添加",
+ "ingredientCount": "{{count}} 种食材",
+ "ingredientCountPlural": "{{count}} 种食材",
+ "titleRequired": "标题为必填项",
+ "loadingIndicator": "加载中…",
+ "recipeUrlLabel": "食谱链接(可选)",
+ "recipeUrlPlaceholder": "https://…",
+ "openRecipe": "打开食谱"
+ },
+ "calendar": {
+ "title": "日历",
+ "newEvent": "新建日程",
+ "editEvent": "编辑日程",
+ "addEvent": "添加日程",
+ "deleteEvent": "删除日程",
+ "noEvents": "所选时间段内暂无日程。",
+ "today": "今天",
+ "back": "返回",
+ "forward": "前进",
+ "viewMonth": "月",
+ "viewWeek": "周",
+ "viewDay": "日",
+ "viewAgenda": "议程",
+ "allDay": "全天",
+ "allDayShort": "全天",
+ "moreEvents": "+{{count}} 更多",
+ "weekNumberLabel": "第 {{week}} 周 · {{month}} {{year}}",
+ "agendaFrom": "从 {{date}} 起",
+ "titleLabel": "标题 *",
+ "titlePlaceholder": "例如:牙医预约",
+ "allDayToggle": "全天",
+ "startDateLabel": "开始日期",
+ "startTimeLabel": "开始时间",
+ "endDateLabel": "结束日期",
+ "endTimeLabel": "结束时间",
+ "fromLabel": "从",
+ "toLabel": "至",
+ "locationLabel": "地点",
+ "locationPlaceholder": "可选",
+ "assignedLabel": "分配给",
+ "assignedNobody": "- 无人 -",
+ "colorLabel": "颜色 {{color}}",
+ "descriptionLabel": "描述",
+ "descriptionPlaceholder": "可选…",
+ "popupEdit": "编辑",
+ "deleteConfirm": "确定删除 \"{{title}}\"?",
+ "createdToast": "日程已创建",
+ "savedToast": "日程已保存",
+ "deletedToast": "日程已删除",
+ "loadError": "日程加载失败。",
+ "saveError": "保存失败",
+ "deleteError": "删除失败",
+ "titleRequired": "标题为必填项",
+ "monthJanuary": "一月",
+ "monthFebruary": "二月",
+ "monthMarch": "三月",
+ "monthApril": "四月",
+ "monthMay": "五月",
+ "monthJune": "六月",
+ "monthJuly": "七月",
+ "monthAugust": "八月",
+ "monthSeptember": "九月",
+ "monthOctober": "十月",
+ "monthNovember": "十一月",
+ "monthDecember": "十二月",
+ "dayShortSunday": "日",
+ "dayShortMonday": "一",
+ "dayShortTuesday": "二",
+ "dayShortWednesday": "三",
+ "dayShortThursday": "四",
+ "dayShortFriday": "五",
+ "dayShortSaturday": "六",
+ "dayLongSunday": "星期日",
+ "dayLongMonday": "星期一",
+ "dayLongTuesday": "星期二",
+ "dayLongWednesday": "星期三",
+ "dayLongThursday": "星期四",
+ "dayLongFriday": "星期五",
+ "dayLongSaturday": "星期六",
+ "timeSuffix": ""
+ },
+ "notes": {
+ "title": "便签板",
+ "newNote": "新建便签",
+ "editNote": "编辑便签",
+ "addNoteLabel": "新建便签",
+ "searchPlaceholder": "搜索便签…",
+ "emptyTitle": "暂无便签",
+ "emptyDescription": "通过 + 按钮创建新便签。",
+ "noResultsTitle": "无结果",
+ "noResultsDescription": "没有包含 \"{{query}}\" 的便签。",
+ "titleLabel": "标题(可选)",
+ "titlePlaceholder": "无标题",
+ "contentLabel": "内容",
+ "contentMarkdownHint": "(支持 Markdown 格式)",
+ "contentPlaceholder": "输入便签…",
+ "colorLabel": "颜色",
+ "pinnedLabel": "置顶(显示在仪表板)",
+ "pinAction": "置顶",
+ "unpinAction": "取消置顶",
+ "deleteLabel": "删除便签",
+ "deleteConfirm": "确定删除便签?",
+ "createdToast": "便签已创建",
+ "savedToast": "便签已保存",
+ "deletedToast": "便签已删除",
+ "loadError": "便签加载失败。",
+ "formatBold": "加粗 (Ctrl+B)",
+ "formatItalic": "斜体 (Ctrl+I)",
+ "formatUnderline": "下划线 (Ctrl+U)",
+ "formatStrikethrough": "删除线",
+ "formatHeading": "标题",
+ "formatList": "列表",
+ "formatOrderedList": "有序列表",
+ "formatChecklist": "待办清单",
+ "formatLink": "链接",
+ "formatCode": "代码",
+ "formatQuote": "引用",
+ "formatDivider": "分隔线"
+ },
+ "contacts": {
+ "title": "联系人",
+ "newContact": "新建联系人",
+ "editContact": "编辑联系人",
+ "addButton": "新建",
+ "newContactLabel": "新建联系人",
+ "searchPlaceholder": "搜索姓名、电话或邮箱…",
+ "importButton": "导入",
+ "importLabel": "从 vCard 导入联系人",
+ "importTooltip": "导入 vCard",
+ "emptyTitle": "暂无联系人",
+ "emptyDescription": "通过 + 按钮添加新联系人。",
+ "filterAll": "全部",
+ "nameLabel": "姓名 *",
+ "namePlaceholder": "全名",
+ "categoryLabel": "分类",
+ "phoneLabel": "电话",
+ "phonePlaceholder": "+86 …",
+ "emailLabel": "邮箱",
+ "emailPlaceholder": "name@example.com",
+ "addressLabel": "地址",
+ "addressPlaceholder": "街道、城市",
+ "notesLabel": "备注",
+ "notesPlaceholder": "可选…",
+ "callLabel": "拨打电话",
+ "emailActionLabel": "发送邮件",
+ "mapsLabel": "在地图中打开",
+ "exportLabel": "导出为 vCard",
+ "exportTooltip": "导出 vCard",
+ "deleteLabel": "删除联系人",
+ "deleteConfirm": "确定删除联系人?",
+ "deletePersonConfirm": "确定删除 \"{{name}}\"?",
+ "savedToast": "联系人已保存",
+ "updatedToast": "联系人已更新",
+ "deletedToast": "联系人已删除",
+ "importedToast": "{{name}} 已导入。",
+ "importError": "导入失败:{{error}}",
+ "vcardNoName": "vCard 不含姓名。",
+ "catDoctor": "医生",
+ "catSchool": "学校/幼儿园",
+ "catAuthority": "政府机构",
+ "catInsurance": "保险",
+ "catCraftsman": "技工",
+ "catEmergency": "紧急联系",
+ "catMisc": "其他",
+ "categoryDoctor": "医生",
+ "categorySchool": "学校/幼儿园",
+ "categoryAuthority": "政府机构",
+ "categoryInsurance": "保险",
+ "categoryCraftsman": "技工",
+ "categoryEmergency": "紧急联系",
+ "categoryOther": "其他"
+ },
+ "budget": {
+ "title": "预算",
+ "newEntry": "新建条目",
+ "editEntry": "编辑条目",
+ "addEntryLabel": "添加条目",
+ "newEntryFabLabel": "新建条目",
+ "currentMonth": "当前",
+ "prevMonth": "上个月",
+ "nextMonth": "下个月",
+ "income": "收入",
+ "expenses": "支出",
+ "balance": "余额",
+ "byCategory": "按分类",
+ "transactions": "交易记录",
+ "emptyTitle": "本月暂无条目",
+ "emptyDescription": "通过 + 按钮添加预算条目。",
+ "csvExport": "CSV",
+ "typeExpense": "支出",
+ "typeIncome": "收入",
+ "titleLabel": "标题 *",
+ "titlePlaceholder": "例如:超市购物",
+ "amountLabel": "金额 *",
+ "amountPlaceholder": "0.00",
+ "categoryLabel": "分类",
+ "dateLabel": "日期 *",
+ "recurringLabel": "重复",
+ "deleteLabel": "删除条目",
+ "deleteConfirm": "确定删除条目?",
+ "deletePersonConfirm": "确定删除 \"{{title}}\"?",
+ "addedToast": "条目已添加",
+ "savedToast": "条目已保存",
+ "deletedToast": "条目已删除",
+ "loadError": "预算加载失败。",
+ "trendNeutral": "- 与 {{month}} 相同",
+ "validAmountRequired": "请输入有效金额",
+ "dateRequired": "日期为必填项",
+ "catFood": "食品",
+ "catRent": "租金",
+ "catInsurance": "保险",
+ "catMobility": "出行",
+ "catLeisure": "休闲",
+ "catClothing": "服装",
+ "catHealth": "健康",
+ "catEducation": "教育",
+ "catMisc": "其他",
+ "loadingIndicator": "加载中…"
+ },
+ "settings": {
+ "title": "设置",
+ "tabGeneral": "常规",
+ "tabMeals": "饮食",
+ "tabBudget": "预算",
+ "tabShopping": "购物",
+ "tabCalendar": "日历",
+ "tabAccount": "账户",
+ "tabsAriaLabel": "设置类别",
+ "sectionDesign": "外观",
+ "sectionShopping": "购物",
+ "shoppingCategoriesLabel": "购物分类",
+ "shoppingCategoriesHint": "添加、重命名、删除或排序分类。",
+ "shoppingCategoryPlaceholder": "新分类…",
+ "shoppingCategoryRenameHint": "点击重命名",
+ "shoppingCategoryRenamePrompt": "新分类名称:",
+ "shoppingCategoryMoveUp": "上移分类",
+ "shoppingCategoryMoveDown": "下移分类",
+ "shoppingCategoryDelete": "删除分类",
+ "shoppingCategoryDeleteConfirm": "删除分类 \"{{name}}\"?现有商品将归入下一分类。",
+ "shoppingCategoryAdded": "分类已添加。",
+ "shoppingCategoryRenamed": "分类已重命名。",
+ "shoppingCategoryDeleted": "分类已删除。",
+ "sectionAccount": "我的账户",
+ "sectionCalendarSync": "日历同步",
+ "sectionFamily": "家庭成员",
+ "cardAppearance": "外观",
+ "themeSystem": "跟随系统",
+ "themeSysLabel": "使用系统设置",
+ "themeLight": "浅色",
+ "themeLightLabel": "浅色主题",
+ "themeDark": "深色",
+ "themeDarkLabel": "深色主题",
+ "changePassword": "修改密码",
+ "currentPasswordLabel": "当前密码",
+ "newPasswordLabel": "新密码",
+ "confirmPasswordLabel": "确认新密码",
+ "savePassword": "保存密码",
+ "passwordMismatch": "两次密码不一致。",
+ "passwordSavedToast": "密码修改成功。",
+ "googleCalendar": "Google 日历",
+ "appleCalendar": "Apple 日历(iCloud)",
+ "syncNow": "立即同步",
+ "disconnect": "断开连接",
+ "connectGoogle": "连接 Google",
+ "connected": "已连接",
+ "connectedLastSync": "已连接 · 上次:{{date}}",
+ "notConnected": "未连接",
+ "notConfigured": "未配置(缺少 .env 变量)",
+ "configured": "已配置(通过 .env)",
+ "configuredLastSync": "已配置(通过 .env) · 上次:{{date}}",
+ "syncSuccess": "{{provider}} 同步成功。",
+ "disconnectedToast": "{{provider}} 已断开。",
+ "googleOnlyAdmin": "只有管理员可以连接 Google 日历。",
+ "appleOnlyAdmin": "只有管理员可以连接 Apple 日历。",
+ "caldavUrlLabel": "CalDAV 服务器 URL",
+ "caldavUrlPlaceholder": "https://caldav.icloud.com",
+ "appleIdLabel": "Apple ID(邮箱)",
+ "applePasswordLabel": "专用 App 密码",
+ "applePasswordHint": "在 appleid.apple.com → 安全 下创建密码。",
+ "appleConnectBtn": "连接并测试",
+ "appleConnecting": "连接中…",
+ "appleConnectedToast": "Apple 日历已连接。",
+ "syncSuccessGoogle": "Google 日历同步连接成功。",
+ "syncSuccessApple": "Apple 日历同步连接成功。",
+ "syncErrorGoogle": "连接 Google 失败,请重试。",
+ "syncErrorApple": "连接 Apple 失败,请重试。",
+ "addMember": "+ 添加成员",
+ "newMemberTitle": "新建家庭成员",
+ "usernameLabel": "用户名",
+ "displayNameLabel": "显示名称",
+ "memberPasswordLabel": "密码",
+ "colorLabel": "颜色",
+ "roleLabel": "角色",
+ "roleMember": "成员",
+ "roleAdmin": "管理员",
+ "createMember": "创建",
+ "cancelAddMember": "取消",
+ "memberAddedToast": "{{name}} 已添加。",
+ "deleteMemberConfirm": "确定删除 {{name}}?",
+ "memberDeletedToast": "{{name}} 已删除。",
+ "deleteMemberLabel": "删除",
+ "logout": "退出登录",
+ "synchronizing": "同步中…",
+ "googleDisconnectConfirm": "断开 Google 日历连接?",
+ "appleDisconnectConfirm": "断开 Apple 日历连接?",
+ "localeSystem": "跟随系统",
+ "localeLabel": "语言",
+ "languageTitle": "语言",
+ "sectionMeals": "饮食计划",
+ "mealTypesLabel": "显示的餐食类型",
+ "mealTypesHint": "饮食计划中只显示已选择的餐食类型。",
+ "mealTypesSaved": "饮食计划设置已保存。",
+ "mealTypesMinOne": "至少需要一种餐食类型处于启用状态。",
+ "sectionBudget": "预算",
+ "currencyLabel": "货币",
+ "currencyHint": "设置整个预算区域使用的货币。",
+ "currencySaved": "货币已保存。"
+ },
+ "login": {
+ "tagline": "家庭规划。安全。注重隐私。开源。",
+ "usernameLabel": "用户名",
+ "usernamePlaceholder": "用户名",
+ "passwordLabel": "密码",
+ "passwordPlaceholder": "••••••••",
+ "loginButton": "登录",
+ "loggingIn": "登录中…",
+ "tooManyAttempts": "尝试次数过多,请稍后再试。",
+ "invalidCredentials": "用户名或密码错误。"
+ },
+ "install": {
+ "title": "安装 Oikos",
+ "subtitle": "添加到应用",
+ "iosTip1": "点击 ",
+ "iosTip2": " → \"添加到主屏幕\"",
+ "installButton": "安装",
+ "dismissLabel": "关闭"
+ },
+ "modal": {
+ "closeLabel": "关闭"
+ },
+ "rrule": {
+ "freqNone": "不重复",
+ "freqDaily": "每天",
+ "freqWeekly": "每周",
+ "freqMonthly": "每月",
+ "dayMo": "一",
+ "dayTu": "二",
+ "dayWe": "三",
+ "dayTh": "四",
+ "dayFr": "五",
+ "daySa": "六",
+ "daySu": "日",
+ "labelRepeat": "重复",
+ "labelEvery": "每",
+ "labelOnDays": "在这些天",
+ "labelUntil": "结束日期(可选)",
+ "unitDay": "天",
+ "unitDays": "天",
+ "unitWeek": "周",
+ "unitWeeks": "周",
+ "unitMonth": "个月",
+ "unitMonths": "个月"
+ }
+}
diff --git a/public/pages/settings.js b/public/pages/settings.js
index ffe0924..1fc65e5 100644
--- a/public/pages/settings.js
+++ b/public/pages/settings.js
@@ -10,7 +10,7 @@ import { t, formatDate, formatTime } from '/i18n.js';
import { esc } from '/utils/html.js';
import '/components/oikos-locale-picker.js';
-const SUPPORTED_CURRENCIES = ['AUD', 'CAD', 'CHF', 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HUF', 'JPY', 'NOK', 'PLN', 'SEK', 'USD'];
+const SUPPORTED_CURRENCIES = ['AUD', 'CAD', 'CHF', 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HUF', 'JPY', 'NOK', 'PLN', 'RUB', 'SEK', 'TRY', 'USD'];
const SETTINGS_TAB_KEY = 'oikos:settings:tab';
function buildCurrencyOptions(selected) {
diff --git a/server/routes/preferences.js b/server/routes/preferences.js
index fa51620..c72eb58 100644
--- a/server/routes/preferences.js
+++ b/server/routes/preferences.js
@@ -15,7 +15,7 @@ const router = express.Router();
const VALID_MEAL_TYPES = ['breakfast', 'lunch', 'dinner', 'snack'];
const DEFAULT_MEAL_TYPES = VALID_MEAL_TYPES.join(',');
-const VALID_CURRENCIES = ['EUR', 'USD', 'GBP', 'SEK', 'NOK', 'DKK', 'CHF', 'CNY', 'PLN', 'CZK', 'HUF', 'JPY', 'AUD', 'CAD'];
+const VALID_CURRENCIES = ['EUR', 'USD', 'GBP', 'SEK', 'NOK', 'DKK', 'CHF', 'CNY', 'PLN', 'CZK', 'HUF', 'JPY', 'AUD', 'CAD', 'TRY', 'RUB'];
const DEFAULT_CURRENCY = 'EUR';
// --------------------------------------------------------