feat(i18n): add internationalization with i18next and expo-localization

This commit is contained in:
2026-08-08 10:09:30 +02:00
parent 408a2fa10c
commit 9946e78858
81 changed files with 1820 additions and 125 deletions
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Workout an Watch gesendet!",
"openWatchApp": "Öffne die SwimBuddy-App auf deiner Apple Watch, um mit der Verfolgung zu beginnen.",
"failedToSend": "Senden fehlgeschlagen",
"couldNotSend": "Workout konnte nicht an Watch gesendet werden. Starte ohne Watch.",
"startWithoutWatch": "Ohne Watch starten",
"sendToWatchAndStart": "An Watch senden & starten",
"syncToWatch": "Workout wird mit deiner Apple Watch synchronisiert",
"trackedOnDevice": "Workout wird auf diesem Gerät verfolgt",
"rest": "Pause",
"betweenSets": "{{duration}}s Pause zwischen Sets",
"swimComplete": "Schwimmen abgeschlossen!",
"greatJob": "Gut gemacht! Du bist heute ins Wasser gegangen!",
"loadingSummary": "Zusammenfassung laden...",
"watchStatus": {
"ready": "Apple Watch bereit",
"pairedNotInRange": "Watch gekoppelt, aber nicht in Reichweite",
"pairedNotInstalled": "Watch gekoppelt, App nicht installiert",
"noWatch": "Keine Watch gekoppelt"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Fertig",
"cancel": "Abbrechen",
"delete": "Löschen",
"start": "Starten",
"back": "Zurück",
"loading": "Laden...",
"error": "Fehler",
"save": "Speichern",
"saving": "Speichern...",
"yes": "Ja",
"no": "Nein",
"confirm": "Bestätigen",
"ok": "OK",
"distance": "Distanz",
"duration": "Dauer",
"workouts": "Workouts",
"sets": "Sets",
"laps": "Bahnen",
"avgSwolf": "Durchschn. SWOLF",
"pool": "{{length}}m Becken",
"untitledWorkout": "Unbenannter Workout",
"swimSession": "Schwimmsitzung",
"workout": "Workout",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Freistil",
"backstroke": "Rücken",
"breaststroke": "Brust",
"butterfly": "Schmetterling",
"mixed": "Gemischt"
},
"alert": {
"deleteWorkout": "Workout löschen",
"deleteTemplate": "Vorlage löschen",
"areYouSure": "Bist du sicher?",
"deleteConfirm": "\"{{name}}\" löschen?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Noch keine abgeschlossenen Workouts",
"lap": "Bahn {{number}}",
"statistics": "Statistiken",
"thisWeek": "Diese Woche",
"allTime": "Gesamtzeit",
"personalBests": "Persönliche Bestleistungen",
"noDataYet": "Noch keine Daten",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Workouts",
"history": "Verlauf",
"settings": "Einstellungen",
"newWorkout": "Neues Workout",
"buildWorkout": "Workout erstellen",
"startSwim": "Schwimmen starten",
"readyToSwim": "Bereit zum Schwimmen",
"swimComplete": "Schwimmen abgeschlossen",
"session": "Sitzung",
"sessionDetail": "Sitzungsdetails"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Einstellungen",
"defaultPoolLength": "Standard-Beckenlänge",
"language": "Sprache",
"systemLanguage": "Systemsprache",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Workout-Name",
"poolLength": "Beckenlänge",
"stroke": "Lage",
"distanceM": "Distanz (m)",
"reps": "Wiederholungen",
"restS": "Pause (s)",
"addSet": "+ Set hinzufügen",
"remove": "Entfernen",
"setNumber": "Set {{number}}",
"startSwim": "Schwimmen starten",
"newWorkout": "+ Neues Workout",
"noWorkoutsYet": "Noch keine Workouts",
"tapNewWorkout": "Tippe auf \"Neues Workout\" um zu beginnen",
"morningSwim": "Morgenschwimmen",
"placeholder": {
"workoutName": "Morgenschwimmen"
},
"validation": {
"error": "Fehler",
"failedToSave": "Workout konnte nicht gespeichert werden: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Træning sendt til Watch!",
"openWatchApp": "Åbn SwimBuddy-appen på din Apple Watch for at begynde sporing.",
"failedToSend": "Sendning mislykkedes",
"couldNotSend": "Kunne ikke sende træning til Watch. Starter uden Watch.",
"startWithoutWatch": "Start uden Watch",
"sendToWatchAndStart": "Send til Watch & start",
"syncToWatch": "Træningen synkroniseres med din Apple Watch",
"trackedOnDevice": "Træningen spores på denne enhed",
"rest": "Hvile",
"betweenSets": "{{duration}}s hvile mellem sæt",
"swimComplete": "Svømning fuldført!",
"greatJob": "Godt gået! Du var i vandet i dag!",
"loadingSummary": "Indlæser opsummering...",
"watchStatus": {
"ready": "Apple Watch klar",
"pairedNotInRange": "Watch parret, men ikke inden for rækkevidde",
"pairedNotInstalled": "Watch parret, app ikke installeret",
"noWatch": "Ingen Watch parret"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Færdig",
"cancel": "Annuller",
"delete": "Slet",
"start": "Start",
"back": "Tilbage",
"loading": "Indlæser...",
"error": "Fejl",
"save": "Gem",
"saving": "Gemmer...",
"yes": "Ja",
"no": "Nej",
"confirm": "Bekræft",
"ok": "OK",
"distance": "Distance",
"duration": "Varighed",
"workouts": "Træninger",
"sets": "Sæt",
"laps": "Baner",
"avgSwolf": "Gns. SWOLF",
"pool": "{{length}}m pool",
"untitledWorkout": "Unavngiven træning",
"swimSession": "Svømmesession",
"workout": "Træning",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Fri crawl",
"backstroke": "Rygcrawl",
"breaststroke": "Brystsvømning",
"butterfly": "Butterfly",
"mixed": "Blandet"
},
"alert": {
"deleteWorkout": "Slet træning",
"deleteTemplate": "Slet skabelon",
"areYouSure": "Er du sikker?",
"deleteConfirm": "Slet \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Ingen fuldførte træninger endnu",
"lap": "Bane {{number}}",
"statistics": "Statistikker",
"thisWeek": "Denne uge",
"allTime": "Alt tid",
"personalBests": "Personlige rekorder",
"noDataYet": "Ingen data endnu",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Træninger",
"history": "Historik",
"settings": "Indstillinger",
"newWorkout": "Ny træning",
"buildWorkout": "Opret træning",
"startSwim": "Start svømning",
"readyToSwim": "Klar til at svømme",
"swimComplete": "Svømning fuldført",
"session": "Session",
"sessionDetail": "Sessionsdetaljer"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Indstillinger",
"defaultPoolLength": "Standard poolængde",
"language": "Sprog",
"systemLanguage": "Systemssprog",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Træningsnavn",
"poolLength": "Poolængde",
"stroke": "Svømmearm",
"distanceM": "Distance (m)",
"reps": "Gentagelser",
"restS": "Hvile (s)",
"addSet": "+ Tilføj sæt",
"remove": "Fjern",
"setNumber": "Sæt {{number}}",
"startSwim": "Start svømning",
"newWorkout": "+ Ny træning",
"noWorkoutsYet": "Ingen træninger endnu",
"tapNewWorkout": "Tryk på \"Ny træning\" for at komme i gang",
"morningSwim": "Morgensvømning",
"placeholder": {
"workoutName": "Morgensvømning"
},
"validation": {
"error": "Fejl",
"failedToSave": "Kunne ikke gemme træningen: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Workout sent to Watch!",
"openWatchApp": "Open the SwimBuddy app on your Apple Watch to start tracking.",
"failedToSend": "Failed to send",
"couldNotSend": "Could not send workout to Watch. Starting without Watch instead.",
"startWithoutWatch": "Start Without Watch",
"sendToWatchAndStart": "Send to Watch & Start",
"syncToWatch": "Workout will be synced to your Apple Watch",
"trackedOnDevice": "Workout will be tracked on this device",
"rest": "Rest",
"betweenSets": "{{duration}}s between sets",
"swimComplete": "Swim Complete!",
"greatJob": "Great job getting in the water today!",
"loadingSummary": "Loading summary...",
"watchStatus": {
"ready": "Apple Watch ready",
"pairedNotInRange": "Watch paired but not in range",
"pairedNotInstalled": "Watch paired, app not installed",
"noWatch": "No Watch paired"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Done",
"cancel": "Cancel",
"delete": "Delete",
"start": "Start",
"back": "Back",
"loading": "Loading...",
"error": "Error",
"save": "Save",
"saving": "Saving...",
"yes": "Yes",
"no": "No",
"confirm": "Confirm",
"ok": "OK",
"distance": "Distance",
"duration": "Duration",
"workouts": "Workouts",
"sets": "Sets",
"laps": "Laps",
"avgSwolf": "Avg SWOLF",
"pool": "{{length}}m pool",
"untitledWorkout": "Untitled Workout",
"swimSession": "Swim Session",
"workout": "Workout",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Freestyle",
"backstroke": "Backstroke",
"breaststroke": "Breaststroke",
"butterfly": "Butterfly",
"mixed": "Mixed"
},
"alert": {
"deleteWorkout": "Delete Workout",
"deleteTemplate": "Delete Template",
"areYouSure": "Are you sure?",
"deleteConfirm": "Delete \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "No completed workouts yet",
"lap": "Lap {{number}}",
"statistics": "Statistics",
"thisWeek": "This Week",
"allTime": "All Time",
"personalBests": "Personal Bests",
"noDataYet": "No data yet",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Workouts",
"history": "History",
"settings": "Settings",
"newWorkout": "New Workout",
"buildWorkout": "Build Workout",
"startSwim": "Start Swim",
"readyToSwim": "Ready to Swim",
"swimComplete": "Swim Complete",
"session": "Session",
"sessionDetail": "Session Detail"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Settings",
"defaultPoolLength": "Default Pool Length",
"language": "Language",
"systemLanguage": "System Language",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Workout Name",
"poolLength": "Pool Length",
"stroke": "Stroke",
"distanceM": "Distance (m)",
"reps": "Reps",
"restS": "Rest (s)",
"addSet": "+ Add Set",
"remove": "Remove",
"setNumber": "Set {{number}}",
"startSwim": "Start Swim",
"newWorkout": "+ New Workout",
"noWorkoutsYet": "No workouts yet",
"tapNewWorkout": "Tap \"New Workout\" to get started",
"morningSwim": "Morning Swim",
"placeholder": {
"workoutName": "Morning Swim"
},
"validation": {
"error": "Error",
"failedToSave": "Failed to save workout: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "¡Entrenamiento enviado al Watch!",
"openWatchApp": "Abre la app SwimBuddy en tu Apple Watch para empezar el seguimiento.",
"failedToSend": "Error al enviar",
"couldNotSend": "No se pudo enviar el entrenamiento al Watch. Iniciando sin Watch.",
"startWithoutWatch": "Iniciar sin Watch",
"sendToWatchAndStart": "Enviar al Watch e iniciar",
"syncToWatch": "El entrenamiento se sincronizará con tu Apple Watch",
"trackedOnDevice": "El entrenamiento se rastreará en este dispositivo",
"rest": "Descanso",
"betweenSets": "{{duration}}s de descanso entre series",
"swimComplete": "¡Natación completada!",
"greatJob": "¡Buen trabajo! ¡Hoy has estado en el agua!",
"loadingSummary": "Cargando resumen...",
"watchStatus": {
"ready": "Apple Watch lista",
"pairedNotInRange": "Watch emparejada pero fuera de alcance",
"pairedNotInstalled": "Watch emparejada, app no instalada",
"noWatch": "Ninguna Watch emparejada"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Listo",
"cancel": "Cancelar",
"delete": "Eliminar",
"start": "Iniciar",
"back": "Volver",
"loading": "Cargando...",
"error": "Error",
"save": "Guardar",
"saving": "Guardando...",
"yes": "Sí",
"no": "No",
"confirm": "Confirmar",
"ok": "OK",
"distance": "Distancia",
"duration": "Duración",
"workouts": "Entrenamientos",
"sets": "Series",
"laps": "Largos",
"avgSwolf": "SWOLF prom.",
"pool": "Piscina de {{length}}m",
"untitledWorkout": "Entrenamiento sin nombre",
"swimSession": "Sesión de natación",
"workout": "Entrenamiento",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Libre",
"backstroke": "Espalda",
"breaststroke": "Pecho",
"butterfly": "Mariposa",
"mixed": "Mixto"
},
"alert": {
"deleteWorkout": "Eliminar entrenamiento",
"deleteTemplate": "Eliminar plantilla",
"areYouSure": "¿Estás seguro?",
"deleteConfirm": "¿Eliminar \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Aún no hay entrenamientos completados",
"lap": "Largo {{number}}",
"statistics": "Estadísticas",
"thisWeek": "Esta semana",
"allTime": "Todos los tiempos",
"personalBests": "Récords personales",
"noDataYet": "Aún no hay datos",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Entrenamientos",
"history": "Historial",
"settings": "Ajustes",
"newWorkout": "Nuevo entrenamiento",
"buildWorkout": "Crear entrenamiento",
"startSwim": "Iniciar natación",
"readyToSwim": "Listo para nadar",
"swimComplete": "Natación completada",
"session": "Sesión",
"sessionDetail": "Detalle de sesión"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Ajustes",
"defaultPoolLength": "Longitud de piscina predeterminada",
"language": "Idioma",
"systemLanguage": "Idioma del sistema",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Nombre del entrenamiento",
"poolLength": "Longitud de la piscina",
"stroke": "Estilo",
"distanceM": "Distancia (m)",
"reps": "Repeticiones",
"restS": "Descanso (s)",
"addSet": "+ Añadir serie",
"remove": "Eliminar",
"setNumber": "Serie {{number}}",
"startSwim": "Iniciar natación",
"newWorkout": "+ Nuevo entrenamiento",
"noWorkoutsYet": "Aún no hay entrenamientos",
"tapNewWorkout": "Toca \"Nuevo entrenamiento\" para empezar",
"morningSwim": "Natación matutina",
"placeholder": {
"workoutName": "Natación matutina"
},
"validation": {
"error": "Error",
"failedToSave": "No se pudo guardar el entrenamiento: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Entraînement envoyé à la Watch !",
"openWatchApp": "Ouvrez l'application SwimBuddy sur votre Apple Watch pour commencer le suivi.",
"failedToSend": "Échec de l'envoi",
"couldNotSend": "Impossible d'envoyer l'entraînement à la Watch. Démarrage sans Watch.",
"startWithoutWatch": "Démarrer sans Watch",
"sendToWatchAndStart": "Envoyer à la Watch et commencer",
"syncToWatch": "L'entraînement sera synchronisé avec votre Apple Watch",
"trackedOnDevice": "L'entraînement sera suivi sur cet appareil",
"rest": "Repos",
"betweenSets": "{{duration}}s de repos entre les séries",
"swimComplete": "Nage terminée !",
"greatJob": "Bravo ! Vous êtes allé à l'eau aujourd'hui !",
"loadingSummary": "Chargement du résumé...",
"watchStatus": {
"ready": "Apple Watch prête",
"pairedNotInRange": "Watch appariée mais hors de portée",
"pairedNotInstalled": "Watch appariée, application non installée",
"noWatch": "Aucune Watch appariée"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Terminé",
"cancel": "Annuler",
"delete": "Supprimer",
"start": "Commencer",
"back": "Retour",
"loading": "Chargement...",
"error": "Erreur",
"save": "Enregistrer",
"saving": "Enregistrement...",
"yes": "Oui",
"no": "Non",
"confirm": "Confirmer",
"ok": "OK",
"distance": "Distance",
"duration": "Durée",
"workouts": "Entraînements",
"sets": "Séries",
"laps": "Longueurs",
"avgSwolf": "SWOLF moy.",
"pool": "Bassin de {{length}}m",
"untitledWorkout": "Entraînement sans titre",
"swimSession": "Séance de natation",
"workout": "Entraînement",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Nage libre",
"backstroke": "Dos",
"breaststroke": "Brasse",
"butterfly": "Papillon",
"mixed": "Mixte"
},
"alert": {
"deleteWorkout": "Supprimer l'entraînement",
"deleteTemplate": "Supprimer le modèle",
"areYouSure": "Êtes-vous sûr ?",
"deleteConfirm": "Supprimer \"{{name}}\" ?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Aucun entraînement terminé pour le moment",
"lap": "Longueur {{number}}",
"statistics": "Statistiques",
"thisWeek": "Cette semaine",
"allTime": "Tout le temps",
"personalBests": "Records personnels",
"noDataYet": "Pas encore de données",
"swolf": "SWOLF : {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Entraînements",
"history": "Historique",
"settings": "Paramètres",
"newWorkout": "Nouvel entraînement",
"buildWorkout": "Créer un entraînement",
"startSwim": "Commencer la nage",
"readyToSwim": "Prêt à nager",
"swimComplete": "Nage terminée",
"session": "Séance",
"sessionDetail": "Détails de la séance"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Paramètres",
"defaultPoolLength": "Longueur du bassin par défaut",
"language": "Langue",
"systemLanguage": "Langue du système",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Nom de l'entraînement",
"poolLength": "Longueur du bassin",
"stroke": "Style",
"distanceM": "Distance (m)",
"reps": "Répétitions",
"restS": "Repos (s)",
"addSet": "+ Ajouter une série",
"remove": "Retirer",
"setNumber": "Série {{number}}",
"startSwim": "Commencer la nage",
"newWorkout": "+ Nouvel entraînement",
"noWorkoutsYet": "Aucun entraînement pour le moment",
"tapNewWorkout": "Appuyez sur \"Nouvel entraînement\" pour commencer",
"morningSwim": "Matinée de natation",
"placeholder": {
"workoutName": "Matinée de natation"
},
"validation": {
"error": "Erreur",
"failedToSave": "Échec de l'enregistrement : {{error}}"
}
}
+231
View File
@@ -0,0 +1,231 @@
import { useRef, useEffect } from "react";
import { AppState, type AppStateStatus } from "react-native";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import { getLocales } from "expo-localization";
import { createMMKV } from "react-native-mmkv";
import enCommon from "./en/common.json";
import enNavigation from "./en/navigation.json";
import enWorkout from "./en/workout.json";
import enActiveWorkout from "./en/active-workout.json";
import enHistory from "./en/history.json";
import enSettings from "./en/settings.json";
import nlCommon from "./nl/common.json";
import nlNavigation from "./nl/navigation.json";
import nlWorkout from "./nl/workout.json";
import nlActiveWorkout from "./nl/active-workout.json";
import nlHistory from "./nl/history.json";
import nlSettings from "./nl/settings.json";
import deCommon from "./de/common.json";
import deNavigation from "./de/navigation.json";
import deWorkout from "./de/workout.json";
import deActiveWorkout from "./de/active-workout.json";
import deHistory from "./de/history.json";
import deSettings from "./de/settings.json";
import frCommon from "./fr/common.json";
import frNavigation from "./fr/navigation.json";
import frWorkout from "./fr/workout.json";
import frActiveWorkout from "./fr/active-workout.json";
import frHistory from "./fr/history.json";
import frSettings from "./fr/settings.json";
import itCommon from "./it/common.json";
import itNavigation from "./it/navigation.json";
import itWorkout from "./it/workout.json";
import itActiveWorkout from "./it/active-workout.json";
import itHistory from "./it/history.json";
import itSettings from "./it/settings.json";
import esCommon from "./es/common.json";
import esNavigation from "./es/navigation.json";
import esWorkout from "./es/workout.json";
import esActiveWorkout from "./es/active-workout.json";
import esHistory from "./es/history.json";
import esSettings from "./es/settings.json";
import plCommon from "./pl/common.json";
import plNavigation from "./pl/navigation.json";
import plWorkout from "./pl/workout.json";
import plActiveWorkout from "./pl/active-workout.json";
import plHistory from "./pl/history.json";
import plSettings from "./pl/settings.json";
import ptCommon from "./pt/common.json";
import ptNavigation from "./pt/navigation.json";
import ptWorkout from "./pt/workout.json";
import ptActiveWorkout from "./pt/active-workout.json";
import ptHistory from "./pt/history.json";
import ptSettings from "./pt/settings.json";
import dkCommon from "./dk/common.json";
import dkNavigation from "./dk/navigation.json";
import dkWorkout from "./dk/workout.json";
import dkActiveWorkout from "./dk/active-workout.json";
import dkHistory from "./dk/history.json";
import dkSettings from "./dk/settings.json";
export const LANGUAGES = [
{ code: "en", label: "English" },
{ code: "nl", label: "Nederlands" },
{ code: "de", label: "Deutsch" },
{ code: "fr", label: "Français" },
{ code: "it", label: "Italiano" },
{ code: "es", label: "Español" },
{ code: "pl", label: "Polski" },
{ code: "pt", label: "Português" },
{ code: "dk", label: "Dansk" },
] as const;
export type LanguageCode = (typeof LANGUAGES)[number]["code"];
export type StoredLanguage = LanguageCode | "system";
const namespaces = [
"common",
"navigation",
"workout",
"active-workout",
"history",
"settings",
] as const;
export type Namespace = (typeof namespaces)[number];
const storage = createMMKV({ id: "i18n-settings" });
export function getStoredLanguage(): StoredLanguage | undefined {
const stored = storage.getString("language");
if (stored === "system") return "system";
if (stored && LANGUAGES.some((l) => l.code === stored)) {
return stored as LanguageCode;
}
return undefined;
}
export function setStoredLanguage(code: StoredLanguage) {
storage.set("language", code);
}
export function getDeviceLanguage(): LanguageCode {
const deviceLocale = getLocales()[0]?.languageCode ?? "en";
if (LANGUAGES.some((l) => l.code === deviceLocale)) {
return deviceLocale as LanguageCode;
}
return "en";
}
export function resolveLanguage(stored: StoredLanguage | undefined): LanguageCode {
if (stored === "system" || stored === undefined) return getDeviceLanguage();
return stored;
}
export function useSystemLanguageListener() {
const appState = useRef(AppState.currentState);
useEffect(() => {
const subscription = AppState.addEventListener("change", (nextState: AppStateStatus) => {
if (appState.current.match(/inactive|background/) && nextState === "active") {
const stored = getStoredLanguage();
if (stored === "system") {
const deviceLang = getDeviceLanguage();
if (i18n.language !== deviceLang) {
i18n.changeLanguage(deviceLang);
}
}
}
appState.current = nextState;
});
return () => subscription.remove();
}, []);
}
i18n.use(initReactI18next).init({
resources: {
en: {
common: enCommon,
navigation: enNavigation,
workout: enWorkout,
"active-workout": enActiveWorkout,
history: enHistory,
settings: enSettings,
},
nl: {
common: nlCommon,
navigation: nlNavigation,
workout: nlWorkout,
"active-workout": nlActiveWorkout,
history: nlHistory,
settings: nlSettings,
},
de: {
common: deCommon,
navigation: deNavigation,
workout: deWorkout,
"active-workout": deActiveWorkout,
history: deHistory,
settings: deSettings,
},
fr: {
common: frCommon,
navigation: frNavigation,
workout: frWorkout,
"active-workout": frActiveWorkout,
history: frHistory,
settings: frSettings,
},
it: {
common: itCommon,
navigation: itNavigation,
workout: itWorkout,
"active-workout": itActiveWorkout,
history: itHistory,
settings: itSettings,
},
es: {
common: esCommon,
navigation: esNavigation,
workout: esWorkout,
"active-workout": esActiveWorkout,
history: esHistory,
settings: esSettings,
},
pl: {
common: plCommon,
navigation: plNavigation,
workout: plWorkout,
"active-workout": plActiveWorkout,
history: plHistory,
settings: plSettings,
},
pt: {
common: ptCommon,
navigation: ptNavigation,
workout: ptWorkout,
"active-workout": ptActiveWorkout,
history: ptHistory,
settings: ptSettings,
},
dk: {
common: dkCommon,
navigation: dkNavigation,
workout: dkWorkout,
"active-workout": dkActiveWorkout,
history: dkHistory,
settings: dkSettings,
},
},
lng: resolveLanguage(getStoredLanguage()),
fallbackLng: "en",
ns: [...namespaces],
defaultNS: "common",
interpolation: {
escapeValue: false,
},
});
export default i18n;
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Allenamento inviato alla Watch!",
"openWatchApp": "Apri l'app SwimBuddy sulla tua Apple Watch per iniziare il monitoraggio.",
"failedToSend": "Invio fallito",
"couldNotSend": "Impossibile inviare l'allenamento alla Watch. Avvio senza Watch.",
"startWithoutWatch": "Avvia senza Watch",
"sendToWatchAndStart": "Invia alla Watch e avvia",
"syncToWatch": "L'allenamento verrà sincronizzato con la tua Apple Watch",
"trackedOnDevice": "L'allenamento verrà monitorato su questo dispositivo",
"rest": "Recupero",
"betweenSets": "{{duration}}s di recupero tra le serie",
"swimComplete": "Nuoto completato!",
"greatJob": "Ottimo lavoro! Sei entrato in acqua oggi!",
"loadingSummary": "Caricamento riepilogo...",
"watchStatus": {
"ready": "Apple Watch pronta",
"pairedNotInRange": "Watch associata ma fuori portata",
"pairedNotInstalled": "Watch associata, app non installata",
"noWatch": "Nessuna Watch associata"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Fatto",
"cancel": "Annulla",
"delete": "Elimina",
"start": "Inizia",
"back": "Indietro",
"loading": "Caricamento...",
"error": "Errore",
"save": "Salva",
"saving": "Salvataggio...",
"yes": "Sì",
"no": "No",
"confirm": "Conferma",
"ok": "OK",
"distance": "Distanza",
"duration": "Durata",
"workouts": "Allenamenti",
"sets": "Serie",
"laps": "Lunghezze",
"avgSwolf": "SWOLF media",
"pool": "Piscina di {{length}}m",
"untitledWorkout": "Allenamento senza nome",
"swimSession": "Sessione di nuoto",
"workout": "Allenamento",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Stile libero",
"backstroke": "Dorso",
"breaststroke": "Rana",
"butterfly": "Farfalla",
"mixed": "Misto"
},
"alert": {
"deleteWorkout": "Elimina allenamento",
"deleteTemplate": "Elimina modello",
"areYouSure": "Sei sicuro?",
"deleteConfirm": "Eliminare \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Nessun allenamento completato ancora",
"lap": "Lunghezza {{number}}",
"statistics": "Statistiche",
"thisWeek": "Questa settimana",
"allTime": "Tutti i tempi",
"personalBests": "Record personali",
"noDataYet": "Nessun dato ancora",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Allenamenti",
"history": "Cronologia",
"settings": "Impostazioni",
"newWorkout": "Nuovo allenamento",
"buildWorkout": "Crea allenamento",
"startSwim": "Inizia nuoto",
"readyToSwim": "Pronto per nuotare",
"swimComplete": "Nuoto completato",
"session": "Sessione",
"sessionDetail": "Dettagli sessione"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Impostazioni",
"defaultPoolLength": "Lunghezza piscina predefinita",
"language": "Lingua",
"systemLanguage": "Lingua di sistema",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Nome allenamento",
"poolLength": "Lunghezza piscina",
"stroke": "Stile",
"distanceM": "Distanza (m)",
"reps": "Ripetizioni",
"restS": "Recupero (s)",
"addSet": "+ Aggiungi serie",
"remove": "Rimuovi",
"setNumber": "Serie {{number}}",
"startSwim": "Inizia nuoto",
"newWorkout": "+ Nuovo allenamento",
"noWorkoutsYet": "Nessun allenamento ancora",
"tapNewWorkout": "Tocca \"Nuovo allenamento\" per iniziare",
"morningSwim": "Nuoto mattutino",
"placeholder": {
"workoutName": "Nuoto mattutino"
},
"validation": {
"error": "Errore",
"failedToSave": "Impossibile salvare l'allenamento: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Workout verzonden naar Watch!",
"openWatchApp": "Open de SwimBuddy-app op je Apple Watch om te beginnen met bijhouden.",
"failedToSend": "Verzenden mislukt",
"couldNotSend": "Kon workout niet naar Watch verzenden. Start zonder Watch.",
"startWithoutWatch": "Start zonder Watch",
"sendToWatchAndStart": "Verzenden naar Watch & Start",
"syncToWatch": "Workout wordt gesynchroniseerd naar je Apple Watch",
"trackedOnDevice": "Workout wordt op dit apparaat bijgehouden",
"rest": "Rust",
"betweenSets": "{{duration}}s rust tussen sets",
"swimComplete": "Zwemmen voltooid!",
"greatJob": "Goed gedaan! Je bent vandaag het water in geweest!",
"loadingSummary": "Samenvatting laden...",
"watchStatus": {
"ready": "Apple Watch gereed",
"pairedNotInRange": "Watch gekoppeld maar niet in bereik",
"pairedNotInstalled": "Watch gekoppeld, app niet geïnstalleerd",
"noWatch": "Geen Watch gekoppeld"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Gereed",
"cancel": "Annuleren",
"delete": "Verwijderen",
"start": "Start",
"back": "Terug",
"loading": "Laden...",
"error": "Fout",
"save": "Opslaan",
"saving": "Opslaan...",
"yes": "Ja",
"no": "Nee",
"confirm": "Bevestigen",
"ok": "OK",
"distance": "Afstand",
"duration": "Duur",
"workouts": "Workouts",
"sets": "Sets",
"laps": "Banen",
"avgSwolf": "Gem. SWOLF",
"pool": "{{length}}m bad",
"untitledWorkout": "Naamloze Workout",
"swimSession": "Zwemsessie",
"workout": "Workout",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Vrije slag",
"backstroke": "Rugslag",
"breaststroke": "Schoolslag",
"butterfly": "Vlinderslag",
"mixed": "Gemengd"
},
"alert": {
"deleteWorkout": "Workout verwijderen",
"deleteTemplate": "Sjabloon verwijderen",
"areYouSure": "Weet je het zeker?",
"deleteConfirm": "\"{{name}}\" verwijderen?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Nog geen voltooide workouts",
"lap": "Baan {{number}}",
"statistics": "Statistieken",
"thisWeek": "Deze week",
"allTime": "Alle tijd",
"personalBests": "Persoonlijke records",
"noDataYet": "Nog geen gegevens",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Workouts",
"history": "Geschiedenis",
"settings": "Instellingen",
"newWorkout": "Nieuwe Workout",
"buildWorkout": "Workout samenstellen",
"startSwim": "Start Zwemmen",
"readyToSwim": "Klaar om te zwemmen",
"swimComplete": "Zwemmen voltooid",
"session": "Sessie",
"sessionDetail": "Sessiedetails"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Instellingen",
"defaultPoolLength": "Standaard badlengte",
"language": "Taal",
"systemLanguage": "Systeemtaal",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Workoutnaam",
"poolLength": "Badlengte",
"stroke": "Slag",
"distanceM": "Afstand (m)",
"reps": "Herhalingen",
"restS": "Rust (s)",
"addSet": "+ Set toevoegen",
"remove": "Verwijderen",
"setNumber": "Set {{number}}",
"startSwim": "Start Zwemmen",
"newWorkout": "+ Nieuwe Workout",
"noWorkoutsYet": "Nog geen workouts",
"tapNewWorkout": "Tik op \"Nieuwe Workout\" om te beginnen",
"morningSwim": "Ochtendzwemmen",
"placeholder": {
"workoutName": "Ochtendzwemmen"
},
"validation": {
"error": "Fout",
"failedToSave": "Workout opslaan mislukt: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Trening wysłany do Watch!",
"openWatchApp": "Otwórz aplikację SwimBuddy na swojej Apple Watch, aby rozpocząć śledzenie.",
"failedToSend": "Nie udało się wysłać",
"couldNotSend": "Nie udało się wysłać treningu do Watch. Uruchamianie bez Watch.",
"startWithoutWatch": "Uruchom bez Watch",
"sendToWatchAndStart": "Wyślij do Watch i rozpocznij",
"syncToWatch": "Trening zostanie zsynchronizowany z Apple Watch",
"trackedOnDevice": "Trening będzie śledzony na tym urządzeniu",
"rest": "Odpoczynek",
"betweenSets": "{{duration}}s odpoczynku między seriami",
"swimComplete": "Pływanie zakończone!",
"greatJob": "Świetna robota! Dziś byłeś w wodzie!",
"loadingSummary": "Ładowanie podsumowania...",
"watchStatus": {
"ready": "Apple Watch gotowa",
"pairedNotInRange": "Watch sparowana, ale poza zasięgiem",
"pairedNotInstalled": "Watch sparowana, aplikacja nie zainstalowana",
"noWatch": "Brak sparowanej Watch"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Gotowe",
"cancel": "Anuluj",
"delete": "Usuń",
"start": "Rozpocznij",
"back": "Wstecz",
"loading": "Ładowanie...",
"error": "Błąd",
"save": "Zapisz",
"saving": "Zapisywanie...",
"yes": "Tak",
"no": "Nie",
"confirm": "Potwierdź",
"ok": "OK",
"distance": "Dystans",
"duration": "Czas",
"workouts": "Treningi",
"sets": "Serie",
"laps": "Długości",
"avgSwolf": "Śr. SWOLF",
"pool": "Basen {{length}}m",
"untitledWorkout": "Trening bez nazwy",
"swimSession": "Sesja pływacka",
"workout": "Trening",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Styl dowolny",
"backstroke": "Grzbietowy",
"breaststroke": "Klasyk",
"butterfly": "Delfin",
"mixed": "Mieszany"
},
"alert": {
"deleteWorkout": "Usuń trening",
"deleteTemplate": "Usuń szablon",
"areYouSure": "Na pewno?",
"deleteConfirm": "Usunąć \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Brak ukończonych treningów",
"lap": "Długość {{number}}",
"statistics": "Statystyki",
"thisWeek": "Ten tydzień",
"allTime": "Cały okres",
"personalBests": "Rekordy osobiste",
"noDataYet": "Brak danych",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Treningi",
"history": "Historia",
"settings": "Ustawienia",
"newWorkout": "Nowy trening",
"buildWorkout": "Stwórz trening",
"startSwim": "Rozpocznij pływanie",
"readyToSwim": "Gotowy do pływania",
"swimComplete": "Pływanie zakończone",
"session": "Sesja",
"sessionDetail": "Szczegóły sesji"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Ustawienia",
"defaultPoolLength": "Domyślna długość basenu",
"language": "Język",
"systemLanguage": "Język systemowy",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Nazwa treningu",
"poolLength": "Długość basenu",
"stroke": "Styl",
"distanceM": "Dystans (m)",
"reps": "Powtórzenia",
"restS": "Odpoczynek (s)",
"addSet": "+ Dodaj serię",
"remove": "Usuń",
"setNumber": "Seria {{number}}",
"startSwim": "Rozpocznij pływanie",
"newWorkout": "+ Nowy trening",
"noWorkoutsYet": "Brak treningów",
"tapNewWorkout": "Stuknij \"Nowy trening\" aby rozpocząć",
"morningSwim": "Poranne pływanie",
"placeholder": {
"workoutName": "Poranne pływanie"
},
"validation": {
"error": "Błąd",
"failedToSave": "Nie udało się zapisać treningu: {{error}}"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"workoutSentToWatch": "Treino enviado para a Watch!",
"openWatchApp": "Abra a aplicação SwimBuddy na sua Apple Watch para iniciar o registo.",
"failedToSend": "Falha no envio",
"couldNotSend": "Não foi possível enviar o treino para a Watch. Iniciando sem Watch.",
"startWithoutWatch": "Iniciar sem Watch",
"sendToWatchAndStart": "Enviar para a Watch e iniciar",
"syncToWatch": "O treino será sincronizado com a sua Apple Watch",
"trackedOnDevice": "O treino será registado neste dispositivo",
"rest": "Descanso",
"betweenSets": "{{duration}}s de descanso entre séries",
"swimComplete": "Natação concluída!",
"greatJob": "Bom trabalho! Esteve na água hoje!",
"loadingSummary": "A carregar resumo...",
"watchStatus": {
"ready": "Apple Watch pronta",
"pairedNotInRange": "Watch associada, mas fora de alcance",
"pairedNotInstalled": "Watch associada, aplicação não instalada",
"noWatch": "Nenhuma Watch associada"
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"done": "Concluído",
"cancel": "Cancelar",
"delete": "Eliminar",
"start": "Iniciar",
"back": "Voltar",
"loading": "A carregar...",
"error": "Erro",
"save": "Guardar",
"saving": "A guardar...",
"yes": "Sim",
"no": "Não",
"confirm": "Confirmar",
"ok": "OK",
"distance": "Distância",
"duration": "Duração",
"workouts": "Treinos",
"sets": "Séries",
"laps": "Comprimentos",
"avgSwolf": "SWOLF médio",
"pool": "Piscina de {{length}}m",
"untitledWorkout": "Treino sem nome",
"swimSession": "Sessão de natação",
"workout": "Treino",
"kcal": "{{count}} kcal",
"meter": "{{count}}m",
"kilometer": "{{count}}km",
"pace": "{{pace}}/100m",
"noPace": "--:--",
"stroke": {
"freestyle": "Estilo livre",
"backstroke": "Costas",
"breaststroke": "Peito",
"butterfly": "Borboleta",
"mixed": "Misto"
},
"alert": {
"deleteWorkout": "Eliminar treino",
"deleteTemplate": "Eliminar modelo",
"areYouSure": "Tem certeza?",
"deleteConfirm": "Eliminar \"{{name}}\"?"
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"noCompletedYet": "Ainda não há treinos concluídos",
"lap": "Comprimento {{number}}",
"statistics": "Estatísticas",
"thisWeek": "Esta semana",
"allTime": "Todo o tempo",
"personalBests": "Recordes pessoais",
"noDataYet": "Ainda sem dados",
"swolf": "SWOLF: {{value}}"
}
+12
View File
@@ -0,0 +1,12 @@
{
"workouts": "Treinos",
"history": "Histórico",
"settings": "Definições",
"newWorkout": "Novo treino",
"buildWorkout": "Criar treino",
"startSwim": "Iniciar natação",
"readyToSwim": "Pronto para nadar",
"swimComplete": "Natação concluída",
"session": "Sessão",
"sessionDetail": "Detalhes da sessão"
}
+7
View File
@@ -0,0 +1,7 @@
{
"settings": "Definições",
"defaultPoolLength": "Comprimento de piscina predefinido",
"language": "Idioma",
"systemLanguage": "Idioma do sistema",
"version": "SwimBuddy v1.0.0"
}
+23
View File
@@ -0,0 +1,23 @@
{
"workoutName": "Nome do treino",
"poolLength": "Comprimento da piscina",
"stroke": "Estilo",
"distanceM": "Distância (m)",
"reps": "Repetições",
"restS": "Descanso (s)",
"addSet": "+ Adicionar série",
"remove": "Remover",
"setNumber": "Série {{number}}",
"startSwim": "Iniciar natação",
"newWorkout": "+ Novo treino",
"noWorkoutsYet": "Ainda não há treinos",
"tapNewWorkout": "Toque em \"Novo treino\" para começar",
"morningSwim": "Natação matinal",
"placeholder": {
"workoutName": "Natação matinal"
},
"validation": {
"error": "Erro",
"failedToSave": "Falha ao guardar o treino: {{error}}"
}
}