feat(i18n): extract strings from navigation, dialogs, onboarding, settings, and login

- Add useTranslation() to TopNav, BottomTabBar, FabMenu, UserMenu
- Internationalize ConfirmDialog, AuthPromptModal, ExternalLinkDialog
- Extract all onboarding flow strings (Welcome, HobbyPicker, ItemBrowser, Review, Done)
- Internationalize settings page (weight unit, currency, API keys, import/export)
- Internationalize login page and root error boundary
- All dialogs in __root.tsx use t() for UI chrome

Phase 34, Plan 02 (core navigation and global UI)
This commit is contained in:
2026-04-13 18:19:29 +02:00
parent 8c0fb31df2
commit 672b17fd13
15 changed files with 123 additions and 98 deletions

View File

@@ -1,5 +1,6 @@
import { Link, useMatchRoute } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { useTranslation } from "react-i18next";
import { useAuth } from "../hooks/useAuth";
import { LucideIcon } from "../lib/iconData";
import { useUIStore } from "../stores/uiStore";
@@ -26,6 +27,7 @@ function TabItemWrapper({ icon, label, isActive }: TabItemProps) {
}
export function BottomTabBar() {
const { t } = useTranslation();
const { data: auth } = useAuth();
const isAuthenticated = !!auth?.user;
const openCatalogSearch = useUIStore((s) => s.openCatalogSearch);
@@ -46,7 +48,7 @@ export function BottomTabBar() {
<div className="flex justify-around">
{/* Home tab — always a Link */}
<Link to="/">
<TabItemWrapper icon="house" label="Home" isActive={isHome} />
<TabItemWrapper icon="house" label={t("nav.home")} isActive={isHome} />
</Link>
{/* Collection tab — Link if authenticated, button if anonymous */}
@@ -54,7 +56,7 @@ export function BottomTabBar() {
<Link to="/collection">
<TabItemWrapper
icon="package"
label="Collection"
label={t("nav.collection")}
isActive={isCollection}
/>
</Link>
@@ -62,7 +64,7 @@ export function BottomTabBar() {
<button type="button" onClick={openAuthPrompt}>
<TabItemWrapper
icon="package"
label="Collection"
label={t("nav.collection")}
isActive={isCollection}
/>
</button>
@@ -71,17 +73,17 @@ export function BottomTabBar() {
{/* Setups tab — Link if authenticated, button if anonymous */}
{isAuthenticated ? (
<Link to="/setups">
<TabItemWrapper icon="layers" label="Setups" isActive={isSetups} />
<TabItemWrapper icon="layers" label={t("nav.setups")} isActive={isSetups} />
</Link>
) : (
<button type="button" onClick={openAuthPrompt}>
<TabItemWrapper icon="layers" label="Setups" isActive={isSetups} />
<TabItemWrapper icon="layers" label={t("nav.setups")} isActive={isSetups} />
</button>
)}
{/* Search tab — always a button, opens CatalogSearchOverlay */}
<button type="button" onClick={() => openCatalogSearch("collection")}>
<TabItemWrapper icon="search" label="Search" isActive={false} />
<TabItemWrapper icon="search" label={t("nav.search")} isActive={false} />
</button>
</div>
</motion.div>