Commit graph

4 commits

Author SHA1 Message Date
oskar 605bc3d025 refactor(i18n): cała treść landingu i /platforma przez useT()
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>
2026-07-31 16:46:46 +02:00
oskar 13ffb8754b refactor: współdzielony LangSwitcher także w nagłówkach podstron
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>
2026-07-31 16:22:50 +02:00
oskar 3e452191de feat: wybór języka przez URL (/en) — reaktywny hook, przełącznik, hreflang
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>
2026-07-31 15:54:49 +02:00
oskar e0557a7d76 feat: dodaj stronę polityki prywatności (PL+EN) z routingiem
- 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>
2026-06-25 12:53:49 +02:00