feat(i18n): add Italian localization

Add complete Italian translation (497 keys) based on PR #7 by
@albanobattistella. Fixed filename from "it. json" to "it.json" and
registered Italian in SUPPORTED_LOCALES and the locale picker component.

Co-Authored-By: albanobattistella <34811668+albanobattistella@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-04-03 15:35:08 +02:00
parent 678c896862
commit b1ee4439be
5 changed files with 550 additions and 2 deletions
+6
View File
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.5.8] - 2026-04-03
### Added
- Add Italian (Italiano) localization — full translation of all 497 i18n keys (thanks @albanobattistella, PR #7)
- Add Italian as selectable language in Settings locale picker
## [0.5.7] - 2026-04-03 ## [0.5.7] - 2026-04-03
### Fixed ### Fixed
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "oikos", "name": "oikos",
"version": "0.5.7", "version": "0.5.8",
"description": "Selbstgehosteter Familienplaner — Kalender, Aufgaben, Einkauf, Essensplan, Budget und mehr. Privat, offen, ohne Abo.", "description": "Selbstgehosteter Familienplaner — Kalender, Aufgaben, Einkauf, Essensplan, Budget und mehr. Privat, offen, ohne Abo.",
"main": "server/index.js", "main": "server/index.js",
"engines": { "engines": {
+1
View File
@@ -10,6 +10,7 @@ import { t, setLocale, getLocale, getSupportedLocales } from '/i18n.js';
const LOCALE_LABELS = { const LOCALE_LABELS = {
de: 'Deutsch', de: 'Deutsch',
en: 'English', en: 'English',
it: 'Italiano',
}; };
class OikosLocalePicker extends HTMLElement { class OikosLocalePicker extends HTMLElement {
+1 -1
View File
@@ -5,7 +5,7 @@
* Dependencies: none (vanilla JS, Fetch API, Intl API) * Dependencies: none (vanilla JS, Fetch API, Intl API)
*/ */
const SUPPORTED_LOCALES = ['de', 'en']; const SUPPORTED_LOCALES = ['de', 'en', 'it'];
const DEFAULT_LOCALE = 'de'; const DEFAULT_LOCALE = 'de';
const STORAGE_KEY = 'oikos-locale'; const STORAGE_KEY = 'oikos-locale';
+541
View File
@@ -0,0 +1,541 @@
{
"common": {
"save": "Salva",
"cancel": "Annulla",
"delete": "Elimina",
"edit": "Modifica",
"close": "Chiudi",
"create": "Crea",
"add": "Aggiungi",
"back": "Indietro",
"next": "Avanti",
"loading": "Caricamento…",
"saving": "Salvataggio…",
"required": "Questo campo è obbligatorio.",
"error": "Errore",
"allFieldsRequired": "Compila tutti i campi.",
"today": "Oggi",
"tomorrow": "Domani",
"skipToContent": "Salta al contenuto",
"reload": "Ricarica",
"errorOccurred": "Si è verificato un errore.",
"unexpectedError": "Si è verificato un errore imprevisto.",
"errorGeneric": "Si è verificato un errore.",
"updateAvailable": "Aggiornamento disponibile — ricarica la pagina per ottenere l'ultima versione.",
"titleRequired": "Il titolo è obbligatorio",
"nameRequired": "Il nome è obbligatorio",
"contentRequired": "Il contenuto è obbligatorio",
"all": "Tutto",
"unknownError": "Errore sconosciuto"
},
"nav": {
"dashboard": "Panoramica",
"tasks": "Compiti",
"calendar": "Calendario",
"meals": "Pasti",
"shopping": "Spesa",
"notes": "Bacheca",
"contacts": "Contatti",
"budget": "Bilancio",
"settings": "Impostazioni",
"main": "Navigazione principale",
"navigation": "Navigazione",
"quickActions": "Azioni rapide"
},
"dashboard": {
"title": "Panoramica",
"greetingMorning": "Buongiorno, {{name}}",
"greetingDay": "Buon pomeriggio, {{name}}",
"greetingEvening": "Buonasera, {{name}}",
"allDone": "Tutto fatto",
"noEvents": "Nessun evento",
"noPinnedNotes": "Nessuna nota fissata",
"todayMeals": "Pasti di oggi",
"allLink": "Tutto",
"weekLink": "Settimana",
"urgentTasksChip": "{{count}} compito urgente",
"urgentTasksChipPlural": "{{count}} compiti urgenti",
"eventsChip": "{{count}} evento oggi",
"eventsChipPlural": "{{count}} eventi oggi",
"todayMealChip": "Oggi: {{title}}",
"loadError": "Impossibile caricare completamente la dashboard.",
"weatherRefresh": "Aggiorna meteo",
"weatherRefreshTitle": "Aggiorna",
"weatherFeelsLike": "Percepiti {{temp}}° · {{humidity}}% · Vento {{wind}} km/h",
"fabTaskLabel": "Aggiungi compito",
"fabCalendarLabel": "Aggiungi evento",
"fabShoppingLabel": "Aggiungi spesa",
"fabNoteLabel": "Aggiungi nota",
"fabTask": "Compito",
"fabCalendar": "Evento",
"fabShopping": "Spesa",
"fabNote": "Nota",
"overdue": "Scaduto",
"dueSoon": "Scade oggi",
"dueTomorrow": "Scade domani",
"allDay": "Tutto il giorno"
},
"tasks": {
"title": "Compiti",
"newTask": "Nuovo compito",
"editTask": "Modifica compito",
"emptyTitle": "Nessun compito — tutto fatto?",
"emptyDescription": "Crea nuovi compiti con il pulsante +.",
"titleLabel": "Titolo *",
"titlePlaceholder": "Cosa bisogna fare?",
"descriptionLabel": "Nota",
"descriptionPlaceholder": "Dettagli opzionali…",
"priorityLabel": "Priorità",
"categoryLabel": "Categoria",
"dueDateLabel": "Data di scadenza",
"dueTimeLabel": "Ora",
"assignedLabel": "Assegnato a",
"assignedNobody": "— Nessuno —",
"statusLabel": "Stato",
"priorityUrgent": "Urgente",
"priorityHigh": "Alta",
"priorityMedium": "Media",
"priorityLow": "Bassa",
"statusOpen": "Aperto",
"statusInProgress": "In corso",
"statusDone": "Completato",
"categoryHousehold": "Casa",
"categorySchool": "Scuola",
"categoryShopping": "Spesa",
"categoryRepair": "Riparazioni",
"categoryHealth": "Salute",
"categoryFinance": "Finanze",
"categoryLeisure": "Tempo libero",
"categoryMisc": "Varie",
"overdue": "Scaduto",
"overdueDay": "Scaduto da {{count}}g",
"dueToday": "Scade oggi",
"dueTomorrow": "Scade domani",
"groupOverdue": "Scaduti",
"groupToday": "Oggi",
"groupThisWeek": "Questa settimana",
"groupNextWeek": "Prossima settimana",
"groupLater": "Più avanti",
"groupNoDate": "Senza data",
"markDone": "Segna {{title}} come completato",
"editButton": "Modifica compito",
"swipeOpen": "Riapri",
"swipeDone": "Completato",
"swipeEdit": "Modifica",
"subtaskAdd": "+ Aggiungi sotto-compito",
"subtaskToggle": "Mostra sotto-compiti",
"subtaskMarkDone": "Segna {{title}} come completato",
"deleteConfirm": "Eliminare il compito e tutti i sotto-compiti?",
"savedToast": "Compito salvato.",
"createdToast": "Compito creato.",
"deletedToast": "Compito eliminato.",
"loadError": "Impossibile caricare il compito.",
"subtaskPrompt": "Sotto-compito:",
"kanbanOpen": "Da fare",
"kanbanInProgress": "In corso",
"kanbanDone": "Completato",
"recurring": "Ricorrente",
"listView": "Vista elenco",
"kanbanView": "Vista Kanban"
},
"shopping": {
"title": "Spesa",
"noLists": "Nessuna lista",
"noListsDescription": "Crea una lista con il pulsante +.",
"emptyList": "La lista è vuota",
"emptyListDescription": "Aggiungi articoli usando il campo sopra.",
"newListPrompt": "Nome per la nuova lista:",
"newListButton": "Crea nuova lista",
"renameListPrompt": "Nuovo nome lista:",
"deleteListConfirm": "Eliminare la lista \"{{name}}\" e tutti gli articoli?",
"deletedListToast": "Lista eliminata.",
"itemsRemovedToast": "{{count}} articoli rimossi.",
"clearChecked": "Rimuovi selezionati ({{count}})",
"itemNamePlaceholder": "Aggiungi articolo…",
"itemQtyPlaceholder": "Quantità",
"itemNameLabel": "Nome articolo",
"itemQtyLabel": "Quantità",
"categoryLabel": "Categoria",
"addItemLabel": "Aggiungi articolo",
"renameListLabel": "Rinomina lista",
"deleteListLabel": "Elimina lista",
"swipeBack": "Annulla",
"swipeCheck": "Spunta",
"swipeDelete": "Elimina",
"markDoneLabel": "Spunta {{name}}",
"markUndoneLabel": "Togli spunta a {{name}}",
"deleteItemLabel": "Elimina {{name}}",
"listsLoadError": "Impossibile caricare le liste.",
"itemsLoadError": "Impossibile caricare gli articoli.",
"catFruitVeg": "Frutta e Verdura",
"catBakery": "Panetteria",
"catDairy": "Latticini",
"catMeatFish": "Carne e Pesce",
"catFrozen": "Surgelati",
"catDrinks": "Bevande",
"catHousehold": "Casa",
"catDrugstore": "Drogheria",
"catMisc": "Varie"
},
"meals": {
"title": "Piano pasti",
"noMealPlanned": "Nessun pasto pianificato",
"addMeal": "Aggiungi {{type}}",
"editMeal": "Modifica pasto",
"addMealTitle": "Aggiungi pasto",
"deleteMeal": "Elimina pasto",
"transferToShoppingList": "Aggiungi ingredienti alla lista della spesa",
"today": "Oggi",
"prevWeek": "Settimana precedente",
"nextWeek": "Settimana successiva",
"loadError": "Impossibile caricare il piano pasti.",
"typeBreakfast": "Colazione",
"typeLunch": "Pranzo",
"typeDinner": "Cena",
"typeSnack": "Spuntino",
"dayMo": "Lun",
"dayDi": "Mar",
"dayMi": "Mer",
"dayDo": "Gio",
"dayFr": "Ven",
"daySa": "Sab",
"daySo": "Dom",
"dateLabel": "Data",
"mealTypeLabel": "Pasto",
"titleLabel": "Titolo *",
"titlePlaceholder": "es. Spaghetti Bolognese",
"notesLabel": "Note",
"notesPlaceholder": "Opzionale…",
"ingredientsLabel": "Ingredienti",
"addIngredient": "Aggiungi ingrediente",
"ingredientNamePlaceholder": "Ingrediente",
"ingredientQtyPlaceholder": "Quantità",
"removeIngredient": "Rimuovi ingrediente",
"transferLabel": "Trasferisci ingredienti alla lista della spesa",
"transferNow": "Trasferisci ora",
"noShoppingLists": "Nessuna lista della spesa disponibile",
"transferSuccess": "{{count}} ingrediente trasferito",
"transferSuccessPlural": "{{count}} ingredienti trasferiti",
"transferAlreadyDone": "Tutti gli ingredienti già trasferiti",
"ingredientCount": "{{count}} ingrediente",
"ingredientCountPlural": "{{count}} ingredienti",
"titleRequired": "Il titolo è obbligatorio",
"loadingIndicator": "Caricamento…"
},
"calendar": {
"title": "Calendario",
"newEvent": "Nuovo evento",
"editEvent": "Modifica evento",
"addEvent": "Aggiungi evento",
"deleteEvent": "Elimina evento",
"noEvents": "Nessun evento nel periodo selezionato.",
"today": "Oggi",
"back": "Indietro",
"forward": "Avanti",
"viewMonth": "Mese",
"viewWeek": "Settimana",
"viewDay": "Giorno",
"viewAgenda": "Agenda",
"allDay": "Tutto il giorno",
"allDayShort": "tutto il giorno",
"moreEvents": "+{{count}} altri",
"weekNumberLabel": "S{{week}} · {{month}} {{year}}",
"agendaFrom": "Dal {{date}}",
"titleLabel": "Titolo *",
"titlePlaceholder": "es. Dentista",
"allDayToggle": "Tutto il giorno",
"startDateLabel": "Data inizio",
"startTimeLabel": "Ora inizio",
"endDateLabel": "Data fine",
"endTimeLabel": "Ora fine",
"fromLabel": "Dal",
"toLabel": "Al",
"locationLabel": "Luogo",
"locationPlaceholder": "Opzionale",
"assignedLabel": "Assegnato a",
"assignedNobody": "— Nessuno —",
"colorLabel": "Colore",
"descriptionLabel": "Descrizione",
"descriptionPlaceholder": "Opzionale…",
"popupEdit": "Modifica",
"deleteConfirm": "Eliminare davvero \"{{title}}\"?",
"createdToast": "Evento creato",
"savedToast": "Evento salvato",
"deletedToast": "Evento eliminato",
"loadError": "Impossibile caricare gli eventi.",
"saveError": "Errore durante il salvataggio",
"deleteError": "Errore durante l'eliminazione",
"titleRequired": "Il titolo è obbligatorio",
"monthJanuary": "Gennaio",
"monthFebruary": "Febbraio",
"monthMarch": "Marzo",
"monthApril": "Aprile",
"monthMay": "Maggio",
"monthJune": "Giugno",
"monthJuly": "Luglio",
"monthAugust": "Agosto",
"monthSeptember": "Settembre",
"monthOctober": "Ottobre",
"monthNovember": "Novembre",
"monthDecember": "Dicembre",
"dayShortSunday": "Dom",
"dayShortMonday": "Lun",
"dayShortTuesday": "Mar",
"dayShortWednesday": "Mer",
"dayShortThursday": "Gio",
"dayShortFriday": "Ven",
"dayShortSaturday": "Sab",
"dayLongSunday": "Domenica",
"dayLongMonday": "Lunedì",
"dayLongTuesday": "Martedì",
"dayLongWednesday": "Mercoledì",
"dayLongThursday": "Giovedì",
"dayLongFriday": "Venerdì",
"dayLongSaturday": "Sabato",
"timeSuffix": "",
"colorLabel": "Colore {{color}}"
},
"notes": {
"title": "Bacheca",
"newNote": "Nuova nota",
"editNote": "Modifica nota",
"addNoteLabel": "Nuova nota",
"searchPlaceholder": "Cerca note…",
"emptyTitle": "Ancora nessuna nota",
"emptyDescription": "Crea una nuova nota con il pulsante +.",
"noResultsTitle": "Nessun risultato",
"noResultsDescription": "Nessuna nota contiene \"{{query}}\".",
"titleLabel": "Titolo (opzionale)",
"titlePlaceholder": "Senza titolo",
"contentLabel": "Contenuto",
"contentMarkdownHint": "(Formattazione Markdown supportata)",
"contentPlaceholder": "Inserisci nota…",
"colorLabel": "Colore",
"pinnedLabel": "Fissa (appare sulla dashboard)",
"pinAction": "Fissa",
"unpinAction": "Sfissa",
"deleteLabel": "Elimina nota",
"deleteConfirm": "Eliminare davvero questa nota?",
"createdToast": "Nota creata",
"savedToast": "Nota salvata",
"deletedToast": "Nota eliminata",
"loadError": "Impossibile caricare le note.",
"formatBold": "Grassetto (Ctrl+B)",
"formatItalic": "Corsivo (Ctrl+I)",
"formatUnderline": "Sottolineato (Ctrl+U)",
"formatStrikethrough": "Barrato",
"formatHeading": "Titolo",
"formatList": "Elenco puntato",
"formatOrderedList": "Elenco numerato",
"formatChecklist": "Lista di controllo",
"formatLink": "Link",
"formatCode": "Codice",
"formatQuote": "Citazione",
"formatDivider": "Divisore"
},
"contacts": {
"title": "Contatti",
"newContact": "Nuovo contatto",
"editContact": "Modifica contatto",
"addButton": "Nuovo",
"newContactLabel": "Nuovo contatto",
"searchPlaceholder": "Cerca per nome, telefono o email…",
"importButton": "Importa",
"importLabel": "Importa contatto da vCard",
"importTooltip": "Importa vCard",
"emptyTitle": "Ancora nessun contatto",
"emptyDescription": "Aggiungi nuovi contatti con il pulsante +.",
"filterAll": "Tutti",
"nameLabel": "Nome *",
"namePlaceholder": "Nome completo",
"categoryLabel": "Categoria",
"phoneLabel": "Telefono",
"phonePlaceholder": "+39 …",
"emailLabel": "Email",
"emailPlaceholder": "nome@example.com",
"addressLabel": "Indirizzo",
"addressPlaceholder": "Via, CAP Città",
"notesLabel": "Note",
"notesPlaceholder": "Opzionale…",
"callLabel": "Chiama",
"emailActionLabel": "Email",
"mapsLabel": "Apri in Maps",
"exportLabel": "Esporta come vCard",
"exportTooltip": "Esporta vCard",
"deleteLabel": "Elimina contatto",
"deleteConfirm": "Eliminare davvero questo contatto?",
"deletePersonConfirm": "Eliminare davvero \"{{name}}\"?",
"savedToast": "Contatto salvato",
"updatedToast": "Contatto aggiornato",
"deletedToast": "Contatto eliminato",
"importedToast": "{{name}} importato.",
"importError": "Importazione fallita: {{error}}",
"vcardNoName": "La vCard non contiene un nome.",
"catDoctor": "Medico",
"catSchool": "Scuola/Asilo",
"catAuthority": "Pubblica amministrazione",
"catInsurance": "Assicurazione",
"catCraftsman": "Artigiano",
"catEmergency": "Emergenza",
"catMisc": "Varie",
"categoryDoctor": "Medico",
"categorySchool": "Scuola/Asilo",
"categoryAuthority": "Pubblica amministrazione",
"categoryInsurance": "Assicurazione",
"categoryCraftsman": "Artigiano",
"categoryEmergency": "Emergenza",
"categoryOther": "Altro"
},
"budget": {
"title": "Bilancio",
"newEntry": "Nuova voce",
"editEntry": "Modifica voce",
"addEntryLabel": "Aggiungi voce",
"newEntryFabLabel": "Nuova voce",
"currentMonth": "Corrente",
"prevMonth": "Mese precedente",
"nextMonth": "Mese successivo",
"income": "Entrate",
"expenses": "Uscite",
"balance": "Saldo",
"byCategory": "Per categoria",
"transactions": "Transazioni",
"emptyTitle": "Nessuna voce questo mese",
"emptyDescription": "Aggiungi voci di bilancio con il pulsante +.",
"csvExport": "CSV",
"typeExpense": "Uscita",
"typeIncome": "Entrata",
"titleLabel": "Titolo *",
"titlePlaceholder": "es. Supermercato",
"amountLabel": "Importo (€) *",
"amountPlaceholder": "0,00",
"categoryLabel": "Categoria",
"dateLabel": "Data *",
"recurringLabel": "Ricorrente",
"deleteLabel": "Elimina voce",
"deleteConfirm": "Eliminare davvero questa voce?",
"deletePersonConfirm": "Eliminare davvero \"{{title}}\"?",
"addedToast": "Voce aggiunta",
"savedToast": "Voce salvata",
"deletedToast": "Voce eliminata",
"loadError": "Impossibile caricare il bilancio.",
"trendNeutral": "— come {{month}}",
"validAmountRequired": "Inserisci un importo valido",
"dateRequired": "La data è obbligatoria",
"catFood": "Spesa alimentare",
"catRent": "Affitto",
"catInsurance": "Assicurazione",
"catMobility": "Trasporti",
"catLeisure": "Tempo libero",
"catClothing": "Abbigliamento",
"catHealth": "Salute",
"catEducation": "Istruzione",
"catMisc": "Varie",
"loadingIndicator": "Caricamento…"
},
"settings": {
"title": "Impostazioni",
"sectionDesign": "Aspetto",
"sectionAccount": "Il mio account",
"sectionCalendarSync": "Sincronizzazione calendario",
"sectionFamily": "Membri della famiglia",
"cardAppearance": "Visualizzazione",
"themeSystem": "Sistema",
"themeSysLabel": "Usa impostazione di sistema",
"themeLight": "Chiaro",
"themeLightLabel": "Modalità chiara",
"themeDark": "Scuro",
"themeDarkLabel": "Modalità scura",
"changePassword": "Cambia password",
"currentPasswordLabel": "Password attuale",
"newPasswordLabel": "Nuova password",
"confirmPasswordLabel": "Conferma nuova password",
"savePassword": "Salva password",
"passwordMismatch": "Le password non corrispondono.",
"passwordSavedToast": "Password modificata con successo.",
"googleCalendar": "Google Calendar",
"appleCalendar": "Apple Calendar (iCloud)",
"syncNow": "Sincronizza ora",
"disconnect": "Disconnetti",
"connectGoogle": "Connetti con Google",
"connected": "Connesso",
"connectedLastSync": "Connesso · Ultima: {{date}}",
"notConnected": "Non connesso",
"notConfigured": "Non configurato (variabili .env mancanti)",
"configured": "Configurato (tramite .env)",
"configuredLastSync": "Configurato (tramite .env) · Ultima: {{date}}",
"syncSuccess": "{{provider}} sincronizzato.",
"disconnectedToast": "{{provider}} disconnesso.",
"googleOnlyAdmin": "Solo l'admin può connettere Google Calendar.",
"appleOnlyAdmin": "Solo l'admin può connettere Apple Calendar.",
"caldavUrlLabel": "URL server CalDAV",
"caldavUrlPlaceholder": "https://caldav.icloud.com",
"appleIdLabel": "Apple ID (email)",
"applePasswordLabel": "Password app-specifica",
"applePasswordHint": "Crea la password su <strong>appleid.apple.com → Sicurezza</strong>.",
"appleConnectBtn": "Connetti e testa",
"appleConnecting": "Connessione…",
"appleConnectedToast": "Apple Calendar connesso.",
"syncSuccessGoogle": "Sincronizzazione calendario con Google connessa con successo.",
"syncSuccessApple": "Sincronizzazione calendario con Apple connessa con successo.",
"syncErrorGoogle": "Connessione a Google fallita. Riprova.",
"syncErrorApple": "Connessione ad Apple fallita. Riprova.",
"addMember": "+ Aggiungi membro",
"newMemberTitle": "Nuovo membro familiare",
"usernameLabel": "Nome utente",
"displayNameLabel": "Nome visualizzato",
"memberPasswordLabel": "Password",
"colorLabel": "Colore",
"roleLabel": "Ruolo",
"roleMember": "Membro",
"roleAdmin": "Admin",
"createMember": "Crea",
"cancelAddMember": "Annulla",
"memberAddedToast": "{{name}} aggiunto.",
"deleteMemberConfirm": "Eliminare davvero {{name}}?",
"memberDeletedToast": "{{name}} eliminato.",
"deleteMemberLabel": "Elimina",
"logout": "Esci",
"synchronizing": "Sincronizzazione…",
"googleDisconnectConfirm": "Disconnettere Google Calendar?",
"appleDisconnectConfirm": "Disconnettere Apple Calendar?",
"localeSystem": "Sistema",
"localeLabel": "Lingua",
"languageTitle": "Lingua"
},
"login": {
"tagline": "Pianificazione familiare. Sicura. Rispettosa della privacy. Open source.",
"usernameLabel": "Nome utente",
"usernamePlaceholder": "nomeutente",
"passwordLabel": "Password",
"passwordPlaceholder": "••••••••",
"loginButton": "Accedi",
"loggingIn": "Accesso in corso…",
"tooManyAttempts": "Troppi tentativi. Attendi un momento.",
"invalidCredentials": "Credenziali non valide."
},
"install": {
"title": "Installa Oikos",
"subtitle": "Aggiungi alla schermata home",
"iosTip1": "Tocca ",
"iosTip2": " → \"Aggiungi a Home\"",
"installButton": "Installa",
"dismissLabel": "Chiudi"
},
"modal": {
"closeLabel": "Chiudi"
}
}