# Internationalisation (i18n) — Design Spec **Date:** 2026-08-10 **Locales:** FR, EN, ES, IT **Library:** next-intl **Default locale (fallback):** en --- ## Scope Add multilingual support to the Ombrora-YTDLP Next.js frontend. The backend (API routes, worker, Prisma) is out of scope — no changes there. --- ## Routing All UI pages move under a `[locale]` dynamic segment. Every URL gains a locale prefix. | Before | After | |--------|-------| | `/` | `/en`, `/fr`, `/es`, `/it` | | `/status/[uuid]` | `/en/status/[uuid]`, `/fr/status/[uuid]`, etc. | A visitor hitting `/` is redirected by the middleware to the detected locale (or `en` if undetectable/unsupported). API routes (`/api/*`) are unaffected. --- ## File Structure ``` src/ app/ [locale]/ layout.tsx # replaces src/app/layout.tsx page.tsx # replaces src/app/page.tsx status/ [uuid]/ page.tsx # replaces src/app/status/[uuid]/page.tsx api/ # unchanged components/ SubmitForm.tsx # updated to use useTranslations StatusView.tsx # updated to use useTranslations LanguageSwitcher.tsx # new middleware.ts # new — locale detection + redirect i18n/ routing.ts # locales + defaultLocale definition request.ts # next-intl server config messages/ en.json fr.json es.json it.json ``` --- ## Locale Detection (middleware) `next-intl/middleware` reads the `Accept-Language` request header and redirects to the best matching locale. If no match among `[fr, en, es, it]`, falls back to `en`. The detected locale is stored in the URL path (not a cookie, not a header). Switching language updates the URL. --- ## Translation Files One JSON file per locale. All UI strings extracted from `SubmitForm.tsx`, `StatusView.tsx`, and the two page files. Keys are namespaced by page/component: ```json { "home": { "title": "...", "urlLabel": "...", "format": "...", "quality": "...", "subtitles": "...", "advanced": "...", "submit": "...", "submitting": "...", "errorRateLimit": "...", "errorGeneric": "..." }, "status": { "title": "...", "notFound": "...", "loading": "...", "pending": "...", "processing": "...", "polling": "...", "ready": "...", "download": "...", "linkExpires": "...", "failed": "...", "deleted": "...", "unknown": "..." } } ``` `yt-dlp` error output (`errorMsg`) is not translated — it stays as-is from the process stderr. --- ## LanguageSwitcher Component Client component placed in the `[locale]/layout.tsx`, visible on all pages. Displays four emoji-flag buttons: 🇫🇷 🇺🇸 🇪🇸 🇮🇹 On click, uses `next-intl`'s `useRouter` + `usePathname` to call `router.replace(pathname, { locale: selectedLocale })`, which swaps only the locale prefix while keeping the rest of the path (e.g. `/fr/status/uuid` → `/en/status/uuid`). Active locale button is visually distinguished (e.g. opacity or underline). --- ## Metadata & HTML lang `[locale]/layout.tsx` sets `` dynamically. The page `