This commit is contained in:
2026-04-22 22:00:50 +02:00
parent 16be247ce1
commit e979fbe755
46 changed files with 5302 additions and 274 deletions

View File

@@ -0,0 +1,283 @@
---
phase: 18-en-translation
plan: 01
type: execute
wave: 1
depends_on: ["16-01", "17-01"]
files_modified:
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po (nowy)
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po (nowy, symlink/kopia en_US)
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo (nowy, binarny)
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo (nowy, binarny)
autonomous: false
delegation: off
---
<objective>
## Goal
Dostarczyć kompletne tłumaczenie EN dla pluginu carei-reservation: przetłumaczyć wszystkie 157 msgid z `carei-reservation.pot` na `msgstr` angielskie w pliku `carei-reservation-en_US.po`, skompilować do `.mo`, zduplikować jako `en_GB` (Polylang w WordPress może używać różnych locale EN — pokrywamy oba najczęstsze warianty). Po uploadzie plików UI pluginu na wersji angielskiej przełącza się całkowicie.
## Purpose
Phase 16 dostarczyła infrastrukturę (`__()`, textdomain, `.pot`), Phase 17 dodała bilingual pakiety + mapowanie błędów Softra. **Phase 18 to jedyna faza, która wizualnie przełączy cały plugin na EN.** Do tej pory użytkownik EN widzi oryginalne polskie stringi — brak `.mo` = brak tłumaczeń, `__('Wybierz segment pojazdu', ...)` zwraca polski msgid.
## Output
- `carei-reservation-en_US.po` — 157 msgstr wypełnionych profesjonalnym angielskim tłumaczeniem branży wynajmu samochodów
- `carei-reservation-en_US.mo` — binarny plik zgodny z formatem gettext (magic bytes `0x950412de`, big-endian byte order)
- `carei-reservation-en_GB.po` + `.mo` — identyczna kopia (lub drobne różnice `rental` vs `hire`, `license` vs `licence` — opcjonalne)
- Po załadowaniu przez `load_plugin_textdomain` (już wpięte w Phase 16) — wszystkie `__()`/`esc_html__()` i `wp_localize_script('careiI18n')` zwracają EN w locale `en_*`
</objective>
<context>
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
@.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
@.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md
@wp-content/plugins/carei-reservation/languages/carei-reservation.pot
</context>
<acceptance_criteria>
## AC-1: Plik .po kompletnie przetłumaczony
```gherkin
Given istnieje plik `languages/carei-reservation-en_US.po`
When policzymy niepuste msgstr (`grep -c '^msgstr "[^"]' plik.po`)
Then wszystkie 157 wpisów ma wypełnione msgstr (nie-puste)
And nagłówek zawiera Language: en_US, poprawne Plural-Forms (nplurals=2; plural=(n != 1))
And msgid zawierające placeholdery `%count%`, `%name%`, `%price%`, `%status%` itp. mają je **zachowane w msgstr** (nietknięte te same tokeny)
And msgid zawierające tagi HTML (`<strong>...</strong>`) mają je zachowane
And tłumaczenie zachowuje branżowy ton: `doba` `day`, `oddział` `location`, `pakiet ochronny` `protection package`, `rezerwacja` `reservation`, `zł` `PLN`, itp.
```
## AC-2: Plik .mo poprawny binarnie
```gherkin
Given istnieje plik `languages/carei-reservation-en_US.mo`
When otworzymy go jako binary
Then pierwsze 4 bajty to magic number `0xde120495` (little-endian) LUB `0x950412de` (big-endian)
And zawiera wszystkie tłumaczenia z .po
And daje się poprawnie sparsować przez `gettext` (WordPress i18n czyta go bez błędu)
And jest < 50 KB (typowy rozmiar dla 157 wpisów)
```
## AC-3: EN UI działa po uploadzie
```gherkin
Given administrator ustawił Polylang locale na en_US albo en_GB
When użytkownik otwiera stronę w EN
Then modal rezerwacji ma WSZYSTKIE etykiety po angielsku (segment, dates, location, protection packages, booking summary, success)
And hero search form po angielsku
And widgety (mapa, grid miast, grid oddziałów) po angielsku gdzie są stringi UI
And wp-admin Rezerwacje (dla administratora z EN locale) po angielsku
And błędy z Softra API (dla znanych komunikatów) po angielsku
And pakiety ochronne pokazują wartości z pól `_en` (lub PL fallback gdy puste z Phase 17)
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Translate .pot → carei-reservation-en_US.po</name>
<files>wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po</files>
<action>
1. Przeczytaj `languages/carei-reservation.pot` (157 msgid, ~13.7 KB)
2. Dla każdego wpisu msgid wygeneruj angielskie tłumaczenie w msgstr:
- **Ton:** profesjonalny, branża wynajmu samochodów (car rental)
- **Kluczowe terminy:**
- „doba" / „dób" / „doby" → `day` / `days` (bez skomplikowanej pluralizacji — EN ma tylko one/other)
- „oddział" → `location` (nie `branch` — bardziej rental-naturalne)
- „pakiet ochronny" → `protection package`
- „rezerwacja" → `reservation` (nie `booking` — spójne z nazwą pluginu)
- „Złóż zapytanie o rezerwację" → `Request a reservation` (skrócone, CTA-friendly)
- „klient" → `customer`
- „zł" → `PLN`
- „Wyjazd zagraniczny" → `International travel`
- „Zniesienie udziału" → `Deductible waiver` (jeśli gdzieś zostało)
- „najemca" → `renter`
- „segment pojazdu" → `vehicle segment`
- „miejsce odbioru" / „miejsce zwrotu" → `pickup location` / `return location`
- „politykę prywatności" → `privacy policy`
- „Pakiet SOFT" → `SOFT Package` (nazwy własne bez zmian)
- **Placeholdery:** tokeny typu `%name%`, `%count%`, `%price%`, `%status%`, `%days%`, `%total%`, `%unit%`, `%no%`, `%msg%`, `%perDay%`, `%min%`, `%max%`, `%label%`**ZACHOWAJ DOKŁADNIE** jak w oryginale (nie tłumacz, nie zmieniaj kolejności placeholderów jeśli to zmieniałoby znaczenie; ale możesz zmienić kolejność słów wokół nich)
- **HTML tagi:** `<strong>...</strong>` → ZACHOWAJ
- **%s** (sprintf) → ZACHOWAJ
3. Zapisz jako `wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po` z nagłówkiem:
```
# English (US) translation for Carei Reservation
# This file is distributed under the same license as the Carei Reservation plugin.
msgid ""
msgstr ""
"Project-Id-Version: Carei Reservation 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-22 12:00+0000\n"
"PO-Revision-Date: 2026-04-22 12:00+0000\n"
"Last-Translator: Carei\n"
"Language-Team: English\n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Domain: carei-reservation\n"
```
4. Zachowaj komentarze `#:` z referencjami plik:linia (dla kontekstu tłumacza przy przyszłych zmianach)
5. Sanity check per wpis:
- msgid niepuste → msgstr niepuste
- `%TOKEN%` w msgid → `%TOKEN%` w msgstr (ten sam token)
- `<strong>` w msgid → `<strong>` w msgstr (dokładnie ten sam tag)
Unikaj:
- Dosłownego tłumaczenia słowo-w-słowo (kluczowe są frazy rental, nie słownikowe mapowania)
- Zachowywania polskich diakrytyków w EN (ąćęłńóśźż nie powinny być w msgstr)
- Tłumaczenia nazw własnych (`SOFT`, `PREMIUM`, `Carei`, `Softra`)
- Zmiany placeholderów (`%count%` → `%iloscDni%` = katastrofa w runtime)
</action>
<verify>
1. Plik `carei-reservation-en_US.po` istnieje
2. `grep -c '^msgid "[^"]' plik.po` ≈ 158 (157 + header)
3. `grep -c '^msgstr "[^"]' plik.po` ≈ 158 (wszystkie wypełnione)
4. `grep -cP "[ąćęłńóśźż]" plik.po` → tylko w msgid (oryginalne PL), zero w msgstr
5. Spot-check 10 wpisów: ton angielski branżowy, placeholdery zachowane, HTML zachowany
</verify>
<done>AC-1 satysfakcjonowane.</done>
</task>
<task type="auto">
<name>Task 2: Kompilacja .po → .mo (+ kopia en_GB)</name>
<files>
wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo,
wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po,
wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo
</files>
<action>
**Problem:** `msgfmt` (standardowy kompilator gettext) nie jest dostępny w systemie developera. Python `msgfmt.py` również nie dostępny.
**Rozwiązanie:** Wygeneruj plik `.mo` binarnie programistycznie. Możliwe ścieżki (wybierz dostępną):
1. **Preferowana:** Napisz tymczasowy skrypt Node.js w sandbox używający pakietu `gettext-parser` (dostępny przez npm):
```js
// Wymaga: npm i gettext-parser
const fs = require('fs');
const gp = require('gettext-parser');
const po = fs.readFileSync('carei-reservation-en_US.po');
const parsed = gp.po.parse(po);
const mo = gp.mo.compile(parsed);
fs.writeFileSync('carei-reservation-en_US.mo', mo);
```
2. **Alternatywa PHP:** Napisz własny skrypt PHP implementujący format `.mo` (magic 0x950412de, header 7×uint32, offset tables, string data). Format jest w dokumentacji gettext: https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
- Parsuj `.po` ręcznie (rozpoznaj msgid/msgstr, unescape)
- Zbuduj binarny layout: header → offset/length tables for originals → offset/length tables for translations → string blobs (null-terminated)
- Zapisz jako binary file
3. **Loco Translate (wp-admin):** gdyby powyższe zawiodły — upload `.po` do wp-content/languages/plugins/ i niech Loco Translate w `wp-admin → Loco Translate → Plugins → Carei Reservation → en_US → Sync → Save` wygeneruje `.mo` w server-side.
**Preferuj opcję 1** (Node.js + gettext-parser) — działa deterministycznie w dev environment, bez potrzeby wp-admin.
**Po skompilowaniu en_US:**
- Skopiuj `.po` jako `carei-reservation-en_GB.po` (zmień `Language: en_US\n` → `Language: en_GB\n` w nagłówku)
- Opcjonalnie: drobne poprawki UK-english (license→licence, color→colour, itp.) — na tym etapie zostaw identyczne
- Skompiluj `.po` → `.mo` tym samym skryptem
**Weryfikacja binarnej poprawności `.mo`:**
```bash
# Pierwsze 4 bajty = magic number (little-endian 0x950412de)
xxd -l 4 carei-reservation-en_US.mo
# Powinno pokazać: 0000000: de12 0495 (le) lub 9504 12de (be)
```
</action>
<verify>
1. Pliki istnieją: `en_US.po`, `en_US.mo`, `en_GB.po`, `en_GB.mo` w `languages/`
2. Rozmiar `.mo` między 550 KB
3. Magic number poprawny (xxd/hexdump pierwsze 4 bajty)
4. PHP sanity check (opcjonalny): `php -r "$mo=file_get_contents('path.mo'); echo bin2hex(substr($mo,0,4));"` → `de120495` lub `950412de`
</verify>
<done>AC-2 satysfakcjonowane.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
- `carei-reservation-en_US.po` + `.mo` z 157 przetłumaczonymi wpisami
- `carei-reservation-en_GB.po` + `.mo` jako kopia en_US
- Po uploadzie wszystkie `__()`/`esc_html__()` + `careiI18n` w pluginie zwracają EN przy locale en_*
</what-built>
<how-to-verify>
1. **Deploy:** wypchnij 4 nowe pliki (`carei-reservation-en_US.po`, `.mo`, `en_GB.po`, `.mo`) do `wp-content/plugins/carei-reservation/languages/` na serwer
2. **Cache:** wyczyść cache pluginu (jeśli masz WP Rocket / Autoptimize) + opcache PHP (zwykle wystarczy reboot fpm, albo `wp cache flush`)
3. **Admin panel pakietów:**
- Zaloguj się do wp-admin (jeśli twoja konto admina jest PL → zostawcie PL dla admina, albo zmień WP user locale na EN w Users → Profile → Language: English (United States))
- `Rezerwacje → Pakiety ochronne` — labele powinny być po EN (jeśli admin locale = EN)
- Uzupełnij pola EN dla SOFT i PREMIUM (np. `SOFT Protection` + `Basic damage coverage with 2000 PLN deductible`; `PREMIUM Protection` + `Full damage waiver, zero deductible`)
- Zapisz
4. **Frontend EN — modal rezerwacji:**
- Przełącz Polylang switcher na EN
- Otwórz stronę z przyciskiem → przycisk pokazuje `Request a reservation` (lub podobne)
- Modal otwiera się, WSZYSTKIE labele po angielsku: `Vehicle segment`, `From`, `To`, `Pickup location`, `Return location`, `Protection packages`, `Additional options`, `International travel`, `First name`, `Last name`, `Email`, `Phone`, `I agree to the privacy policy`, `Send request`
- Pakiety ochronne: `SOFT Protection` / `PREMIUM Protection` z angielskim opisem (z Phase 17)
- Komunikaty walidacji (spróbuj wysłać pusty form) po EN: `Enter first name`, `Enter a valid email`, itp.
- Podsumowanie → `Reservation summary`, `Subtotal`, `VAT`, `Total`, `Confirm reservation`
- Success: `Reservation confirmed`, `Order number: X`
5. **Hero search form:** `Vehicle segment`, `From`, `To`, `Pickup location`, `Check availability`
6. **Widgety:**
- Mapa Polski: tooltipy `Location: {city}`, `ul. {street}` → po EN
- Grid miast, grid oddziałów → po EN
7. **Błędy Softra:**
- Spróbuj zarezerwować niedostępny pojazd / nieprawidłową datę — komunikat po EN (z słownika Phase 17 via `__()`)
8. **DevTools sanity:**
- `window.careiI18n.selectSegment` → `"Select vehicle segment"` (lub podobne)
- `window.careiI18n.dayOne` → `"day"`, `dayOther` → `"days"` (jeśli dodałeś `dayOther` jako en-plural)
- Network: `/wp-json/carei/v1/protection-packages?lang=en` → response z EN treścią
9. **Powrót do PL:**
- Przełącz Polylang na PL
- Wszystko wraca do polskiego bez regresji (oryginał zachowany)
**Kryterium przejścia:** cała UI pluginu w EN (modal + hero + widgety + admin + error messages). Zero polskich literałów przy locale EN (poza treściami z DB które admin zostawił puste → fallback PL z Phase 17).
**Znane limitacje (NIE blokują approve):**
- Treści stron Elementora (nagłówki hero, sekcje marketingowe) — tłumaczone osobno przez Polylang addon (poza scope tej fazy)
- Menu, footer, inne treści WP — Polylang / Polylang Strings Translation (poza scope)
</how-to-verify>
<resume-signal>Napisz "approved" aby zamknąć Milestone v0.7, albo wskaż stringi które pozostały po polsku pomimo EN locale.</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Żaden plik PHP/JS pluginu (Phase 1617 już to załatwiły)
- Plik `.pot` (baseline — nie nadpisujemy bazowego template)
- Textdomain, mechanizm `load_plugin_textdomain` (Phase 16)
- Panel admina pakietów — tylko DANE w bazie mogą być wypełnione przez admina (to nie zmiana kodu)
- `carei-reservation.css` — styling niezależny od języka
- `mu-plugins/fix-sprintf-global.php` — dalej potrzebny dla Polylang addon
## SCOPE LIMITS
- Nie tłumaczymy innych pluginów (tylko carei-reservation)
- Nie tłumaczymy Elementora ani treści stron (Polylang addon)
- Nie tłumaczymy nazw krajów w sekcji wyjazdu zagranicznego (dane biznesowe COUNTRY_FLAGS — pozostają po polsku bo backend tak zwraca)
- Nie tłumaczymy slugów URL, permalink structures
- Nie generujemy dodatkowych locale (fr, de, itp.) — tylko en_US + en_GB
- Nie zmieniamy generatora .pot ani pipeline-u i18n
</boundaries>
<verification>
Przed zamknięciem planu:
- [ ] `carei-reservation-en_US.po` istnieje, 157 wpisów, msgstr wszystkie wypełnione
- [ ] `carei-reservation-en_US.mo` istnieje, magic number poprawny
- [ ] `carei-reservation-en_GB.po` + `.mo` — kopia en_US
- [ ] Brak polskich diakrytyków w msgstr
- [ ] Placeholdery `%TOKEN%` zachowane 1:1 między msgid i msgstr
- [ ] HTML tagi zachowane w msgstr
- [ ] Human-verify — pełen flow EN bez regresji PL
- [ ] AC-1, AC-2, AC-3 pass
</verification>
<success_criteria>
- Task 12 ukończone
- Checkpoint zatwierdzony
- Milestone v0.7 — 100% complete
- Plugin carei-reservation pełnoprawnie dwujęzyczny (PL + EN)
</success_criteria>
<output>
Po zakończeniu: `.paul/phases/18-en-translation/18-01-SUMMARY.md`
Następnie: `/paul:complete-milestone v0.7`
</output>

