Krok 1 z dwóch: przenosi stringi, nie tłumaczy ich. en.ts dostaje komplet
kluczy z polską treścią — angielskie wartości wchodzą osobnym commitem, żeby
diff tłumaczeń dało się czytać zdanie po zdaniu.
- data/content.ts: zostaje sama struktura (id, icon, href). Ikony to komponenty
Reacta i nie mogą wejść do słowników; kolejność i liczba kart są przez to
zdefiniowane raz, a nie duplikowane per język.
- i18n/types.ts + RichText: akapity z <strong>/<em> w środku zdania trzymamy
jako listę przebiegów. Bez dangerouslySetInnerHTML — tłumaczenie nie może
wstrzyknąć znaczników.
- en.ts typowane jako `typeof pl`, więc rozjazd kluczy to błąd builda. Przy
~150 kluczach to główne ryzyko tej zmiany.
- privacy.backToHome -> common.backToHome (używane przez obie podstrony).
Weryfikacja: renderowany tekst porównany z produkcją na wszystkich sześciu
trasach — identyczny, poza linkiem powrotnym na /en/platforma, który był
zahardkodowany po polsku i teraz zgadza się z /en/privacy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Przełącznik siedział w Header.tsx, który renderuje się tylko na landingu —
PrivacyPolicyPage i PlatformPage mają własne <header>. Z /en/privacy i
/en/platforma nie dało się wrócić na PL inaczej niż przez stronę główną
albo ręczną edycję URL-a.
Wyciągnięty do src/components/LangSwitcher.tsx i wstawiony w oba nagłówki
obok linku powrotnego.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Język wynikał z navigator.language liczonego przy każdym renderze, więc nie
dało się go wybrać ani zalinkować. Teraz nośnikiem języka jest ścieżka.
- src/router.ts: usePathname() na useSyncExternalStore + navigate(). pushState
nie emituje zdarzenia, więc bez tego React nie widzi zmiany URL-a.
- i18n: useLang(), localizePath(), stripLangPrefix(), setLang(). useT() czyta
język ze ścieżki i jest reaktywne na nawigację.
- Detekcja przeglądarki tylko przy pierwszym wejściu na "/" i tylko gdy w
localStorage nic nie ma; wynik zapisujemy od razu, więc przekierowanie na
/en zadziała najwyżej raz i nie nadpisze późniejszego wyboru użytkownika.
Deep linki honorujemy bez ruszania.
- App.tsx: jedna tablica tras dla obu języków (/en/privacy -> '/privacy').
Ustawia też document.documentElement.lang, bo index.html ma na sztywno "pl".
- Header: przełącznik PL/EN (desktop + mobile), <button>, nie <a> — link
przeładowałby dokument.
- Hrefy w Header/Footer/PrivacyPolicyPage/PlatformPage przez localizePath,
inaczej /en jest pułapką jednokierunkową: każdy klik wraca na PL.
- index.html: hreflang pl/en/x-default; og:url z gethumanai.com na .pl
(wskazywał na cudzą domenę).
Bez nowych tłumaczeń treści — to osobny krok.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- nginx.conf: SPA fallback (try_files → index.html), bez niego /privacy zwracało 404
- Dockerfile: kopiuje nginx.conf do kontenera
- i18n/pl.ts + en.ts: nowa sekcja `privacy` z 6 podsekcjami (admin, formularz, Umami analytics, cookies, prawa, zmiany)
- PrivacyPolicyPage.tsx: strona polityki używająca useT(), sekcje z kotwicami (controller/contact/analytics/cookies/rights/changes)
- App.tsx: prosty routing po window.location.pathname — /privacy → PrivacyPolicyPage
- Footer.tsx: /privacy i /privacy#cookies zamiast martwych href="#"
Co do sekcji Umami: napisana na podstawie kodu (self-hosted stats.gethumanai.pl, cookie-free). Zweryfikuj treść przed publikacją.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>