mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 10:26:41 +02:00
🌐 feat: add i18n support to 6 web apps
Add internationalization (DE + EN) to previously missing apps:
- todo: task management translations
- skilltree: skill/XP system translations
- nutriphi: nutrition tracking translations
- planta: plant care translations
- questions: research app translations
- matrix: chat client translations (layout integration)
Each app includes:
- svelte-i18n setup with SSR support
- localStorage persistence ({app}_locale pattern)
- i18n loading state in +layout.svelte
- German (default) and English translations
Updated CONSISTENCY_REPORT.md to mark i18n task as complete.
Also includes:
- mana-tts service placeholder files
This commit is contained in:
parent
a938ed86d4
commit
5a0815708c
35 changed files with 3440 additions and 56 deletions
49
apps/nutriphi/apps/web/src/lib/i18n/index.ts
Normal file
49
apps/nutriphi/apps/web/src/lib/i18n/index.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { browser } from '$app/environment';
|
||||
import { init, register, locale, waitLocale } from 'svelte-i18n';
|
||||
|
||||
// List of supported locales
|
||||
export const supportedLocales = ['de', 'en'] as const;
|
||||
export type SupportedLocale = (typeof supportedLocales)[number];
|
||||
|
||||
// Default locale
|
||||
const defaultLocale = 'de';
|
||||
|
||||
// Register all available locales
|
||||
register('de', () => import('./locales/de.json'));
|
||||
register('en', () => import('./locales/en.json'));
|
||||
|
||||
// Get initial locale from browser or localStorage
|
||||
function getInitialLocale(): SupportedLocale {
|
||||
if (browser) {
|
||||
// Check localStorage first
|
||||
const stored = localStorage.getItem('nutriphi_locale');
|
||||
if (stored && supportedLocales.includes(stored as SupportedLocale)) {
|
||||
return stored as SupportedLocale;
|
||||
}
|
||||
|
||||
// Fall back to browser language
|
||||
const browserLang = navigator.language.split('-')[0];
|
||||
if (supportedLocales.includes(browserLang as SupportedLocale)) {
|
||||
return browserLang as SupportedLocale;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
// Initialize i18n at module scope (required for SSR)
|
||||
init({
|
||||
fallbackLocale: defaultLocale,
|
||||
initialLocale: getInitialLocale(),
|
||||
});
|
||||
|
||||
// Set locale and persist to localStorage
|
||||
export function setLocale(newLocale: SupportedLocale) {
|
||||
locale.set(newLocale);
|
||||
if (browser) {
|
||||
localStorage.setItem('nutriphi_locale', newLocale);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for locale to be loaded (useful for SSR)
|
||||
export { waitLocale };
|
||||
89
apps/nutriphi/apps/web/src/lib/i18n/locales/de.json
Normal file
89
apps/nutriphi/apps/web/src/lib/i18n/locales/de.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "NutriPhi",
|
||||
"loading": "Laden...",
|
||||
"tagline": "Ernährung verstehen"
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "Dashboard",
|
||||
"meals": "Mahlzeiten",
|
||||
"goals": "Ziele",
|
||||
"favorites": "Favoriten",
|
||||
"stats": "Statistiken",
|
||||
"settings": "Einstellungen"
|
||||
},
|
||||
"meal": {
|
||||
"add": "Mahlzeit hinzufügen",
|
||||
"edit": "Mahlzeit bearbeiten",
|
||||
"delete": "Mahlzeit löschen",
|
||||
"photo": "Foto aufnehmen",
|
||||
"text": "Beschreiben",
|
||||
"analyzing": "Analysiere...",
|
||||
"noMeals": "Noch keine Mahlzeiten",
|
||||
"breakfast": "Frühstück",
|
||||
"lunch": "Mittagessen",
|
||||
"dinner": "Abendessen",
|
||||
"snack": "Snack"
|
||||
},
|
||||
"nutrition": {
|
||||
"calories": "Kalorien",
|
||||
"protein": "Protein",
|
||||
"carbs": "Kohlenhydrate",
|
||||
"fat": "Fett",
|
||||
"fiber": "Ballaststoffe",
|
||||
"sugar": "Zucker",
|
||||
"kcal": "kcal",
|
||||
"grams": "g"
|
||||
},
|
||||
"goals": {
|
||||
"daily": "Tagesziele",
|
||||
"setGoals": "Ziele setzen",
|
||||
"calories": "Kalorien-Ziel",
|
||||
"protein": "Protein-Ziel",
|
||||
"carbs": "Kohlenhydrate-Ziel",
|
||||
"fat": "Fett-Ziel",
|
||||
"progress": "Fortschritt"
|
||||
},
|
||||
"stats": {
|
||||
"today": "Heute",
|
||||
"week": "Diese Woche",
|
||||
"remaining": "Verbleibend",
|
||||
"consumed": "Verzehrt",
|
||||
"average": "Durchschnitt"
|
||||
},
|
||||
"favorites": {
|
||||
"add": "Zu Favoriten",
|
||||
"remove": "Aus Favoriten entfernen",
|
||||
"noFavorites": "Keine Favoriten",
|
||||
"useAgain": "Erneut verwenden"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"register": "Registrieren"
|
||||
},
|
||||
"common": {
|
||||
"save": "Speichern",
|
||||
"cancel": "Abbrechen",
|
||||
"delete": "Löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"add": "Hinzufügen",
|
||||
"close": "Schließen",
|
||||
"search": "Suchen",
|
||||
"error": "Fehler",
|
||||
"success": "Erfolgreich",
|
||||
"loading": "Laden..."
|
||||
},
|
||||
"errors": {
|
||||
"loadMeals": "Mahlzeiten konnten nicht geladen werden",
|
||||
"analyzeFailed": "Analyse fehlgeschlagen",
|
||||
"saveFailed": "Speichern fehlgeschlagen",
|
||||
"loadGoals": "Ziele konnten nicht geladen werden"
|
||||
},
|
||||
"success": {
|
||||
"mealAdded": "Mahlzeit hinzugefügt",
|
||||
"mealDeleted": "Mahlzeit gelöscht",
|
||||
"goalsSaved": "Ziele gespeichert",
|
||||
"favoriteAdded": "Zu Favoriten hinzugefügt"
|
||||
}
|
||||
}
|
||||
89
apps/nutriphi/apps/web/src/lib/i18n/locales/en.json
Normal file
89
apps/nutriphi/apps/web/src/lib/i18n/locales/en.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "NutriPhi",
|
||||
"loading": "Loading...",
|
||||
"tagline": "Understand nutrition"
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "Dashboard",
|
||||
"meals": "Meals",
|
||||
"goals": "Goals",
|
||||
"favorites": "Favorites",
|
||||
"stats": "Statistics",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"meal": {
|
||||
"add": "Add meal",
|
||||
"edit": "Edit meal",
|
||||
"delete": "Delete meal",
|
||||
"photo": "Take photo",
|
||||
"text": "Describe",
|
||||
"analyzing": "Analyzing...",
|
||||
"noMeals": "No meals yet",
|
||||
"breakfast": "Breakfast",
|
||||
"lunch": "Lunch",
|
||||
"dinner": "Dinner",
|
||||
"snack": "Snack"
|
||||
},
|
||||
"nutrition": {
|
||||
"calories": "Calories",
|
||||
"protein": "Protein",
|
||||
"carbs": "Carbohydrates",
|
||||
"fat": "Fat",
|
||||
"fiber": "Fiber",
|
||||
"sugar": "Sugar",
|
||||
"kcal": "kcal",
|
||||
"grams": "g"
|
||||
},
|
||||
"goals": {
|
||||
"daily": "Daily goals",
|
||||
"setGoals": "Set goals",
|
||||
"calories": "Calorie goal",
|
||||
"protein": "Protein goal",
|
||||
"carbs": "Carbohydrate goal",
|
||||
"fat": "Fat goal",
|
||||
"progress": "Progress"
|
||||
},
|
||||
"stats": {
|
||||
"today": "Today",
|
||||
"week": "This week",
|
||||
"remaining": "Remaining",
|
||||
"consumed": "Consumed",
|
||||
"average": "Average"
|
||||
},
|
||||
"favorites": {
|
||||
"add": "Add to favorites",
|
||||
"remove": "Remove from favorites",
|
||||
"noFavorites": "No favorites",
|
||||
"useAgain": "Use again"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"register": "Register"
|
||||
},
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"add": "Add",
|
||||
"close": "Close",
|
||||
"search": "Search",
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"loading": "Loading..."
|
||||
},
|
||||
"errors": {
|
||||
"loadMeals": "Failed to load meals",
|
||||
"analyzeFailed": "Analysis failed",
|
||||
"saveFailed": "Failed to save",
|
||||
"loadGoals": "Failed to load goals"
|
||||
},
|
||||
"success": {
|
||||
"mealAdded": "Meal added",
|
||||
"mealDeleted": "Meal deleted",
|
||||
"goalsSaved": "Goals saved",
|
||||
"favoriteAdded": "Added to favorites"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import '$lib/i18n';
|
||||
import { isLoading as i18nLoading, _ as t } from 'svelte-i18n';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
|
|
@ -11,7 +13,13 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>NutriPhi - Ernährung verstehen</title>
|
||||
<title>{$t('app.name')} - {$t('app.tagline')}</title>
|
||||
</svelte:head>
|
||||
|
||||
{@render children()}
|
||||
{#if $i18nLoading}
|
||||
<div class="flex min-h-screen items-center justify-center">
|
||||
<p>{$t('app.loading')}</p>
|
||||
</div>
|
||||
{:else}
|
||||
{@render children()}
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue