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
+10 -7
View File
@@ -1,5 +1,6 @@
import { useCallback, useState } from "react";
import { View, Text, Pressable, FlatList, Alert } from "react-native";
import { useTranslation } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
import { useFocusEffect } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
@@ -20,6 +21,8 @@ type WorkoutRow = {
};
export function HistoryScreen() {
const { t } = useTranslation("history");
const { t: tc } = useTranslation("common");
const navigation = useNavigation<NativeStackNavigationProp<HistoryStackParamList>>();
const [workouts, setWorkouts] = useState<WorkoutRow[]>([]);
const [refreshing, setRefreshing] = useState(false);
@@ -42,10 +45,10 @@ export function HistoryScreen() {
};
const onDelete = (id: string) => {
Alert.alert("Delete Workout", "Are you sure?", [
{ text: "Cancel", style: "cancel" },
Alert.alert(tc("alert.deleteWorkout"), tc("alert.areYouSure"), [
{ text: tc("cancel"), style: "cancel" },
{
text: "Delete",
text: tc("delete"),
style: "destructive",
onPress: async () => {
await deleteWorkout(id);
@@ -74,7 +77,7 @@ export function HistoryScreen() {
onRefresh={onRefresh}
ListEmptyComponent={
<View className="py-12 items-center">
<Text className="text-slate-400 text-lg">No completed workouts yet</Text>
<Text className="text-slate-400 text-lg">{t("noCompletedYet")}</Text>
</View>
}
renderItem={({ item: [date, rows] }) => (
@@ -83,7 +86,7 @@ export function HistoryScreen() {
{date}
</Text>
{rows.map((w) => (
<SwipeableRow key={w.id} onDelete={() => onDelete(w.id)}>
<SwipeableRow key={w.id} onDelete={() => onDelete(w.id)} deleteLabel={tc("delete")}>
<Pressable
onPress={() => navigation.navigate("SessionDetail", { sessionId: w.id })}
className="p-4"
@@ -91,12 +94,12 @@ export function HistoryScreen() {
<View className="flex-row justify-between items-center">
<View>
<Text className="font-semibold text-slate-900">
{w.name || "Swim Session"}
{w.name || tc("swimSession")}
</Text>
<Text className="text-slate-500 text-sm">
{formatDistance(w.totalDistance ?? 0)}
{w.totalDuration ? ` · ${formatTime(w.totalDuration)}` : ""}
{w.calories ? ` · ${Math.round(w.calories)} kcal` : ""}
{w.calories ? ` · ${tc("kcal", { count: Math.round(w.calories) })}` : ""}
</Text>
</View>
<Text className="text-slate-400 text-xs">