View File

@@ -0,0 +1,168 @@
---
phase: 18-en-translation
plan: 01
subsystem: i18n
tags: [gettext, po, mo, translation, flatpickr, bilingual]
requires:
- phase: 16-i18n-plugin-refactor
provides: .pot (157 msgid) + textdomain carei-reservation
- phase: 17-bilingual-packages-and-softra-errors
provides: bilingual pakiety + mapowanie Softra przez __()
provides:
- carei-reservation-en_US.po + .mo (158 wpisów przetłumaczonych)
- carei-reservation-en_GB.po + .mo (kopia en_US)
- PHP mini-kompilator po2mo.php (bez potrzeby msgfmt)
- Flatpickr jako cross-browser date picker z PL/EN locale (scope addition)
- Kompaktowy CSS theme dla flatpickr w kolorach Carei
affects: []
tech-stack:
added:
- Flatpickr 4.6.13 (CDN jsdelivr, enqueued z dependencies)
patterns:
- "MO compilation: własny parser+packer PHP bez msgfmt/Python"
- "Flatpickr static:true dla modala (popup w container) + default dla hero"
- "disableMobile:true dla spójności UX PL/EN na wszystkich urządzeniach"
key-files:
created:
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo
modified:
- wp-content/plugins/carei-reservation/carei-reservation.php
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
- wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
key-decisions:
- "Tłumaczenie przez agenta z pre-zdefiniowanym słownikiem terminów branżowych (~50 mapowań PL→EN car rental)"
- "Własny kompilator PHP zamiast msgfmt/Loco — deterministyczny, niezależny od środowiska"
- "Flatpickr z CDN jsdelivr — brak lokalnych plików, szybki deploy; fallback do native picker jeśli CDN padnie"
- "static:true dla modala — rozwiązuje konflikt focus-trap + z-index"
- "disableMobile:true — jednolity UX zamiast native iOS spinner / Android Material"
- "en_GB = kopia en_US — brak realnej potrzeby różnicowania UK/US na tym etapie"
patterns-established:
- "Własny po2mo compiler w PHP (stored w ~/temp) — reusable dla przyszłych tłumaczeń"
- "CSS override per-klasa flatpickr (compact height/width/spacing) w kolorach Carei #2F2482"
duration: ~40min
started: 2026-04-22
completed: 2026-04-22
---
# Phase 18 Plan 01: EN translation (.po/.mo) + QA — Summary
**Plugin carei-reservation dostarczony w wersji dwujęzycznej: 158 wpisów przetłumaczonych na EN, skompilowanych do .mo (en_US + en_GB), plus cross-browser Flatpickr jako date picker z locale PL/EN. Milestone v0.7 — 100% complete.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~40min |
| Tasks | 2 auto + 1 human-verify + 1 scope addition (Flatpickr) |
| Files created | 4 (.po + .mo × 2 locale) |
| Files modified | 3 (bootstrap PHP, JS, CSS) |
| Delegation | 1 agent (tłumaczenie .pot → .po) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Plik .po kompletnie przetłumaczony | Pass | 158 msgstr, placeholdery zachowane, HTML OK, zero PL diakrytyków |
| AC-2: Plik .mo poprawny binarnie | Pass | Magic 0x950412de, version 0, N=158, 9455 bytes |
| AC-3: EN UI działa po uploadzie | Pass | User confirmed "approved" po pełnym teście |
## Accomplishments
- **158 wpisów PL→EN** przez agenta z uzgodnionym słownikiem terminów rental (`doba→day`, `oddział→location`, `zł→PLN`, `pakiet ochronny→protection package`, etc.)
- **Własny PHP `.mo` compiler** (po2mo.php, ~150 linii) — parsuje .po, sortuje, pakuje binarnie wg gettext spec
- **en_US + en_GB** — dwa locale EN pokryte (Polylang może używać dowolnego)
- **Flatpickr scope addition:** CDN enqueue + JS init + kompaktowy CSS w kolorach Carei. Modal z `static:true` (popup w container, bypass focus-trap). Hero z default renderowaniem. Jednolity UX desktop + mobile (`disableMobile:true`).
- **Weryfikacja binarna `.mo`** przez PHP: `Magic: 0x950412de, Version: 0, N: 158`
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `languages/carei-reservation-en_US.po` | Created | 158 wpisów PL→EN |
| `languages/carei-reservation-en_US.mo` | Created | Binarka gettext, 9455 bytes |
| `languages/carei-reservation-en_GB.po` | Created | Kopia en_US z `Language: en_GB\n` |
| `languages/carei-reservation-en_GB.mo` | Created | Skompilowana en_GB |
| `carei-reservation.php` | Modified | Flatpickr enqueue (CDN + pl locale) + deps |
| `assets/js/carei-reservation.js` | Modified | `initDatePickers()` + static:true dla modal + graceful fallback |
| `assets/css/carei-reservation.css` | Modified | Kompaktowy flatpickr theme w kolorach Carei |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Własny po2mo.php zamiast msgfmt | msgfmt/Python msgfmt niedostępne w dev env; Loco Translate wymaga wp-admin flow | Deterministic compilation, reusable dla przyszłych locale (fr, de) |
| Flatpickr scope addition | User zgłosił potrzebę tłumaczenia kalendarza natywnego (browser używa OS locale) — flatpickr jedyne sensowne rozwiązanie | +40KB JS (CDN), ale spójne UX + locale-aware |
| CDN jsdelivr | Szybki deploy, brak zarządzania plikami lokalnymi | Dependency na CDN; graceful fallback do native picker |
| `static:true` dla modal | Default popup w body → konflikt z focus-trap + z-index modala | Picker w containerze inputa — kompatybilne z modalem |
| `disableMobile:true` | Native mobile (iOS spinner, Android Material) ignoruje strony locale | Jednolity UX PL/EN niezależnie od OS użytkownika |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 2 | Flatpickr popup ukryty w modalu (static:true), native mobile picker (disableMobile:true) |
| Scope additions | 1 | Flatpickr integracja (cross-browser date picker z i18n) — user request podczas apply |
| Deferred | 0 | — |
### Scope additions
**1. [UX] Flatpickr date picker z i18n**
- Found during: Task 3 (human-verify) — user zapytał o tłumaczenie kalendarza
- Problem: Natywny `<input type="datetime-local">` używa locale OS przeglądarki, ignoruje WP locale
- Fix: Integracja Flatpickr 4.6.13 z CDN — enqueue + init per input + kompaktowy CSS theme
- Files: carei-reservation.php, carei-reservation.js, carei-reservation.css
- Verification: User "jest ok" po kompaktowym themie; PL kalendarz, EN kalendarz, oba na desktop + mobile
### Auto-fixed Issues
**1. [Modal] Flatpickr popup nie otwierał się w modalu**
- Issue: Default append do body + focus-trap z Phase 4 → picker focus tracony przed interakcją
- Fix: `static: true` w opts — popup renderowany wewnątrz `.carei-form__date-wrap`
- Verification: User confirmed kalendarz otwiera się w modalu po zmianie
**2. [Mobile] Inny kalendarz na mobile vs desktop**
- Issue: Flatpickr default `disableMobile: false` → na mobile fallback do native OS picker (iOS spinner / Android Material)
- Fix: `disableMobile: true` — flatpickr wszędzie, jednolity wygląd + locale
- Verification: User confirmed "na obu jest flatpickr"
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| msgfmt niedostępny lokalnie | Własny PHP kompilator (po2mo.php) — reusable |
| Modal calendar nie otwiera | static:true flatpickr option |
| Mobile inny picker | disableMobile:true |
| Hero kalendarz po polsku mimo EN | Dodanie hero inputs do initDatePickers() (wcześniej tylko modal) |
## Next Phase Readiness
**Milestone v0.7 COMPLETE** — plugin carei-reservation pełnoprawnie dwujęzyczny (PL + EN):
- Infrastruktura: textdomain + __() + careiI18n (Phase 16)
- Bilingual dane: pakiety pól _en + mapowanie Softra errors (Phase 17)
- Tłumaczenia: .po/.mo dla en_US + en_GB (Phase 18)
- UX: Flatpickr cross-browser + locale-aware (Phase 18 scope addition)
**Out of scope dla kolejnych milestones:**
- Tłumaczenie treści stron Elementora (Polylang Automatic Translate Addon)
- Menu, footer, theme stringi (Polylang String Translation)
- Nazwy miast/krajów (dane biznesowe z API Softra)
- Inne locale (fr, de) — dodaje się przez sam `.po/.mo` bez zmian w kodzie
**Blockers:** None.
---
*Phase: 18-en-translation, Plan: 01*
*Completed: 2026-04-22*