mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-21 23:46: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
|
|
@ -41,6 +41,7 @@
|
|||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-utils": "workspace:*",
|
||||
"idb": "^8.0.0",
|
||||
"svelte-i18n": "^4.0.1",
|
||||
"uuid": "^11.0.0"
|
||||
},
|
||||
"type": "module"
|
||||
|
|
|
|||
49
apps/skilltree/apps/web/src/lib/i18n/index.ts
Normal file
49
apps/skilltree/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('skilltree_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('skilltree_locale', newLocale);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for locale to be loaded (useful for SSR)
|
||||
export { waitLocale };
|
||||
83
apps/skilltree/apps/web/src/lib/i18n/locales/de.json
Normal file
83
apps/skilltree/apps/web/src/lib/i18n/locales/de.json
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "SkillTree",
|
||||
"loading": "Laden...",
|
||||
"tagline": "Level Up Your Life"
|
||||
},
|
||||
"nav": {
|
||||
"skills": "Skills",
|
||||
"activities": "Aktivitäten",
|
||||
"stats": "Statistiken",
|
||||
"settings": "Einstellungen"
|
||||
},
|
||||
"skill": {
|
||||
"create": "Skill erstellen",
|
||||
"edit": "Skill bearbeiten",
|
||||
"delete": "Skill löschen",
|
||||
"name": "Name",
|
||||
"description": "Beschreibung",
|
||||
"branch": "Kategorie",
|
||||
"level": "Level",
|
||||
"xp": "XP",
|
||||
"totalXp": "Gesamt-XP",
|
||||
"noSkills": "Noch keine Skills",
|
||||
"addFirst": "Füge deinen ersten Skill hinzu"
|
||||
},
|
||||
"branch": {
|
||||
"intellect": "Intellekt",
|
||||
"body": "Körper",
|
||||
"creativity": "Kreativität",
|
||||
"social": "Soziales",
|
||||
"practical": "Praktisches",
|
||||
"mindset": "Mindset"
|
||||
},
|
||||
"level": {
|
||||
"unknown": "Unbekannt",
|
||||
"beginner": "Anfänger",
|
||||
"intermediate": "Fortgeschritten",
|
||||
"competent": "Kompetent",
|
||||
"expert": "Experte",
|
||||
"master": "Meister"
|
||||
},
|
||||
"activity": {
|
||||
"log": "Aktivität loggen",
|
||||
"recent": "Letzte Aktivitäten",
|
||||
"xpEarned": "+{xp} XP",
|
||||
"noActivities": "Noch keine Aktivitäten"
|
||||
},
|
||||
"stats": {
|
||||
"totalXp": "Gesamt-XP",
|
||||
"totalSkills": "Skills",
|
||||
"highestLevel": "Höchstes Level",
|
||||
"streak": "Streak"
|
||||
},
|
||||
"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": {
|
||||
"loadSkills": "Skills konnten nicht geladen werden",
|
||||
"createSkill": "Skill konnte nicht erstellt werden",
|
||||
"updateSkill": "Skill konnte nicht aktualisiert werden",
|
||||
"deleteSkill": "Skill konnte nicht gelöscht werden"
|
||||
},
|
||||
"success": {
|
||||
"skillCreated": "Skill erstellt",
|
||||
"skillUpdated": "Skill aktualisiert",
|
||||
"skillDeleted": "Skill gelöscht",
|
||||
"xpAdded": "XP hinzugefügt"
|
||||
}
|
||||
}
|
||||
83
apps/skilltree/apps/web/src/lib/i18n/locales/en.json
Normal file
83
apps/skilltree/apps/web/src/lib/i18n/locales/en.json
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "SkillTree",
|
||||
"loading": "Loading...",
|
||||
"tagline": "Level Up Your Life"
|
||||
},
|
||||
"nav": {
|
||||
"skills": "Skills",
|
||||
"activities": "Activities",
|
||||
"stats": "Statistics",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"skill": {
|
||||
"create": "Create skill",
|
||||
"edit": "Edit skill",
|
||||
"delete": "Delete skill",
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"branch": "Category",
|
||||
"level": "Level",
|
||||
"xp": "XP",
|
||||
"totalXp": "Total XP",
|
||||
"noSkills": "No skills yet",
|
||||
"addFirst": "Add your first skill"
|
||||
},
|
||||
"branch": {
|
||||
"intellect": "Intellect",
|
||||
"body": "Body",
|
||||
"creativity": "Creativity",
|
||||
"social": "Social",
|
||||
"practical": "Practical",
|
||||
"mindset": "Mindset"
|
||||
},
|
||||
"level": {
|
||||
"unknown": "Unknown",
|
||||
"beginner": "Beginner",
|
||||
"intermediate": "Intermediate",
|
||||
"competent": "Competent",
|
||||
"expert": "Expert",
|
||||
"master": "Master"
|
||||
},
|
||||
"activity": {
|
||||
"log": "Log activity",
|
||||
"recent": "Recent activities",
|
||||
"xpEarned": "+{xp} XP",
|
||||
"noActivities": "No activities yet"
|
||||
},
|
||||
"stats": {
|
||||
"totalXp": "Total XP",
|
||||
"totalSkills": "Skills",
|
||||
"highestLevel": "Highest Level",
|
||||
"streak": "Streak"
|
||||
},
|
||||
"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": {
|
||||
"loadSkills": "Failed to load skills",
|
||||
"createSkill": "Failed to create skill",
|
||||
"updateSkill": "Failed to update skill",
|
||||
"deleteSkill": "Failed to delete skill"
|
||||
},
|
||||
"success": {
|
||||
"skillCreated": "Skill created",
|
||||
"skillUpdated": "Skill updated",
|
||||
"skillDeleted": "Skill deleted",
|
||||
"xpAdded": "XP added"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import '$lib/i18n';
|
||||
import { onMount } from 'svelte';
|
||||
import { isLoading as i18nLoading, _ as t } from 'svelte-i18n';
|
||||
import { skillStore } from '$lib/stores/skills.svelte';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let loading = $state(true);
|
||||
let appReady = $derived(!loading && !$i18nLoading);
|
||||
|
||||
onMount(async () => {
|
||||
await Promise.all([authStore.initialize(), skillStore.initialize()]);
|
||||
|
|
@ -15,15 +18,15 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>SkillTree - Level Up Your Life</title>
|
||||
<title>{$t('app.name')} - {$t('app.tagline')}</title>
|
||||
<meta name="description" content="Track your skills like a game. Level up in real life." />
|
||||
</svelte:head>
|
||||
|
||||
{#if loading}
|
||||
{#if !appReady}
|
||||
<div class="flex min-h-screen items-center justify-center bg-gray-900">
|
||||
<div class="text-center">
|
||||
<div class="mb-4 text-6xl">🌳</div>
|
||||
<div class="text-xl text-gray-300">Loading SkillTree...</div>
|
||||
<div class="text-xl text-gray-300">{$t('app.loading')}</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue