feat(i18n): add language picker to settings and sync i18n with persisted preference

- Add language picker (English/Deutsch) to settings page using pill-toggle pattern
- Import useLanguage hook and i18n instance in settings
- Language change persists via updateSetting and calls i18n.changeLanguage
- Add useEffect in RootLayout to sync i18n language with DB setting on load
- Language labels use native names (English, Deutsch) for identification

Phase 34, Plan 04
This commit is contained in:
2026-04-13 18:21:30 +02:00
parent f759dd0fde
commit 46715cc793
2 changed files with 49 additions and 1 deletions

View File

@@ -9,9 +9,16 @@ import {
} from "../hooks/useAuth";
import { useCurrency } from "../hooks/useCurrency";
import { useExportItems, useImportItems } from "../hooks/useItems";
import { useLanguage } from "../hooks/useLanguage";
import { useUpdateSetting } from "../hooks/useSettings";
import { useWeightUnit } from "../hooks/useWeightUnit";
import type { Currency, WeightUnit } from "../lib/formatters";
import i18n from "../lib/i18n";
const LANGUAGES = [
{ value: "en", label: "English" },
{ value: "de", label: "Deutsch" },
];
const UNITS: WeightUnit[] = ["g", "oz", "lb", "kg"];
const CURRENCIES: { value: Currency; label: string }[] = [
@@ -231,6 +238,7 @@ function getSuggestedCurrency(): Currency | null {
function SettingsPage() {
const { t } = useTranslation("settings");
const unit = useWeightUnit();
const language = useLanguage();
const { currency, showConversions } = useCurrency();
const updateSetting = useUpdateSetting();
const { data: auth } = useAuth();
@@ -279,6 +287,36 @@ function SettingsPage() {
)}
<div className="bg-white rounded-xl border border-gray-100 p-5 space-y-6">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-gray-900">{t("language.title")}</h3>
<p className="text-xs text-gray-500 mt-0.5">
{t("language.description")}
</p>
</div>
<div className="flex items-center gap-1 bg-gray-100 rounded-full px-1 py-0.5">
{LANGUAGES.map((lang) => (
<button
key={lang.value}
type="button"
onClick={() => {
updateSetting.mutate({ key: "language", value: lang.value });
i18n.changeLanguage(lang.value);
}}
className={`px-2.5 py-1 text-xs rounded-full transition-colors ${
language === lang.value
? "bg-white text-gray-700 shadow-sm font-medium"
: "text-gray-400 hover:text-gray-600"
}`}
>
{lang.label}
</button>
))}
</div>
</div>
<div className="border-t border-gray-100" />
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-gray-900">{t("weightUnit.title")}</h3>