feat(localization): expand language support and enhance UI for user settings
All checks were successful
Deploy to production / deploy (push) Successful in 3m0s

- Added support for additional UI locales including Cebuano and Spanish, improving accessibility for a broader user base.
- Updated language selection components in the AppHeader and SettingsWidget to reflect new language options, enhancing user experience.
- Enhanced localization of various UI elements across components, ensuring consistent language representation and improved user engagement.
- Implemented logic to synchronize user language preferences with backend settings, providing a seamless experience when changing languages.
This commit is contained in:
Torsten Schulz (local)
2026-04-02 07:54:44 +02:00
parent ac5d436a36
commit 6d9d69dc10
72 changed files with 1792 additions and 343 deletions

View File

@@ -430,7 +430,9 @@
"selectStockType": "Seleccionar tipo de almacén",
"costPerUnit": "Coste por unidad",
"buycost": "Coste",
"sellincome": "Ingresos"
"sellincome": "Ingresos",
"buyPartialError": "Error al comprar parte de la capacidad del almacén.",
"sellError": "Error al vender la capacidad del almacén."
},
"vehicles": {
"cargo_cart": "Carro de carga",

View File

@@ -1,6 +1,12 @@
{
"friends": {
"kicker": "Comunidad",
"intro": "Amistades, solicitudes abiertas y contactos en curso en un solo lugar.",
"title": "Amigos",
"stats": {
"existing": "Activos",
"open": "Abiertos"
},
"tabs": {
"existing": "Actuales",
"rejected": "Rechazadas",

View File

@@ -41,6 +41,59 @@
"message": {
"close": "Cerrar"
},
"appShell": {
"header": {
"tagline": "Plataforma comunitaria",
"beta": "Beta",
"backend": "Backend",
"daemon": "Daemon",
"language": "Idioma"
},
"footer": {
"systemLabel": "Sistema",
"noOpenDialogs": "No hay diálogos abiertos",
"activeWindows": "{count} ventanas activas",
"systemReady": "Sistema listo",
"systemStatusUnavailable": "El estado del sistema no está disponible directamente en esta vista en este momento."
}
},
"widgets": {
"dashboard": {
"dragHandle": "Mover",
"loading": "Cargando..."
},
"birthdays": {
"today": "¡Hoy!",
"tomorrow": "Mañana",
"turningAge": "(cumple {age})",
"inDays": "{count} días",
"empty": "No hay cumpleaños visibles de amigos"
},
"upcoming": {
"today": "Hoy",
"tomorrow": "Mañana",
"timeAt": "{time} h",
"allDay": "Todo el día",
"empty": "No hay citas próximas"
},
"appointments": {
"title": "📅 Citas",
"loading": "Cargando citas...",
"empty": "No hay citas próximas",
"loadError": "No se pudieron cargar las citas"
},
"list": {
"noEntries": "No hay entradas",
"entriesCount": "({count} entradas)",
"fieldsCount": "({count} campos)"
},
"news": {
"emptyValue": "—"
},
"falukant": {
"emptyValue": "—"
}
},
"gender": {
"male": "Masculino",
"female": "Femenino",

View File

@@ -1,5 +1,54 @@
{
"home": {
"dashboard": {
"kicker": "Tu área",
"title": "¡Bienvenido de nuevo!",
"subtitle": "Tu punto de entrada personal a la comunidad, las citas, Falukant y la actividad en curso.",
"edit": "Editar panel",
"addWidget": "+ Añadir widget ...",
"addAgain": "Añadir de nuevo",
"done": "Listo",
"sectionTitle": "Tu resumen",
"sectionIntro": "Los widgets se pueden mover y ajustar en el modo de edición.",
"widgetTitlePlaceholder": "Título",
"removeWidget": "Eliminar widget",
"remove": "Eliminar",
"empty": "Aún no hay widgets. Haz clic en “Editar panel” y luego en “+ Añadir widget”.",
"defaultAppointmentsWidget": "Citas",
"loadError": "No se pudo cargar el panel.",
"saveError": "No se pudo guardar el panel.",
"widgetLabels": {
"appointments": "Citas",
"falukant": "Falukant",
"news": "Noticias",
"birthdays": "Cumpleaños",
"upcoming": "Próximas citas",
"calendar": "Calendario"
},
"overview": {
"activeWidgetsLabel": "Widgets activos",
"activeWidgetsText": "Tu panel es modular y se puede reorganizar en cualquier momento.",
"availableModulesLabel": "Módulos disponibles",
"availableModulesText": "Puedes combinar módulos de comunidad, calendario, noticias y Falukant.",
"editModeLabel": "Modo de edición",
"editModeActive": "Activo",
"editModeInactive": "Desactivado",
"editModeActiveText": "Ahora mismo se pueden añadir y ajustar widgets.",
"editModeInactiveText": "El contenido sigue siendo claro y fácil de leer."
}
},
"vocabLanding": {
"eyebrow": "Aprende idiomas en línea",
"title": "El entrenador de vocabulario en YourPart combina aprendizaje, cursos y ejercicios en una sola plataforma.",
"lead": "Trabaja con lecciones interactivas, amplía tu vocabulario y usa contenido estructurado para un flujo de aprendizaje motivador directamente en el navegador.",
"cta": "Empezar gratis",
"feature1Title": "Cursos interactivos",
"feature1Text": "Cursos, lecciones y ejercicios ayudan a construir nuevas competencias lingüísticas de forma sistemática.",
"feature2Title": "Orientado a la práctica",
"feature2Text": "Vocabulario, gramática y repaso se adaptan a una rutina de aprendizaje cotidiana.",
"feature3Title": "Parte de una comunidad",
"feature3Text": "El área de idiomas está integrada en una plataforma comunitaria más amplia con blogs, foro y chat."
},
"betaNoticeLabel": "Aviso beta:",
"betaNoticeText": "YourPart está en desarrollo activo. Algunas funciones pueden estar incompletas, pueden faltar contenidos y puede haber cambios.",
"nologin": {

View File

@@ -2,6 +2,8 @@
"personal": {
"calendar": {
"title": "Calendario",
"kicker": "Planificación",
"intro": "Citas, cumpleaños y tus propias entradas en una vista estructurada.",
"today": "Hoy",
"newEntry": "Nueva entrada",
"editEntry": "Editar entrada",

View File

@@ -57,7 +57,8 @@
"language": {
"de": "Alemán",
"en": "Inglés",
"ceb": "Bisaya"
"ceb": "Bisaya",
"es": "Español"
},
"eyecolor": {
"blue": "Azul",

View File

@@ -1,6 +1,11 @@
{
"socialnetwork": {
"usersearch": {
"kicker": "Búsqueda en la comunidad",
"intro": "Encuentra contactos adecuados en la comunidad por nombre, edad y género.",
"ageSeparator": "hasta",
"resultsCount": "{count} resultados",
"openProfile": "Abrir perfil",
"title": "Búsqueda de usuarios",
"username": "Nombre de usuario",
"age_from": "Edad desde",
@@ -120,7 +125,8 @@
"hideInput": "Ocultar nueva entrada",
"imageUpload": "Imagen",
"submit": "Enviar entrada",
"noEntries": "No se han encontrado entradas"
"noEntries": "No se han encontrado entradas",
"entryImageAlt": "Imagen de la entrada del libro de visitas"
},
"interestedInGender": "Interesado/a en",
"hasChildren": "Tiene hijos",
@@ -147,6 +153,8 @@
"weight": "Peso"
},
"gallery": {
"kicker": "Imágenes y carpetas",
"intro": "Organiza tu propio contenido, controla su visibilidad y ordénalo en carpetas.",
"title": "Galería",
"folders": "Carpetas",
"create_folder": "Crear carpeta",
@@ -189,15 +197,22 @@
},
"show_image_dialog": {
"title": "Imagen"
}
},
"imagePreviewAlt": "Vista previa de la imagen",
"imageLoadingAlt": "Cargando imagen"
},
"guestbook": {
"kicker": "Libro de visitas",
"intro": "Mensajes, comentarios y pequeños vistazos desde tu red.",
"title": "Libro de visitas",
"prevPage": "Atrás",
"nextPage": "Siguiente",
"page": "Página"
},
"diary": {
"kicker": "Entradas personales",
"intro": "Pensamientos, notas y pequeñas actualizaciones en una vista tranquila y personal.",
"placeholder": "Escribe tu entrada del diario...",
"title": "Diario",
"noEntries": "Aún no has escrito ninguna entrada en el diario.",
"newEntry": "Nueva entrada",
@@ -213,6 +228,16 @@
"page": "Página"
},
"forum": {
"kicker": "Foro de la comunidad",
"intro": "Temas, debates y nuevas publicaciones en un lugar estructurado.",
"createTitle": "Redactar un nuevo tema",
"createIntro": "Primero pon el título, luego escribe la publicación y después publícala directamente.",
"cancelCreation": "Cancelar",
"creationHint": "El título y el contenido deben estar completos.",
"communityFallback": "Comunidad",
"topicIntro": "Debates, respuestas y nuevas publicaciones en una vista de lectura enfocada.",
"topicCreated": "Tema creado correctamente.",
"topicCreateError": "Error al crear el tema",
"title": "Forum",
"showNewTopic": "Crear nuevo tema",
"hideNewTopic": "Cancelar creación",
@@ -277,9 +302,32 @@
"videoUploadHint": "Sube aquí vídeos para tu área erótica desbloqueada y completa título y descripción directamente durante la subida.",
"videoDescription": "Descripción",
"videoFile": "Archivo de vídeo",
"videoFormats": "MP4, WEBM, OGG, MOV",
"myVideos": "Mis vídeos",
"sharedVideos": "Vídeos compartidos",
"foreignVideosIntro": "Vídeos compartidos del área para adultos.",
"foreignVideosOnlyHint": "Aquí solo ves vídeos que han sido compartidos contigo para el área para adultos.",
"sharedVideosIntro": "Vídeos visibles de áreas para adultos compartidas.",
"noSharedVideos": "Ahora mismo no hay vídeos compartidos disponibles para ti.",
"libraryTitle": "Biblioteca",
"libraryIntro": "Tus subidas, permisos y reportes en un solo lugar.",
"libraryEmptyHint": "Crea a la izquierda tu primer vídeo y luego administraciónalo aquí en la biblioteca.",
"latestUpload": "Última subida",
"visibleVideos": "Vídeos visibles",
"moderationCases": "Casos de moderación",
"notesTitle": "Notas",
"friendsVisibilityHint": "Los amigos solo ven el contenido si son mayores de edad y están autorizados para el área para adultos.",
"selectedUsersVisibilityHint": "Las personas autorizadas de forma específica también deben ser mayores de edad y estar autorizadas.",
"selectedUsersPlaceholder": "anna, bert, clara",
"imagePreviewAlt": "Vista previa de la imagen",
"imageLoadingAlt": "Cargando imagen",
"untitled": "Sin título",
"noUploadYet": "Aún no hay subida",
"closeEditing": "Cerrar edición",
"editVisibility": "Editar permisos",
"noVideos": "Todavía no has subido vídeos eróticos.",
"reportAction": "Denunciar",
"reportHint": "Usa {action} directamente en el elemento correspondiente si el contenido debe revisarse.",
"reportNote": "Nota breve para moderación",
"submitReport": "Enviar denuncia",
"reportSubmitted": "La denuncia fue enviada.",
@@ -323,6 +371,36 @@
"vocab": {
"title": "Entrenador de vocabulario",
"description": "Crea idiomas (o suscríbete) y compártelos con tus amigos.",
"heroEyebrow": "Aprendizaje de idiomas",
"summaryTotalLabel": "Idiomas en total",
"summaryTotalIntro": "Todas las áreas de idioma activas donde usas o administras contenido.",
"summaryOwnedLabel": "Áreas propias",
"summaryOwnedIntro": "Aquí creas por tu cuenta contenido, capítulos y material de aprendizaje.",
"summarySubscribedLabel": "Suscrito",
"summarySubscribedIntro": "Estas áreas están pensadas más para aprender y avanzar que para administrar.",
"taskCreateEyebrow": "Inicio rápido",
"taskCreateTitle": "Crear nuevo idioma",
"taskCreateIntro": "La mejor entrada si quieres estructurar y mantener el contenido tú mismo.",
"taskContinueEyebrow": "Seguir aprendiendo",
"taskContinueTitle": "Abrir cursos y capítulos",
"taskContinueIntro": "Entra directamente en rutas de aprendizaje ya existentes y sigue con los cursos disponibles.",
"ownedSectionTitle": "Idiomas propios",
"ownedSectionIntro": "Acceso directo a edición, capítulos y gestión de cursos.",
"ownedHint": "Administrar y mantener contenido",
"ownedEmpty": "Todavía no hay áreas de idioma propias.",
"subscribedSectionTitle": "Idiomas suscritos",
"subscribedSectionIntro": "Ideal para volver rápido al aprendizaje sin carga administrativa.",
"subscribedHint": "Aprender, practicar y ver el progreso",
"subscribedEmpty": "No hay idiomas suscritos disponibles.",
"languageHeroEyebrow": "Idioma",
"languageHeroIntro": "Capítulos, búsqueda y compartición de este idioma en un solo lugar.",
"newLanguageHeroEyebrow": "Entrenador de vocabulario",
"newLanguageHeroIntro": "Crea un nuevo idioma, genera un código para compartir y pasa directamente a la edición.",
"newLanguageNameHint": "Basta con un nombre corto y claro para empezar.",
"newLanguageNameValidation": "El nombre debe tener al menos 2 caracteres.",
"subscribeHeroEyebrow": "Entrenador de vocabulario",
"chapterHeroEyebrow": "Entrenador de vocabulario",
"chapterHeroIntro": "Explora el contenido del capítulo, mantén el vocabulario y pasa directamente a la práctica.",
"newLanguage": "Nuevo idioma",
"newLanguageTitle": "Crear nuevo idioma",
"languageName": "Nombre del idioma",
@@ -568,7 +646,91 @@
"languageAssistantPatternHint": "Concéntrate especialmente en este patrón",
"languageAssistantPresetPracticeStart": "Practiquemos un diálogo cotidiano corto para la lección \"{lesson}\". Hazme preguntas y espera mis respuestas.",
"languageAssistantPresetCorrectStart": "Quiero escribir mis propias frases para la lección \"{lesson}\". Corrige mis respuestas de forma breve y clara.",
"thisLesson": "esta lección"
"thisLesson": "esta lección",
"courseKicker": "Curso de aprendizaje",
"courseListKicker": "Cursos",
"courseListIntro": "Filtra cursos públicos y propios, encuentra el adecuado y continúa directamente.",
"courseShareCodePlaceholder": "p. ej. abc123def456",
"courseFlowEyebrow": "Flujo del día",
"courseFlowTitle": "La mejor continuación para hoy",
"courseFlowIntro": "El orden sigue el concepto: primero los repasos pendientes, luego el bloque actual, después la fase intensiva y al final la práctica libre.",
"courseFlowReviewStat": "Repaso pendiente: {count}",
"courseFlowBlockStat": "Bloque activo: {block}",
"courseFlowReviewTitle": "Repaso pendiente",
"courseFlowReviewDescription": "Lecciones ya completadas que deberían volver hoy.",
"courseFlowReviewEmpty": "Hoy no hay ninguna lección antigua marcada como repaso pendiente.",
"courseFlowBlockTitle": "Bloque actual",
"courseFlowBlockDescription": "Aquí está el siguiente progreso regular dentro del curso.",
"courseFlowBlockEmpty": "El bloque actual ya está terminado o ahora mismo no hay ninguna lección abierta del bloque.",
"courseFlowIntensiveTitle": "Fase intensiva pendiente",
"courseFlowIntensiveDescription": "Repaso concentrado cuando el bloque anterior ya está bastante asentado.",
"courseFlowIntensiveEmpty": "Ahora mismo no hay ninguna nueva fase intensiva desbloqueada.",
"courseFlowPracticeTitle": "Práctica libre",
"courseFlowPracticeDescription": "Lecciones completadas para repasar con calma fuera del camino obligatorio.",
"courseFlowPracticeEmpty": "En cuanto completes tus primeras lecciones, aparecerán aquí para práctica libre.",
"practiceInTrainer": "Practicar en el entrenador",
"lessonsCount": "{count} lecciones",
"lessonBlockLabel": "Bloque {number}",
"lessonIntensiveBadge": "Repaso intensivo",
"addLessonValidation": "Indica por completo el número, el título y el capítulo.",
"addLessonSuccess": "Lección creada correctamente.",
"addLessonError": "No se pudo añadir la lección.",
"createCourseError": "No se pudo crear el curso.",
"deleteLessonTitle": "Eliminar lección",
"deleteLessonSuccess": "Lección eliminada correctamente.",
"deleteLessonError": "No se pudo eliminar la lección.",
"enrollCourseError": "No se pudo inscribirse en el curso.",
"editLessonPending": "La edición individual de lecciones llegará después.",
"timeToday": "hoy",
"timeSinceOneDay": "desde hace 1 día",
"timeSinceDays": "desde hace {count} días",
"reviewDueNow": "vence ahora",
"reviewDueTomorrow": "vence mañana",
"reviewDueInDays": "vence en {count} días",
"reviewDueToday": "vence hoy",
"reviewDueSinceOneDay": "vence desde hace 1 día",
"reviewDueSinceDays": "vence desde hace {count} días",
"reviewStageDay1": "Día 1",
"reviewStageDay3": "Día 3",
"reviewStageDay7": "Día 7",
"reviewStageCompleted": "Repaso completado",
"phaseQuickstart": "Inicio rápido",
"phaseDailyLife": "Vida diaria",
"phaseStabilization": "Estabilización",
"phaseDefault": "Fase de aprendizaje",
"didacticModeCoreInput": "Contenido nuevo",
"didacticModeGuidedDialogue": "Diálogo guiado",
"didacticModeContrastTraining": "Entrenamiento por contraste",
"didacticModePatternDrill": "Entrenamiento de patrones",
"didacticModeRealLifeScenario": "Escenario cotidiano",
"didacticModeIntensiveReview": "Fase de repaso",
"didacticModeCheckpoint": "Checkpoint",
"didacticModeDefault": "Unidad de aprendizaje",
"didacticModeFocusDefault": "Foco de aprendizaje",
"lessonMetaFocus": "Enfoque",
"lessonMetaPhase": "Fase",
"lessonMetaNewUnits": "Nuevas unidades",
"lessonMetaReview": "Repaso",
"intensiveReviewTitle": "Fase de repaso intensivo",
"intensiveReviewIntro": "Esta lección da prioridad al repaso y la consolidación. El material nuevo se reduce de forma consciente para estabilizar los patrones ya conocidos.",
"reviewPriorityTitle": "El repaso se mezcla paso a paso",
"reviewPriorityIntro": "Primero el foco está en los términos nuevos de esta lección. Con tu progreso se van mezclando cada vez más vocablos anteriores.",
"exerciseLockTitle": "La prueba del capítulo sigue bloqueada",
"trainerStartWithReview": "Empieza con el vocabulario nuevo de esta lección. A medida que avances, el entrenador mezclará automáticamente repasos adecuados.",
"startLesson": "Empezar lección",
"trainerProgressNewContent": "Contenido nuevo: {current}/{target}",
"trainerProgressReview": "Repaso: {count}",
"trainerProgressMixShare": "Parte mezclada: {percent}%",
"unknownExerciseTypeNotice": "Este tipo de ejercicio todavía no se muestra de forma interactiva en la vista actual.",
"unknownExerciseTypeLabel": "Tipo: {type}",
"lessonReviewHeadlineDone": "Esta lección ya ha llegado a la fase de práctica libre.",
"lessonReviewHeadlineDue": "Esta ola de repaso está pendiente ahora mismo.",
"lessonReviewHeadlineScheduled": "Esta lección está prevista para la siguiente ola de repaso.",
"lessonReviewHintDone": "El ciclo de repaso de 1/3/7 días está completado. Ahora puedes seguir practicando esta lección libremente.",
"lessonReviewHintNextDue": "Próximo vencimiento: {due}.",
"reviewTimeNow": "ahora",
"reviewTimeTomorrow": "mañana",
"reviewTimeInDays": "en {count} días"
}
}
}