update
This commit is contained in:
165
.paul/phases/15-remove-softra-insurance/15-01-PLAN.md
Normal file
165
.paul/phases/15-remove-softra-insurance/15-01-PLAN.md
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
phase: 15-remove-softra-insurance
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Usunąć z modala rezerwacji wyświetlanie pozycji ubezpieczeniowych pobieranych z Softra API (np. "Zniesienie udziału własnego"). W sekcji "Pakiety ochronne" pozostają wyłącznie kafelki SOFT/PREMIUM zarządzane w panelu WP (Phase 13).
|
||||
|
||||
## Purpose
|
||||
Po wdrożeniu Phase 13 (pakiety WP) w sekcji "Pakiety ochronne" równolegle wyświetlały się dwa źródła: (1) kafelki WP (SOFT/PREMIUM), (2) pozycje z Softra pricelist rozpoznawane heurystyką nazwy ("zniesienie"/"insurance"). Powoduje to dublowanie oferty i konflikt z polityką cenową Carei. Zostawiamy jedno źródło prawdy — panel WP.
|
||||
|
||||
## Output
|
||||
- Dynamiczny kontener `#carei-insurance-container` usunięty z szablonu widgetu
|
||||
- Logika JS klasyfikująca i renderująca `insuranceItems` usunięta
|
||||
- Pozycje Softra rozpoznane jako „zniesienie/insurance” są **odfiltrowywane całkowicie** (nie trafiają ani do kafelków, ani do `extraItems`, ani do podsumowania)
|
||||
- Pakiety WP (SOFT/PREMIUM) działają bez zmian
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||
@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Brak renderowania pozycji Softra-insurance
|
||||
```gherkin
|
||||
Given użytkownik otworzył modal rezerwacji i wybrał pojazd/daty
|
||||
When załaduje się lista dodatków z Softra pricelist zawierająca pozycję "Zniesienie udziału własnego"
|
||||
Then pozycja ta NIE pojawia się w sekcji "Pakiety ochronne"
|
||||
And NIE pojawia się w sekcji "Opcje dodatkowe"
|
||||
And w DOM nie istnieje element `#carei-insurance-container`
|
||||
```
|
||||
|
||||
## AC-2: Pakiety WP działają bez zmian
|
||||
```gherkin
|
||||
Given w panelu WP są zdefiniowane pakiety SOFT i PREMIUM z ceną/dobę
|
||||
When użytkownik otworzy modal
|
||||
Then w sekcji "Pakiety ochronne" widoczne są wyłącznie kafelki SOFT i PREMIUM
|
||||
And zaznaczenie pakietu dodaje wiersz do podsumowania i do `grandTotal` (bez zmian względem Phase 13)
|
||||
```
|
||||
|
||||
## AC-3: Brak wycieku do podsumowania i submit payload
|
||||
```gherkin
|
||||
Given użytkownik przechodzi do podsumowania bez zaznaczania żadnego pakietu WP
|
||||
When pricelist zawierał pozycję „Zniesienie udziału"
|
||||
Then pozycja NIE pojawia się w tabeli podsumowania
|
||||
And nie jest wysyłana w `priceItems` w `makebooking`
|
||||
And całkowity koszt nie zawiera składnika ubezpieczenia Softra
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Usunięcie kontenera insurance z szablonu widgetu</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-elementor-widget.php</files>
|
||||
<action>
|
||||
W `class-elementor-widget.php` (ok. linia 158–161) usunąć blok:
|
||||
- `<div class="carei-form__protection-divider" aria-hidden="true"></div>`
|
||||
- `<div class="carei-form__row" id="carei-insurance-container">...komentarz...</div>`
|
||||
Zachować strukturę sekcji „Pakiety ochronne" z kontenerem `#carei-protection-packages-container` (kafelki SOFT/PREMIUM).
|
||||
Unikać: usuwania dividerów sekcji zewnętrznej ani innych pól formularza. Zmieniamy wyłącznie divider wewnętrzny + kontener insurance.
|
||||
</action>
|
||||
<verify>
|
||||
grep -n "carei-insurance-container" wp-content/plugins/carei-reservation/includes/class-elementor-widget.php → brak wyników.
|
||||
Ręcznie: otwórz modal, sekcja „Pakiety ochronne" pokazuje tylko kafelki WP.
|
||||
</verify>
|
||||
<done>AC-1 satysfakcjonowane: kontener nie istnieje w DOM.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Usunięcie logiki insurance w carei-reservation.js</name>
|
||||
<files>wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
|
||||
<action>
|
||||
1. Usunąć zmienną `insuranceContainer` z deklaracji (ok. linia 82) oraz jej przypisanie w init (`insuranceContainer = document.getElementById('carei-insurance-container');` ok. linia 107).
|
||||
2. W funkcji renderującej dodatki (ok. linie 503–525):
|
||||
- Usunąć lokalną zmienną `insuranceItems` i blok `if (insuranceContainer) { ... }`.
|
||||
- Zastąpić heurystykę „dopasuj do insurance LUB extras" filtrowaniem typu **drop** — pozycje zawierające w nazwie `zniesienie` lub `insurance` (case-insensitive) mają być **pomijane całkowicie** (nie trafiają do `extraItems`).
|
||||
- Pozostałe pozycje trafiają jak dotychczas do `extraItems` i są renderowane w `extrasContainer`.
|
||||
3. Sprawdzić, że przy budowaniu payloadu (`priceItems` / `makebooking`) nie ma osobnej ścieżki pobierającej z `insuranceContainer` — jeśli jest, usunąć.
|
||||
Unikać: zmian w logice Phase 13 (renderProtectionPackages, onProtectionCardClick, getSelectedProtectionPayload) — ten kod zostaje nietknięty. Nie usuwać `extrasContainer`.
|
||||
</action>
|
||||
<verify>
|
||||
grep -nE "insuranceContainer|insuranceItems|carei-insurance-container" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js → brak wyników.
|
||||
grep -nE "zniesienie|insurance" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js → tylko w filtrze drop.
|
||||
</verify>
|
||||
<done>AC-1, AC-3 satysfakcjonowane: pozycje Softra-insurance są dropowane przed renderem i przed budową payloadu.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- Usunięty kontener `#carei-insurance-container` i jego divider w szablonie PHP.
|
||||
- Usunięta zmienna + render logic `insuranceItems` w JS; pozycje Softra „Zniesienie udziału"/„insurance" są pomijane.
|
||||
- Pakiety WP (SOFT/PREMIUM) bez zmian.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Wypchnij przez SFTP (albo zweryfikuj lokalnie, jeśli dostępny).
|
||||
2. Otwórz stronę z modalem rezerwacji (carei.pagedev.pl) z DevTools.
|
||||
3. Uzupełnij krok 1 formularza tak, aby pricelist z Softra został pobrany (wybór dat/oddziału/klasy).
|
||||
4. Sprawdź sekcję „Pakiety ochronne":
|
||||
- Widoczne TYLKO kafelki SOFT i PREMIUM z panelu WP.
|
||||
- Brak pozycji „Zniesienie udziału własnego" ani żadnej innej z Softra.
|
||||
5. Sprawdź sekcję „Opcje dodatkowe":
|
||||
- Pozycje Softra są (fotelik, GPS itd.), ale brak pozycji zawierających słowo „zniesienie" lub „insurance".
|
||||
6. W DevTools → Elements: `document.getElementById('carei-insurance-container')` → `null`.
|
||||
7. Przejdź do podsumowania bez zaznaczania pakietu WP:
|
||||
- W tabeli brak wiersza ubezpieczenia Softra.
|
||||
- `grandTotal` = suma Softra (bez insurance) (+ pakiet WP jeśli zaznaczony).
|
||||
8. Zaznacz pakiet SOFT → kafelek zaznacza się, wiersz pakietu pojawia się w podsumowaniu, total rośnie o cena×doba.
|
||||
</how-to-verify>
|
||||
<resume-signal>Napisz "approved" aby zamknąć plan, lub opisz problemy do poprawy.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- `includes/class-admin-panel.php` — panel WP pakietów (Phase 13 stable)
|
||||
- `includes/class-rest-proxy.php` endpoint `/protection-packages`
|
||||
- Logika Phase 13 w JS: `loadProtectionPackages`, `renderProtectionPackages`, `onProtectionCardClick`, `getSelectedProtectionPayload`, sekcja podsumowania z pakietem WP
|
||||
- Kontener `#carei-protection-packages-container` i jego style
|
||||
- Inne sekcje formularza (segment, daty, lokalizacja, wyjazd zagraniczny, dane osobowe)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie zmieniamy heurystyki klasyfikacji innych pozycji extras
|
||||
- Nie dotykamy backend (PHP) poza szablonem widgetu
|
||||
- Nie ruszamy CSS — osierocone reguły `.carei-form__protection-divider` w CSS mogą zostać (brak użycia == brak efektu)
|
||||
- Nie zmieniamy wywołań API Softra ani żadnych endpointów
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed zamknięciem planu:
|
||||
- [ ] `grep -n "carei-insurance-container"` → 0 wyników w `includes/` oraz `assets/js/`
|
||||
- [ ] `grep -n "insuranceContainer"` w JS → 0 wyników
|
||||
- [ ] Modal otwiera się, sekcja „Pakiety ochronne" pokazuje tylko SOFT/PREMIUM
|
||||
- [ ] Opcje dodatkowe: brak pozycji „Zniesienie udziału"
|
||||
- [ ] Podsumowanie i makebooking payload wolne od Softra-insurance
|
||||
- [ ] AC-1, AC-2, AC-3 przeszły human-verify
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie tasks zakończone
|
||||
- Checkpoint human-verify zatwierdzony ("approved")
|
||||
- Brak regresji w Phase 13 (pakiety WP) i w pobieraniu extras Softra
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md`
|
||||
</output>
|
||||
111
.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
Normal file
111
.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
phase: 15-remove-softra-insurance
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [elementor, reservation-modal, pricelist, protection-packages, polylang-ready]
|
||||
|
||||
requires:
|
||||
- phase: 13-protection-packages
|
||||
provides: WP-managed SOFT/PREMIUM packages + /protection-packages REST endpoint
|
||||
|
||||
provides:
|
||||
- Single source of truth for protection packages (panel WP)
|
||||
- Drop-filter for Softra-insurance items in pricelist rendering
|
||||
|
||||
affects: future-extras-work
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Pricelist drop-filter: pozycje zawierające ubezp/ochrony/zniesienie/insurance są pomijane przed renderem"
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
|
||||
key-decisions:
|
||||
- "Drop zamiast kategoryzacji: pozycje Softra-insurance są całkowicie pomijane (nie trafiają do extras ani payloadu)"
|
||||
|
||||
patterns-established:
|
||||
- "Jedno źródło prawdy dla pakietów ochronnych: panel WP (Phase 13)"
|
||||
|
||||
duration: ~10min
|
||||
started: 2026-04-22
|
||||
completed: 2026-04-22
|
||||
---
|
||||
|
||||
# Phase 15 Plan 01: Remove Softra-insurance z modala — Summary
|
||||
|
||||
**W sekcji „Pakiety ochronne" modala rezerwacji pozostają wyłącznie kafelki SOFT/PREMIUM z panelu WP; pozycje ubezpieczeniowe z Softra API są pomijane przed renderem i przed budową payloadu.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Tasks | 2 auto + 1 human-verify completed |
|
||||
| Files modified | 2 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Brak renderowania pozycji Softra-insurance | Pass | Kontener `#carei-insurance-container` usunięty z DOM; filtr drop w JS |
|
||||
| AC-2: Pakiety WP działają bez zmian | Pass | Phase 13 logic nietknięta |
|
||||
| AC-3: Brak wycieku do podsumowania i payloadu | Pass | Pozycje są dropowane przed `extraItems.push` — nie trafiają do summary ani do `priceItems` |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Usunięty kontener `#carei-insurance-container` + wewnętrzny divider w szablonie Elementor widget
|
||||
- Usunięta zmienna `insuranceContainer` (deklaracja + init) z `carei-reservation.js`
|
||||
- Heurystyka nazewnicza `ubezp|ochrony|zniesienie|insurance` zmieniona z „kategoryzuj jako insurance" na „drop całkowicie"
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` | Modified | Usunięty blok insurance-container + divider (linie ~158–161) |
|
||||
| `wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` | Modified | Usunięta zmienna + render insuranceContainer; pozycje Softra-insurance dropowane |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Drop zamiast oddzielnej kategorii | Phase 13 już dostarcza pakiety WP — Softra-insurance staje się redundantny i konfliktuje z polityką cenową | Prostsza logika, brak sekcji do utrzymania, brak konfliktu dwóch źródeł |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 0 | — |
|
||||
| Scope additions | 0 | — |
|
||||
| Deferred | 0 | — |
|
||||
|
||||
**Total impact:** Plan wykonany dokładnie jak zaplanowano.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
None.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Modal rezerwacji wyświetla jedno źródło pakietów ochronnych (WP)
|
||||
- Payload `makebooking` czysty — bez Softra-insurance
|
||||
|
||||
**Concerns:**
|
||||
- Osierocona reguła CSS `.carei-form__protection-divider` pozostała w pliku (brak użycia = brak efektu wizualnego). Do opcjonalnego cleanupu w backlog.
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 15-remove-softra-insurance, Plan: 01*
|
||||
*Completed: 2026-04-22*
|
||||
316
.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md
Normal file
316
.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md
Normal file
@@ -0,0 +1,316 @@
|
||||
---
|
||||
phase: 16-i18n-plugin-refactor
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
- wp-content/plugins/carei-reservation/languages/carei-reservation.pot (nowy)
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Przygotować plugin `carei-reservation` do dwujęzyczności: wszystkie user-facing stringi w PHP owinięte w `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`; stringi w JS zmigrowane do `wp_localize_script` (obiekt `careiI18n`) tłumaczony po stronie PHP; textdomain ładowany w bootstrapie; wygenerowany plik `.pot` gotowy do tłumaczenia w Phase 18.
|
||||
|
||||
## Purpose
|
||||
Plugin pokrywa ~100% interakcji użytkownika w języku rezerwacji (modal, hero search, admin panel, widgety mapa/miasta/oddziały). Bez i18n żaden zewnętrzny translator (Polylang, Automatic Translate Addon, Loco) nie jest w stanie tłumaczyć tej zawartości — Polylang widzi tylko treść WordPressa/Elementora, a stringi w JS nie istnieją w DOM-ie serwerowo. i18n-refactor jest twardym blokerem dla Phase 17 i 18 milestone'u v0.7.
|
||||
|
||||
## Output
|
||||
- 8 plików PHP z stringami owiniętymi w funkcje i18n
|
||||
- Bootstrap `carei-reservation.php` ładuje `load_plugin_textdomain` na `plugins_loaded`
|
||||
- `carei-reservation.js` nie ma hardkodowanych stringów PL — używa obiektu `careiI18n`
|
||||
- `class-elementor-widget.php` enqueue `wp_localize_script('carei-reservation', 'careiI18n', [...])` z kluczami zmapowanymi 1:1 na użycia w JS
|
||||
- `languages/carei-reservation.pot` zawiera ~80–150 wpisów gotowych do tłumaczenia
|
||||
- Strona po polsku działa **identycznie jak przed zmianami** (żaden tekst się nie zmienia — tylko jest owinięty)
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||
@.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
|
||||
@wp-content/plugins/carei-reservation/carei-reservation.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: PHP i18n kompletny, textdomain załadowany
|
||||
```gherkin
|
||||
Given plugin carei-reservation jest aktywny
|
||||
When wyszukamy `grep -rn "__\|_e\|esc_html__\|esc_attr__" wp-content/plugins/carei-reservation/`
|
||||
Then każdy user-facing string w plikach PHP jest owinięty w odpowiednią funkcję z textdomain 'carei-reservation'
|
||||
And bootstrap `carei-reservation.php` wywołuje `load_plugin_textdomain('carei-reservation', false, dirname(plugin_basename(__FILE__)) . '/languages/')` na haku `plugins_loaded`
|
||||
And żaden user-facing literał PL nie pozostaje bez opakowania (z wyjątkiem: komentarzy, kluczy meta zaczynających się od `_`, nazw taxonomii jako slugi)
|
||||
```
|
||||
|
||||
## AC-2: JS migrated do careiI18n, brak regresji w PL
|
||||
```gherkin
|
||||
Given modal rezerwacji jest otwierany w języku polskim
|
||||
When użytkownik przechodzi przez pełny flow (wybór dat/oddziału/klasy → podsumowanie → booking)
|
||||
Then wszystkie etykiety, komunikaty błędów, placeholdery i nagłówki są identyczne tekstowo jak przed refactorem (AC: parity wizualna 1:1)
|
||||
And plik `carei-reservation.js` NIE zawiera żadnego literału z polskimi znakami diakrytycznymi (ąćęłńóśźż) ani polskich słów kluczowych
|
||||
And w DevTools → Console: `typeof window.careiI18n === 'object'` zwraca `true` po załadowaniu strony
|
||||
And obiekt `careiI18n` zawiera klucze odpowiadające wszystkim zmigrowanym stringom
|
||||
```
|
||||
|
||||
## AC-3: .pot wygenerowany, gotowy do tłumaczenia
|
||||
```gherkin
|
||||
Given Task 1 i 2 zakończone
|
||||
When otworzymy `wp-content/plugins/carei-reservation/languages/carei-reservation.pot`
|
||||
Then plik zawiera nagłówek z metadanymi pluginu (Project-Id-Version, Language: en)
|
||||
And zawiera wpisy `msgid` dla wszystkich stringów z plików PHP (włącznie z kluczami z careiI18n w wp_localize_script)
|
||||
And wszystkie `msgstr` są puste (ready for translation)
|
||||
And ilość wpisów >= 80 (szacunek na podstawie skanu)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: PHP i18n — wrap stringów + textdomain bootstrap</name>
|
||||
<files>
|
||||
wp-content/plugins/carei-reservation/carei-reservation.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-elementor-widget.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-admin-panel.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-rest-proxy.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-search-widget.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-cities-widget.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-map-widget.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||
</files>
|
||||
<action>
|
||||
1. W `carei-reservation.php` w bootstrap pluginu dodać (na haku `plugins_loaded` lub przy init):
|
||||
```php
|
||||
add_action( 'plugins_loaded', function () {
|
||||
load_plugin_textdomain( 'carei-reservation', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
|
||||
} );
|
||||
```
|
||||
2. Utworzyć katalog `wp-content/plugins/carei-reservation/languages/` (pusty placeholder lub `.gitkeep`).
|
||||
3. Dla każdego z 8 plików PHP — owinąć wszystkie user-facing stringi:
|
||||
- Tekst w HTML-u → `esc_html__( 'tekst', 'carei-reservation' )` lub `esc_html_e( 'tekst', 'carei-reservation' )` (wewnątrz `echo`)
|
||||
- Atrybuty (placeholder, aria-label, title, alt) → `esc_attr__( 'tekst', 'carei-reservation' )`
|
||||
- Etykiety kontrolek Elementor (`add_control`, `add_responsive_control`) — `'label' => esc_html__( 'tekst', 'carei-reservation' )`
|
||||
- Labels CPT i meta box (`class-admin-panel.php`) → `__( 'tekst', 'carei-reservation' )` w tablicach labels
|
||||
- Kolumny admin listy, filtry, akcje masowe → `__()` / `esc_html__()`
|
||||
- Komunikaty błędów z REST proxy (`class-rest-proxy.php`) w `WP_Error` → `__( 'komunikat', 'carei-reservation' )`
|
||||
- Tytuły widgetów Elementor i ich opisy (get_title, get_keywords) → `esc_html__()`
|
||||
4. NIE OWIJAJ:
|
||||
- Slugów CPT/post_type/taxonomy (`carei_reservation`)
|
||||
- Kluczy meta (`_carei_protection_package`, `_carei_status`)
|
||||
- Nazw pól w payloadach do Softra API (klucze JSON)
|
||||
- Kluczy tablic konfiguracyjnych, nazw hooków, nazw klas/funkcji
|
||||
- Komentarzy PHP i docblocków
|
||||
- Logów error_log (to stringi techniczne, nie user-facing)
|
||||
- Wartości statusów w DB (`nowe`, `przeczytane`, `zrealizowane`) — zostają jako slug; tłumaczymy tylko UI labels (osobny klucz → label mapping)
|
||||
5. Komunikaty z Softra API (Phase 17 zajmie się mapowaniem — w tym planie NIE ruszamy error message coming FROM Softra). Owijamy tylko nasze własne błędy.
|
||||
6. Użyj konsystentnie `'carei-reservation'` jako textdomain — bez wyjątków.
|
||||
|
||||
Unikaj: globalnego find/replace bez kontekstu — każdy string wymaga decyzji czy jest user-facing czy nie. Nie zmieniaj logiki biznesowej, tylko opakowanie tekstu.
|
||||
</action>
|
||||
<verify>
|
||||
1. `grep -rn "esc_html__\|__(\|esc_attr__" wp-content/plugins/carei-reservation/includes/ | wc -l` → >= 60 wystąpień
|
||||
2. `grep -rn "load_plugin_textdomain" wp-content/plugins/carei-reservation/` → 1 wystąpienie w bootstrap
|
||||
3. Ręcznie otwórz stronę z modalem po polsku → każdy tekst identyczny jak przed zmianą (nic się nie przetłumaczyło bo .po jeszcze nie ma)
|
||||
4. Otwórz wp-admin → Rezerwacje → lista + szczegóły → wszystkie etykiety po PL identyczne
|
||||
5. `php -l` na każdym z 8 plików → No syntax errors
|
||||
</verify>
|
||||
<done>AC-1 satysfakcjonowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: JS i18n — migracja stringów do careiI18n przez wp_localize_script</name>
|
||||
<files>
|
||||
wp-content/plugins/carei-reservation/assets/js/carei-reservation.js,
|
||||
wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
</files>
|
||||
<action>
|
||||
1. W `class-elementor-widget.php` — w metodzie enqueue skryptów (tam gdzie `wp_enqueue_script('carei-reservation', ...)` ) dodać **po** enqueue:
|
||||
```php
|
||||
wp_localize_script( 'carei-reservation', 'careiI18n', array(
|
||||
'selectSegment' => esc_html__( 'Wybierz segment', 'carei-reservation' ),
|
||||
'selectBranch' => esc_html__( 'Wybierz oddział', 'carei-reservation' ),
|
||||
'selectDates' => esc_html__( 'Wybierz daty', 'carei-reservation' ),
|
||||
'loading' => esc_html__( 'Ładowanie...', 'carei-reservation' ),
|
||||
'errorNetwork' => esc_html__( 'Błąd połączenia. Spróbuj ponownie.', 'carei-reservation' ),
|
||||
'errorRequired' => esc_html__( 'To pole jest wymagane', 'carei-reservation' ),
|
||||
// ...wszystkie pozostałe stringi użyte w JS
|
||||
) );
|
||||
```
|
||||
**Uwaga:** jeśli widget renderuje się w więcej niż jednym miejscu (search widget też ładuje ten sam JS?), upewnij się że `wp_localize_script` leci tylko raz per żądanie (handle `carei-reservation` jest globalny).
|
||||
|
||||
2. W `carei-reservation.js` — odnaleźć WSZYSTKIE polskie stringi literalne i zamienić na odwołania do `careiI18n.kluczX`:
|
||||
- Polskie znaki diakrytyczne (ąćęłńóśźż) są dobrym pierwotnym filtrem do wyszukania
|
||||
- Etykiety w `buildExtraCard`, nagłówki sekcji, komunikaty walidacji, błędy API, teksty przycisków, placeholdery setowane z JS, toast/error summary
|
||||
- Stringi budowane dynamicznie (`'Pakiet ochronny: ' + name + ' — ' + price`) → użyj wrappera z template string: `careiI18n.protectionLine.replace('%name%', name).replace('%price%', price)` i podobnie w PHP `sprintf()` nie jest potrzebny po stronie JS — użyj prostego `.replace()` z placeholderami `%name%`/`%days%`/`%total%`. Alternatywnie: przetłumaczony szablon z `{name}` / `{days}`.
|
||||
- Lista krajów, dni tygodnia, nazwy miesięcy (jeśli są hardkodowane PL) → do `careiI18n` albo do natywnego `Intl.DateTimeFormat(locale)` tam gdzie to możliwe
|
||||
- Konsol.log/error → **NIE ruszaj** (to techniczne)
|
||||
|
||||
3. Dla stringów z formatowaniem liczbowo-pluralnym (np. "1 doba" / "2 doby" / "5 dób") zdefiniuj w careiI18n 3 warianty (`dayOne`, `dayFew`, `dayMany`) i w JS napisz pomocnika `pluralPl(n, one, few, many)` który używa reguł polskich. Dla EN wystarczy `dayOne` / `dayOther`.
|
||||
|
||||
4. Końcowa weryfikacja: `grep -nE "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → zero wyników (poza komentarzami jeśli są).
|
||||
|
||||
Unikaj:
|
||||
- Zmiany logiki biznesowej (walidacja, flow booking, API calls) — tylko tekst
|
||||
- Tworzenia dziesięciu funkcji pomocniczych — jedna `t(key)` z fallbackiem do `key` wystarczy
|
||||
- Zmiany `console.*` komunikatów (techniczne, nie user-facing)
|
||||
- Nie twórz duplikatów stringów — jeśli „Wybierz oddział" występuje w 3 miejscach, ma 1 klucz
|
||||
</action>
|
||||
<verify>
|
||||
1. `grep -cP "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → 0 (lub tylko w komentarzach)
|
||||
2. Otwórz modal w przeglądarce → `window.careiI18n` w console → obiekt z kluczami
|
||||
3. Kliknij przez cały flow rezerwacji w języku polskim → tekst identyczny jak przed Task 2
|
||||
4. DevTools Network → brak 404 dla `carei-reservation.js`
|
||||
5. `node --check wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → OK (walidacja składni)
|
||||
</verify>
|
||||
<done>AC-2 satysfakcjonowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Generate .pot file w languages/</name>
|
||||
<files>wp-content/plugins/carei-reservation/languages/carei-reservation.pot</files>
|
||||
<action>
|
||||
1. Opcja A (preferowana jeśli dostępne WP-CLI na serwerze):
|
||||
```bash
|
||||
wp i18n make-pot wp-content/plugins/carei-reservation wp-content/plugins/carei-reservation/languages/carei-reservation.pot --domain=carei-reservation
|
||||
```
|
||||
2. Opcja B (ręcznie, jeśli brak WP-CLI):
|
||||
- Skan wszystkich plików PHP (8 sztuk) i wyciągnięcie wszystkich `__()`, `esc_html__()`, `esc_attr__()` z textdomain `carei-reservation`
|
||||
- Dodatkowo wyciągnięcie kluczy z `careiI18n` w `class-elementor-widget.php` (bo są też owinięte w `esc_html__`)
|
||||
- Stworzenie pliku `.pot` zgodnie z formatem gettext:
|
||||
```
|
||||
# Copyright (C) 2026 Carei
|
||||
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Carei Reservation 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-04-22\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: manual\n"
|
||||
"Language-Team: \n"
|
||||
|
||||
#: includes/class-elementor-widget.php:XXX
|
||||
msgid "Złóż zapytanie o rezerwację"
|
||||
msgstr ""
|
||||
|
||||
[...kolejne wpisy...]
|
||||
```
|
||||
- Każdy msgid pojawia się raz (deduplikacja), wszystkie msgstr puste
|
||||
3. Jeśli opcja A zadziała → zweryfikuj liczbę wpisów (`grep -c "^msgid " plik.pot` >= 80)
|
||||
4. Jeśli używamy B → spróbuj użyć **Loco Translate** (plugin w WP) do skanu i wygenerowania .pot — to najszybsza ścieżka w praktyce (Loco Translate → Plugins → Carei Reservation → Create template). Wtedy Task 3 staje się głównie zadaniem w wp-admin (nie w kodzie), i plik pojawia się w `languages/carei-reservation.pot` generowany przez Loco.
|
||||
|
||||
Unikaj: ręcznego edytowania .pot jeśli WP-CLI lub Loco dostępne. Automatyczne narzędzie nie przegapi stringa.
|
||||
</action>
|
||||
<verify>
|
||||
1. Plik `languages/carei-reservation.pot` istnieje
|
||||
2. `grep -c "^msgid " wp-content/plugins/carei-reservation/languages/carei-reservation.pot` → >= 80
|
||||
3. Plik kończy się pustą linią i ma poprawny nagłówek (UTF-8)
|
||||
4. Otwórz w POEdit / text editor → czy widoczne są stringi w formacie `msgid "Wybierz oddział"` z pustym `msgstr ""`
|
||||
</verify>
|
||||
<done>AC-3 satysfakcjonowane: .pot gotowy dla Phase 18 (tłumaczenie).</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- 8 plików PHP z hardkodowanymi PL stringami przeniesionymi do `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`
|
||||
- Bootstrap `carei-reservation.php` ładuje textdomain przez `load_plugin_textdomain` na `plugins_loaded`
|
||||
- `carei-reservation.js` bez hardkodowanych PL — wszystkie stringi przez `careiI18n` globalny obiekt
|
||||
- `class-elementor-widget.php` robi `wp_localize_script('carei-reservation', 'careiI18n', [...])` z PHP-owymi tłumaczeniami
|
||||
- `languages/carei-reservation.pot` zawiera >= 80 wpisów gotowych do tłumaczenia
|
||||
- **Strona po polsku działa identycznie jak przed Phase 16** — to jest kluczowe: żaden tekst się nie zmienia, zmienia się TYLKO sposób jego dostarczenia do frontendu
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Wypchnij cały plugin przez SFTP (całe `wp-content/plugins/carei-reservation/`)
|
||||
2. W wp-admin: Plugins → deaktywuj i aktywuj ponownie „Carei Reservation" (żeby upewnić się że bootstrap nie crashuje)
|
||||
3. Otwórz stronę z modalem rezerwacji PO POLSKU (Polylang = PL)
|
||||
4. Pełen flow:
|
||||
a. Wypełnij krok 1: segment, daty, oddział, klasa
|
||||
b. Sprawdź sekcję „Pakiety ochronne" (SOFT/PREMIUM) — tekst identyczny
|
||||
c. Sprawdź sekcję „Opcje dodatkowe"
|
||||
d. Sekcja „Wyjazd zagraniczny" — wyszukiwarka krajów
|
||||
e. Krok 2: podsumowanie → złóż rezerwację
|
||||
f. Success view
|
||||
5. Sprawdź hero search form (mini formularz w hero) — etykiety, placeholder, CTA
|
||||
6. Sprawdź widgety: mapa Polski (tooltipy), grid miast, grid oddziałów — wszystkie teksty po PL
|
||||
7. Wejdź w wp-admin → Rezerwacje:
|
||||
- Lista z kolumnami, filtrem statusu — etykiety po PL
|
||||
- Kliknij rezerwację → meta box szczegółów po PL
|
||||
- Status dropdown (nowe/przeczytane/zrealizowane) po PL
|
||||
8. Wejdź w wp-admin → Rezerwacje → Pakiety ochronne — formularz edycji SOFT/PREMIUM — etykiety po PL
|
||||
9. DevTools Console (na stronie frontowej):
|
||||
- `window.careiI18n` → obiekt z kluczami
|
||||
- `typeof window.careiI18n.selectBranch` → `'string'` (lub jakikolwiek klucz który był migrowany)
|
||||
10. Sprawdź plik `wp-content/plugins/carei-reservation/languages/carei-reservation.pot` — otwórz, zobacz stringi
|
||||
11. (Opcjonalnie) Zainstaluj Loco Translate → Plugins → Carei Reservation → zobacz że plugin jest rozpoznany jako "translatable"
|
||||
|
||||
**Kryterium przejścia:** PL działa IDENTYCZNIE jak przed zmianą, zero regresji tekstowych.
|
||||
</how-to-verify>
|
||||
<resume-signal>Napisz "approved" aby zamknąć plan, albo opisz które miejsca pokazują nietłumaczone / regresyjne stringi.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Logika biznesowa pluginu: `class-softra-api.php` (API calls, JWT auth, caching) — nie owijamy stringów technicznych ani nie zmieniamy flow
|
||||
- Slugi CPT (`carei_reservation`), kluczy meta (`_carei_*`), nazw taxonomii, statusów w bazie (`nowe`, `przeczytane`, `zrealizowane`)
|
||||
- Struktura payloadów wysyłanych do Softra API (klucze JSON, wartości boolean, nazwy pól)
|
||||
- `class-softra-api.php` — ten plik nie ma user-facing stringów; zostaje nietknięty (mapowanie komunikatów z Softra = Phase 17)
|
||||
- Hooki, nazwy akcji, nazwy filtrów WordPress
|
||||
- Logika Phase 13 (pakiety ochronne): UI labels owijamy, ale struktura danych (REST `/protection-packages`, meta `_carei_protection_package`) bez zmian
|
||||
- Logika Phase 15 (drop Softra-insurance): filtr pozostaje, komunikaty JS też owijamy w careiI18n
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie generujemy tłumaczeń EN w tym planie — tylko `.pot`. Phase 18 zajmie się `.po`/`.mo`
|
||||
- Nie dodajemy dwujęzycznych pól w panelu pakietów ochronnych — Phase 17
|
||||
- Nie mapujemy komunikatów z Softra API — Phase 17
|
||||
- Nie tłumaczymy treści dynamicznych z bazy (np. nazwy pakietów w DB zostają po PL na tym etapie — zmieni to Phase 17)
|
||||
- Nie dotykamy CSS
|
||||
- Nie dotykamy treści w Elementorze (strony, widgety natywne) — te już są tłumaczone przez Polylang addon
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed zamknięciem planu:
|
||||
- [ ] `grep -rn "load_plugin_textdomain" wp-content/plugins/carei-reservation/carei-reservation.php` → 1 wynik
|
||||
- [ ] `grep -rnE "__\(|esc_html__|esc_attr__" wp-content/plugins/carei-reservation/includes/ | wc -l` → >= 60
|
||||
- [ ] `grep -cP "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → 0 (lub tylko komentarze)
|
||||
- [ ] `wp-content/plugins/carei-reservation/languages/carei-reservation.pot` istnieje, >= 80 wpisów msgid
|
||||
- [ ] `php -l` dla wszystkich 8 plików → No syntax errors
|
||||
- [ ] Strona PL działa bez regresji (checkpoint human-verify)
|
||||
- [ ] AC-1, AC-2, AC-3 przeszły weryfikację
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 auto tasks zakończone
|
||||
- Checkpoint human-verify zatwierdzony ("approved")
|
||||
- Brak regresji w języku polskim
|
||||
- `.pot` gotowy do Phase 18
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md`
|
||||
</output>
|
||||
156
.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
Normal file
156
.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
Normal file
@@ -0,0 +1,156 @@
|
||||
---
|
||||
phase: 16-i18n-plugin-refactor
|
||||
plan: 01
|
||||
subsystem: i18n
|
||||
tags: [polylang, gettext, pot, wp-localize-script, textdomain, elementor]
|
||||
|
||||
requires:
|
||||
- phase: 13-protection-packages
|
||||
provides: panel WP pakietów SOFT/PREMIUM
|
||||
- phase: 15-remove-softra-insurance
|
||||
provides: czysty pricelist bez Softra-insurance
|
||||
|
||||
provides:
|
||||
- textdomain 'carei-reservation' ładowany na plugins_loaded
|
||||
- wszystkie user-facing PHP stringi w __()/esc_html__()/esc_attr__()
|
||||
- helper I18N/t()/tFmt()/pluralPl() w carei-reservation.js
|
||||
- wp_localize_script('careiI18n', [...]) z 78 kluczami
|
||||
- languages/carei-reservation.pot (157 msgid)
|
||||
|
||||
affects: [17-bilingual-packages, 18-en-translation]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Helper I18N + t(key, fallback) w JS z obiektem window.careiI18n"
|
||||
- "pluralPl(n, one, few, many) — polskie reguły pluralizacji (1=one, 2-4 bez 12-14=few, reszta=many)"
|
||||
- "Drop Softra passthrough errors z tłumaczenia — mapowanie zostaje na Phase 17"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- wp-content/plugins/carei-reservation/languages/carei-reservation.pot
|
||||
modified:
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
|
||||
key-decisions:
|
||||
- "Bootstrap: wp_localize_script dla careiI18n leci w wp_enqueue_scripts (nie w Elementor widget render) — obiekt dostępny na każdej stronie gdzie ładowany jest skrypt"
|
||||
- "Helper get_status_label() zamiast statycznej tablicy labels w Admin panelu — pozwala tłumaczyć w runtime po załadowaniu textdomain"
|
||||
- "Softra API passthrough errors nietłumaczone w Phase 16 — Phase 17 zajmie się mapowaniem"
|
||||
- "Fallback english w t()/tFmt() = oryginalny polski string (żeby bez .po strona dalej działała po polsku)"
|
||||
|
||||
patterns-established:
|
||||
- "Klucze careiI18n: camelCase, grupowane semantycznie (selectX, btnX, labelX, errorX, thX, rejectX, announceX)"
|
||||
- "Multi-line UI stringi z HTML (np. <strong>%count% %unit%</strong>) przez wp_kses po stronie PHP, renderowane jako innerHTML w JS"
|
||||
|
||||
duration: ~45min
|
||||
started: 2026-04-22
|
||||
completed: 2026-04-22
|
||||
---
|
||||
|
||||
# Phase 16 Plan 01: i18n refactor pluginu — Summary
|
||||
|
||||
**Plugin carei-reservation przygotowany do dwujęzyczności: 8 plików PHP owiniętych w funkcje gettext, JS zmigrowany do obiektu `window.careiI18n` przez `wp_localize_script`, wygenerowany `.pot` z 157 unikalnymi msgid gotowy do tłumaczenia w Phase 18.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~45min |
|
||||
| Tasks | 3 auto + 1 human-verify completed |
|
||||
| Files modified | 9 |
|
||||
| Files created | 1 (.pot) |
|
||||
| Delegation | 5 agentów równolegle (1 elementor-widget, 1 admin-panel, 1 JS, 1 dla 4 widgetów, 1 .pot) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: PHP i18n kompletny, textdomain załadowany | Pass | `load_plugin_textdomain` na plugins_loaded; ~105 stringów PHP owiniętych |
|
||||
| AC-2: JS migrated do careiI18n, brak regresji w PL | Pass | `window.careiI18n` = 78 kluczy, grep diakrytyków w JS = 0 poza fallbackami/API payloads; human-verify PL OK |
|
||||
| AC-3: .pot wygenerowany | Pass | 157 msgid, UTF-8, nagłówek poprawny, msgstr puste |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **8 plików PHP** z user-facing stringami w funkcjach i18n (carei-reservation, elementor-widget, admin-panel, rest-proxy, search/cities/map/branches widgets)
|
||||
- **`carei-reservation.js`** (1573 linie) — ~70 literałów przez `t()`/`tFmt()` + helper `pluralPl()` dla polskich form
|
||||
- **`wp_localize_script('careiI18n', [...])`** z 78 kluczami w bootstrapie
|
||||
- **`.pot`** gotowy do tłumaczenia — 157 unikalnych msgid w `languages/`
|
||||
- Dekonstrukcja admin-panelu: statyczna tablica labels statusów rozbita na helper `get_status_label()` (wymóg WP — translation w runtime)
|
||||
- Zgoda RODO z osadzonym `<a>` — owinięcie przez `wp_kses(__(...))`
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `carei-reservation.php` | Modified | load_plugin_textdomain + wp_localize_script('careiI18n', 78 kluczy) |
|
||||
| `includes/class-elementor-widget.php` | Modified | ~30 stringów (Elementor controls + HTML modal) |
|
||||
| `includes/class-admin-panel.php` | Modified | ~55 stringów + helper get_status_label() |
|
||||
| `includes/class-rest-proxy.php` | Modified | 2 stringi (Invalid nonce, Softra not configured) |
|
||||
| `includes/class-search-widget.php` | Modified | 8 stringów (hero search form) |
|
||||
| `includes/class-cities-widget.php` | Modified | 1 string (get_title) |
|
||||
| `includes/class-map-widget.php` | Modified | 3 stringi (Oddział %s, ul. %s, get_title) |
|
||||
| `includes/class-branches-widget.php` | Modified | 3 stringi |
|
||||
| `assets/js/carei-reservation.js` | Modified | ~70 literałów → t()/tFmt() + helpery + pluralPl() |
|
||||
| `languages/carei-reservation.pot` | Created | 157 msgid, UTF-8, ready for Phase 18 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| `careiI18n` w bootstrapie (nie w Elementor render) | Skrypt jest rejestrowany w bootstrapie → localize leci raz, dostępne globalnie | Upraszcza architekturę, nie ma per-widget duplikacji |
|
||||
| Fallback w `t('key', 'polski string')` | Bez .mo plik dalej działa po polsku; bez niego crashowałoby `undefined` | Zero-downtime dla PL, bezpieczne wrap bez straty funkcjonalności |
|
||||
| Softra error messages nietłumaczone | Zewnętrzne API zwraca polskie błędy — mapowanie = Phase 17 | Zostaje jasna granica między „nasze" a „Softra" stringami |
|
||||
| Statuses slugi niezmienione (`nowe`/`przeczytane`/`zrealizowane`) | Dane w DB, klucze logiki; tylko UI labels idą przez `get_status_label()` | Brak regresji w filtrach i warunkach logicznych |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 1 | Admin panel: statyczna tablica statuses → helper (konieczne dla WP i18n timing) |
|
||||
| Scope additions | 0 | — |
|
||||
| Deferred | 1 | Softra API error mapping (świadomie przełożone na Phase 17) |
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Admin panel] Statyczna tablica `$statuses` z labels nie może być tłumaczona przed init**
|
||||
- Found during: Task 1 (admin-panel refactor)
|
||||
- Issue: Tablica jako property klasy = translation wywoływane przed załadowaniem textdomain → pusty string
|
||||
- Fix: Rozbicie na helper `get_status_label($key)` z switchem, wywoływany w runtime po plugins_loaded
|
||||
- Files: class-admin-panel.php
|
||||
- Verification: human-verify PL — statusy na liście, w dropdown i meta boxie wyglądają identycznie jak przed
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- Phase 17: mapowanie ~10-15 typowych komunikatów Softra API na lokalizowane wersje (np. `Brak pojazdów w tym terminie` → reject key + tłumaczenie)
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- `.pot` z 157 msgid — gotowy input dla Phase 18 (tłumaczenie na EN przez GPT-4.1 mini lub Loco Translate)
|
||||
- Infrastruktura i18n kompletna — Phase 17 i 18 mogą budować na niej bez refactoru
|
||||
- Polski UI działa bez regresji (confirmed by user: „język polski jest ok.")
|
||||
|
||||
**Concerns:**
|
||||
- `window.careiI18n` rośnie (78 kluczy) — dla performance można rozważyć lazy-load w przyszłości, ale na razie ~3KB JSON to szum
|
||||
- Niektóre msgid zawierają HTML (`Wybrano: <strong>%count% %unit%</strong>`) — wymagają translatora świadomego tagów; .pot ma to oznaczone wp_kses whitelist
|
||||
- Softra error messages (Phase 17) — w JS są klucze `rejectCarNotFound` itd. jako fallbacki, ale aktualny kod Phase 16 nie używa jeszcze mapowania → Phase 17 musi dodać warstwę w rest-proxy lub softra-api
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 16-i18n-plugin-refactor, Plan: 01*
|
||||
*Completed: 2026-04-22*
|
||||
@@ -0,0 +1,305 @@
|
||||
---
|
||||
phase: 17-bilingual-packages-and-softra-errors
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["16-01"]
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js (drobne — użycie istniejących reject* kluczy)
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
(1) Panel administratora pakietów ochronnych SOFT/PREMIUM dostaje dodatkowe pola `name_en` i `description_en`. REST endpoint `/protection-packages` zwraca wariant zgodny z aktualnym locale (`get_locale()` lub parametr `?lang=`): PL → pola bazowe, EN → pola `_en` z fallbackiem do bazowych gdy puste. (2) Słownik mapowania ~12–15 typowych komunikatów Softra API (PL stringi zwracane przez zewnętrzny system) na lokalizowane klucze — warstwa w `Carei_Softra_API` / `Carei_REST_Proxy` podmienia `message` w `WP_Error`/response na tekst wg current locale.
|
||||
|
||||
## Purpose
|
||||
Bez Phase 17 użytkownik na wersji EN widziałby (1) polskie nazwy pakietów ochronnych wprowadzone przez admina w panelu WP, (2) polskie komunikaty z Softra API przy konflikcie rezerwacji / braku pojazdu / błędzie walidacji. Phase 17 domyka jedyne dwa źródła „obcych" polskich tekstów pozostałych po Phase 16 — po niej całość UI jest dwujęzyczna bez wyjątków.
|
||||
|
||||
## Output
|
||||
- Panel `Rezerwacje → Pakiety ochronne`: każdy pakiet (SOFT/PREMIUM) ma 4 pola tekstowe zamiast 2 — `name` + `name_en`, `description` + `description_en` (cena `pricePerDay` pozostaje jedna, wspólna)
|
||||
- Option `carei_protection_packages` w DB ma nową strukturę: `soft: {name, name_en, description, description_en, pricePerDay, enabled}`, analogicznie premium
|
||||
- REST `/protection-packages` zwraca wariant zlokalizowany wg `determine_locale()` — klucze `name`/`description` w odpowiedzi są już właściwe dla języka (EN lub PL), frontend nie musi wiedzieć nic o wariantach
|
||||
- Słownik mapowania w `Carei_Softra_API` (nowa metoda `map_error_message( $pl_message )`) — zwraca sparowany klucz tłumaczenia z textdomain `carei-reservation` dla znanych komunikatów, albo oryginał jeśli brak mapowania
|
||||
- Komunikaty w REST response / `WP_Error` przechodzą przez filtr mapowania przed zwróceniem do frontu
|
||||
- Zero regresji w wersji PL — wszystkie pakiety i błędy wyświetlają się identycznie jak po Phase 16
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||
@.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
|
||||
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Panel pakietów obsługuje pola _en
|
||||
```gherkin
|
||||
Given administrator wchodzi w `wp-admin → Rezerwacje → Pakiety ochronne`
|
||||
When wypełnia pola `Nazwa` / `Nazwa (EN)` / `Opis` / `Opis (EN)` dla SOFT i PREMIUM i klika "Zapisz"
|
||||
Then wszystkie 4 pola tekstowe per pakiet są zapisane w option `carei_protection_packages`
|
||||
And po odświeżeniu strony formularz pokazuje zapisane wartości (w tym EN)
|
||||
And walidacja/sanityzacja jest konsystentna między polami PL i EN
|
||||
And brak EN (puste pole `name_en`/`description_en`) jest poprawny — to oznacza „fallback do PL"
|
||||
```
|
||||
|
||||
## AC-2: REST endpoint zwraca wariant per locale
|
||||
```gherkin
|
||||
Given w DB są zapisane pakiety z polami PL i EN
|
||||
When frontend PL woła `/wp-json/carei/v1/protection-packages` (locale = pl_PL)
|
||||
Then odpowiedź zawiera `soft.name` i `soft.description` z wariantu PL
|
||||
And analogicznie dla premium
|
||||
|
||||
When frontend EN (Polylang język EN) woła ten sam endpoint
|
||||
Then odpowiedź zawiera `soft.name` = wartość `name_en` (lub PL jeśli pole EN puste)
|
||||
And `soft.description` = `description_en` z fallbackiem
|
||||
And struktura odpowiedzi nie zmienia kluczy (`name`, `description`, `pricePerDay`, `enabled`) — tylko treści
|
||||
And frontend JS nie wymaga żadnej zmiany logicznej (oprócz ewentualnego `?lang=` jeśli Polylang nie ustawia locale serwerowo)
|
||||
```
|
||||
|
||||
## AC-3: Błędy Softra mapowane na lokalizowane stringi
|
||||
```gherkin
|
||||
Given użytkownik próbuje zarezerwować pojazd niedostępny w danym terminie
|
||||
When Softra API zwraca message "Brak dostępnego pojazdu w wybranym terminie" (lub podobny)
|
||||
Then `Carei_Softra_API::map_error_message()` rozpoznaje string
|
||||
And zwraca lokalizowany wariant zgodny z current locale (`__('rejectCarNotFound', ...)` lub bezpośredni tekst EN)
|
||||
And REST response `message` zawiera tekst w języku zgodnym z UI
|
||||
And dla nieznanych komunikatów — przepuszcza oryginał (graceful fallback)
|
||||
And słownik pokrywa co najmniej 12 typowych komunikatów (lista w Task 2)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Panel admina — pola name_en / description_en + zapis/odczyt</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-admin-panel.php</files>
|
||||
<action>
|
||||
1. Zaktualizuj `get_protection_packages_defaults()` — dodaj domyślne klucze `name_en` i `description_en` per pakiet (wartości puste albo angielskie defaulty typu `'Protection SOFT'` / `'Protection PREMIUM'`).
|
||||
2. Zaktualizuj `get_protection_packages()` — metodę czyszczenia/merge z defaultami, żeby obsłużyła nowe pola (użytkownicy z starą strukturą dostają puste EN, bez crasha).
|
||||
3. W `render_protection_packages_page()`:
|
||||
- Dla każdego pakietu (SOFT, PREMIUM) dodać pod polami PL dwa kolejne wiersze:
|
||||
- `<input type="text" name="packages[soft][name_en]">` z labelką `esc_html__( 'Nazwa (EN)', 'carei-reservation' )`
|
||||
- `<textarea name="packages[soft][description_en]">` z labelką `esc_html__( 'Opis (EN)', 'carei-reservation' )`
|
||||
- Wizualnie oddzielone (np. `<small>` note: „puste = fallback do wersji polskiej")
|
||||
- Placeholdery w `esc_attr__()`
|
||||
4. W `handle_protection_packages_save()`:
|
||||
- Sanitize `name_en` przez `sanitize_text_field()`
|
||||
- Sanitize `description_en` przez `sanitize_textarea_field()`
|
||||
- Zapisz oba pola w tej samej strukturze co PL
|
||||
- Nie ruszaj walidacji `pricePerDay` ani `enabled` — bez zmian
|
||||
5. Nie zmieniaj slugów meta, endpointu REST (Task 2), żadnej logiki biznesowej poza polem form.
|
||||
|
||||
Unikaj:
|
||||
- Dodawania nowego mechanizmu po stronie DB (np. osobnej opcji dla EN) — wszystko w istniejącym `carei_protection_packages`
|
||||
- Tłumaczenia opisu na bieżąco w panelu admin — admin wpisuje ręcznie EN
|
||||
- Refaktoru istniejącej struktury defaulti — tylko DODAJ klucze
|
||||
</action>
|
||||
<verify>
|
||||
1. Otwórz `wp-admin → Rezerwacje → Pakiety ochronne` — widoczne 4 pola tekstowe per pakiet (PL name, EN name, PL description, EN description) + cena + enabled
|
||||
2. Wpisz testowe wartości w EN, zapisz → po odświeżeniu zachowane
|
||||
3. `get_option( 'carei_protection_packages' )` w wp-admin → Tools → Site Health → Info (lub `wp db query` na locale) zawiera nowe klucze `name_en`, `description_en`
|
||||
4. `php -l class-admin-panel.php` → No syntax errors
|
||||
5. Stary kod readujący `$packages['soft']['name']` dalej działa (brak BC break)
|
||||
</verify>
|
||||
<done>AC-1 satysfakcjonowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: REST endpoint per-locale + mapowanie błędów Softra</name>
|
||||
<files>
|
||||
wp-content/plugins/carei-reservation/includes/class-rest-proxy.php,
|
||||
wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
</files>
|
||||
<action>
|
||||
|
||||
**Część A — REST `/protection-packages` zwraca wariant zlokalizowany:**
|
||||
|
||||
1. W `class-rest-proxy.php` odnajdź handler endpointu `/protection-packages` (prawdopodobnie metoda typu `get_protection_packages()`).
|
||||
2. Dodaj helper `resolve_locale()`:
|
||||
```php
|
||||
private function resolve_locale( $request ) {
|
||||
$lang = $request->get_param( 'lang' );
|
||||
if ( $lang && in_array( strtolower( $lang ), array( 'pl', 'en' ), true ) ) {
|
||||
return strtolower( $lang );
|
||||
}
|
||||
$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||
return ( 0 === strpos( $locale, 'en' ) ) ? 'en' : 'pl';
|
||||
}
|
||||
```
|
||||
- Polylang przy żądaniach REST powinien ustawić locale automatycznie (filter `locale` lub `determine_locale`). Gdyby nie — frontend może dopisać `?lang=en` jawnie (fallback). JS zostaje bez zmian jeśli Polylang dobrze współpracuje z WP REST.
|
||||
3. W handlerze endpointu: po pobraniu `$packages = Carei_Admin_Panel::get_protection_packages()`:
|
||||
- Dla lokalu `'en'`: podmień `$pkg['name']` na `$pkg['name_en']` gdy niepuste, inaczej pozostaw `$pkg['name']`. Analogicznie `description`.
|
||||
- Dla lokalu `'pl'`: bez zmian.
|
||||
- Usuń z odpowiedzi klucze `name_en`/`description_en` (frontend nie musi ich widzieć — unika leakowania i mylenia schematu).
|
||||
4. Odpowiedź REST zachowuje schemat: `{ soft: { name, description, pricePerDay, enabled }, premium: {...} }`.
|
||||
|
||||
**Część B — Mapowanie błędów Softra:**
|
||||
|
||||
5. W `class-softra-api.php` dodaj nową public static method:
|
||||
```php
|
||||
public static function map_error_message( $original_message ) {
|
||||
if ( ! is_string( $original_message ) || '' === trim( $original_message ) ) {
|
||||
return $original_message;
|
||||
}
|
||||
|
||||
$dict = array(
|
||||
'Brak dostępnego pojazdu w wybranym terminie' => __( 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.', 'carei-reservation' ),
|
||||
'Nieprawidłowy zakres dat' => __( 'Nieprawidłowy zakres dat', 'carei-reservation' ),
|
||||
'Nie znaleziono oddziału' => __( 'Nie znaleziono oddziału', 'carei-reservation' ),
|
||||
'Klient o tych danych już istnieje' => __( 'Klient o tych danych już istnieje w systemie', 'carei-reservation' ),
|
||||
'Nieprawidłowy numer PESEL' => __( 'Nieprawidłowy numer PESEL', 'carei-reservation' ),
|
||||
'Cennik wygasł' => __( 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.', 'carei-reservation' ),
|
||||
'Token wygasł' => __( 'Sesja wygasła. Odśwież stronę.', 'carei-reservation' ),
|
||||
'Nieprawidłowe dane logowania' => __( 'Błąd autoryzacji API. Skontaktuj się z administratorem.', 'carei-reservation' ),
|
||||
'Brak uprawnień' => __( 'Brak uprawnień do wykonania operacji.', 'carei-reservation' ),
|
||||
'Błąd serwera' => __( 'Błąd serwera. Spróbuj ponownie za chwilę.', 'carei-reservation' ),
|
||||
'Przekroczono limit rezerwacji' => __( 'Przekroczono limit rezerwacji dla tego klienta.', 'carei-reservation' ),
|
||||
'Nieprawidłowy numer telefonu' => __( 'Podaj poprawny numer telefonu (min. 9 cyfr).', 'carei-reservation' ),
|
||||
'Wymagane pole' => __( 'To pole jest wymagane.', 'carei-reservation' ),
|
||||
);
|
||||
|
||||
// Exact match
|
||||
if ( isset( $dict[ $original_message ] ) ) {
|
||||
return $dict[ $original_message ];
|
||||
}
|
||||
|
||||
// Fuzzy: prefix match (Softra bywa mało przewidywalny z końcówkami)
|
||||
foreach ( $dict as $pl_key => $translated ) {
|
||||
if ( 0 === stripos( $original_message, $pl_key ) ) {
|
||||
return $translated;
|
||||
}
|
||||
}
|
||||
|
||||
return $original_message; // graceful fallback
|
||||
}
|
||||
```
|
||||
- Zasada: `__()` przechodzi przez textdomain `carei-reservation` → dla locale EN pobiera tłumaczenie z `.mo` (Phase 18). W PL zwraca ten sam tekst co msgid (co jest OK — zachowuje polski oryginał).
|
||||
6. Zintegruj mapowanie:
|
||||
- W miejscach gdzie `Carei_Softra_API` zwraca błąd (np. metoda `make_booking`, `get_car_classes`, itp.) — po odebraniu `$response['error']['message']` lub `$response['message']` (sprawdź strukturę w istniejącym kodzie) zawiń przez `self::map_error_message( $msg )` przed utworzeniem `WP_Error` / przed zwrotem.
|
||||
- Jeśli API używa `WP_Error` już z warstwy `rest-proxy` — zaktualizuj tam (przechwyć message, mapuj, przekaż dalej).
|
||||
|
||||
**Część C — Frontend (drobna zmiana w JS):**
|
||||
|
||||
7. W `carei-reservation.js` w miejscach gdzie łapiesz `err.message` z odpowiedzi API przy niepowodzeniu rezerwacji — jeśli obecnie wyświetlasz surowy message, zostaw bez zmian (backend już mapuje). Jeśli masz heurystykę kategoryzacji po kluczach (np. "no car available" → pokaż specjalny alert), rozważ aktualizację. W MINIMUM: upewnij się że fallbacki `rejectCarNotFound` itd. w `careiI18n` są używane gdzie trzeba. Prawdopodobnie zmiana zero/minimal.
|
||||
8. Jeśli frontend musi jawnie dopisać `?lang=` przy requeście `/protection-packages` (bo Polylang nie ustawia locale REST automatycznie) — dodaj to w `loadProtectionPackages()`:
|
||||
```js
|
||||
var lang = (document.documentElement.lang || '').toLowerCase().indexOf('en') === 0 ? 'en' : 'pl';
|
||||
fetch(REST_URL + 'protection-packages?lang=' + lang, {...})
|
||||
```
|
||||
|
||||
Unikaj:
|
||||
- Tłumaczenia komunikatów po stronie JS — wszystko leci z PHP (`__()` rozwiązuje per locale)
|
||||
- Wprowadzania nowego schemat odpowiedzi (`name_pl` / `name_en` w payloadzie) — odpowiedź zawsze ma `name` w właściwym języku
|
||||
- Nadpisywania obecnej logiki reject — tylko dodaj warstwę mapowania
|
||||
</action>
|
||||
<verify>
|
||||
1. `php -l` dla obu zmienionych plików → No syntax errors
|
||||
2. W PL (Polylang = PL): `curl /wp-json/carei/v1/protection-packages` → `soft.name` = polska nazwa, `soft.description` = polski opis
|
||||
3. W EN (Polylang = EN) albo `?lang=en`: `soft.name` = angielska nazwa (lub polska fallback gdy puste)
|
||||
4. Odpowiedź REST NIE zawiera kluczy `name_en`/`description_en` (czyste API)
|
||||
5. Wywołanie błędnej rezerwacji (niedostępny pojazd) → komunikat zwrócony do frontendu = tłumaczenie z `__()` zamiast surowego Softra-PL (w EN)
|
||||
6. Nieznany komunikat Softra (spoza słownika) → passthrough bez zmian (graceful)
|
||||
</verify>
|
||||
<done>AC-2, AC-3 satysfakcjonowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- Panel `wp-admin → Rezerwacje → Pakiety ochronne` ma teraz 4 pola tekstowe per pakiet (`name`, `name_en`, `description`, `description_en`) + cena + enabled
|
||||
- Option `carei_protection_packages` przechowuje wersje PL i EN każdego pakietu
|
||||
- REST `/protection-packages` zwraca wariant per locale (Polylang EN → pola `_en` z fallbackiem do PL)
|
||||
- Słownik `Carei_Softra_API::map_error_message()` z 12+ wpisami + exact/fuzzy match, wbudowany w ścieżkę zwrotu błędów API
|
||||
- Wersja PL — zero regresji, wersja EN — pakiety i błędy w języku EN (po załadowaniu .mo w Phase 18)
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Wypchnij zmienione 3–4 pliki przez SFTP
|
||||
2. **Admin panel test:**
|
||||
- `wp-admin → Rezerwacje → Pakiety ochronne`
|
||||
- Sprawdź że są widoczne nowe pola: „Nazwa (EN)", „Opis (EN)" dla SOFT i PREMIUM
|
||||
- Wypełnij testowo: SOFT EN name = „SOFT Protection", EN description = „Basic protection package..."
|
||||
- PREMIUM EN name = „PREMIUM Protection", EN description = „Enhanced protection..."
|
||||
- Kliknij „Zapisz" → odśwież stronę → wartości EN są zachowane
|
||||
3. **Frontend PL (Polylang = PL):**
|
||||
- Otwórz modal rezerwacji → sekcja „Pakiety ochronne"
|
||||
- Kafelki SOFT/PREMIUM pokazują **polskie** nazwy i opisy (jak zapisane w polach PL)
|
||||
- Cena `X.XX zł/doba` — bez zmian
|
||||
4. **Frontend EN (Polylang = EN):**
|
||||
- Przełącz język na EN w switcherze Polylang
|
||||
- Otwórz modal rezerwacji → sekcja „Pakiety ochronne"
|
||||
- Kafelki pokazują **angielskie** nazwy i opisy (wartości z pól `_en`)
|
||||
- Jeśli któreś pole `_en` zostawiłeś puste → frontend powinien pokazać PL wariant (fallback)
|
||||
5. **Test fallbacku:**
|
||||
- Wróć do admin → pole EN jednego pakietu wyczyść
|
||||
- Frontend EN → ten pakiet pokazuje polski oryginał (graceful)
|
||||
6. **Error mapping test:**
|
||||
- Wymuś błąd Softra (np. data w przeszłości albo niedostępny pojazd — jeśli API tak odpowiada)
|
||||
- PL: komunikat w języku polskim (bez zmian)
|
||||
- EN: komunikat po angielsku (po załadowaniu .mo w Phase 18) albo oryginalne PL jeśli Phase 18 jeszcze niedostępne
|
||||
- Uwaga: **dopóki .po/.mo nie istnieją (Phase 18), `__()` zwraca oryginalny polski string w obu językach.** To jest OK — Phase 17 dostarcza infrastrukturę, Phase 18 dostarcza treść.
|
||||
7. **DevTools Network:**
|
||||
- `GET /wp-json/carei/v1/protection-packages` w PL → odpowiedź z PL
|
||||
- `GET /wp-json/carei/v1/protection-packages?lang=en` → odpowiedź z EN
|
||||
- Oba responsy nie zawierają pól `name_en`/`description_en` w payloadzie
|
||||
8. **Brak regresji:**
|
||||
- Cały flow rezerwacji PL działa jak po Phase 16 — nic się nie popsuło
|
||||
|
||||
**Kryterium przejścia:** Admin zapisuje i odczytuje EN pola, REST zwraca wariant per locale, error mapping działa (graceful fallback dla nieznanych), PL bez zmian.
|
||||
</how-to-verify>
|
||||
<resume-signal>Napisz "approved" lub opisz co nie działa.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Schema `carei_reservation` CPT, meta keys (`_carei_*`), statusy w DB
|
||||
- Logika JWT auth w `Carei_Softra_API` (cache, retry)
|
||||
- Phase 13 struktura REST endpointu `/protection-packages` — klucze odpowiedzi (`name`, `description`, `pricePerDay`, `enabled`) pozostają, zmienia się treść per locale
|
||||
- Phase 15 filtr Softra-insurance (drop) — nietknięty
|
||||
- Phase 16 textdomain i wp_localize_script — tylko ROZSZERZAMY (dokładamy nowe klucze jeśli potrzeba), nie refaktorujemy
|
||||
- Frontend JS — minimalne zmiany (ewentualnie `?lang=` parameter), bez refactoru flow
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie generujemy tłumaczeń `.po`/`.mo` — to Phase 18
|
||||
- Nie dodajemy mechanizmu Polylang "String Translation" per-post-meta (nadkomplikacja) — option w DB z polami `_en` wystarczy dla 2 pakietów
|
||||
- Nie rozszerzamy słownika Softra na >20 pozycji — 12–15 pokrywa realne przypadki, resztę mapujemy iteracyjnie gdy się pojawią
|
||||
- Nie tłumaczymy statusów rezerwacji w DB (`nowe`/`przeczytane`/`zrealizowane`) — tylko UI labels (już zrobione w Phase 16)
|
||||
- Nie dotykamy Elementora ani treści stron
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed zamknięciem planu:
|
||||
- [ ] Panel admin pokazuje 4 pola per pakiet (PL + EN name, PL + EN description)
|
||||
- [ ] Zapisanie EN w panelu → dane utrzymują się po reload
|
||||
- [ ] REST `/protection-packages` w PL zwraca polskie, w EN zwraca angielskie (lub PL fallback)
|
||||
- [ ] Odpowiedź REST nie zawiera `name_en`/`description_en` (czyste API)
|
||||
- [ ] `Carei_Softra_API::map_error_message()` istnieje, słownik ma >= 12 wpisów
|
||||
- [ ] Exact match + fuzzy prefix match działają
|
||||
- [ ] Zero regresji w PL (human-verify)
|
||||
- [ ] AC-1, AC-2, AC-3 przeszły weryfikację
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 2 auto tasks zakończone
|
||||
- Checkpoint human-verify zatwierdzony ("approved")
|
||||
- PL bez regresji
|
||||
- EN: pakiety pokazują wartości z pól `_en` (lub PL fallback)
|
||||
- Infrastruktura mapowania błędów Softra gotowa (tłumaczenia pojawią się po Phase 18)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
phase: 17-bilingual-packages-and-softra-errors
|
||||
plan: 01
|
||||
subsystem: i18n
|
||||
tags: [polylang, softra-errors, rest-api, protection-packages, bilingual]
|
||||
|
||||
requires:
|
||||
- phase: 13-protection-packages
|
||||
provides: panel WP pakietów SOFT/PREMIUM + REST /protection-packages
|
||||
- phase: 16-i18n-plugin-refactor
|
||||
provides: textdomain carei-reservation + __() we wszystkich plikach
|
||||
|
||||
provides:
|
||||
- Pola name_en / description_en w panelu pakietów + zapis/odczyt
|
||||
- REST /protection-packages zwraca wariant per locale (z fallbackiem PL)
|
||||
- Carei_Softra_API::extract_softra_message() + map_error_message() ze słownikiem 13 komunikatów
|
||||
- JS loadProtectionPackages() dodaje ?lang= na podstawie document.documentElement.lang
|
||||
|
||||
affects: [18-en-translation]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Locale resolution: ?lang= param → determine_locale() → get_locale() fallback"
|
||||
- "Softra error mapping: exact match → fuzzy prefix match → graceful passthrough"
|
||||
- "Bilingual fields pattern: base field + _en variant w tej samej WP option, fallback gdy _en puste"
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
|
||||
key-decisions:
|
||||
- "Klucze _en nie wyciekają do REST response — frontend dostaje już rozwiązane name/description"
|
||||
- "map_error_message przez __() zamiast surowych PL→EN — czeka na .mo (Phase 18) bez osobnego słownika angielskiego"
|
||||
- "document.documentElement.lang zamiast explicit Polylang API — działa cross-plugin (TranslatePress, WPML) jeśli ktoś zmieni w przyszłości"
|
||||
- "Default name_en w defaults (SOFT Protection / PREMIUM Protection) — admin dostaje sensowną propozycję od razu"
|
||||
|
||||
patterns-established:
|
||||
- "Bilingual option: {base, base_en, base_description, description_en} — jedna opcja w WP DB, fallback gdy puste EN"
|
||||
- "REST locale resolution z priorytetem ?lang=<pl|en> — explicit > implicit (determine_locale)"
|
||||
|
||||
duration: ~25min
|
||||
started: 2026-04-22
|
||||
completed: 2026-04-22
|
||||
---
|
||||
|
||||
# Phase 17 Plan 01: Dwujęzyczne pakiety + mapowanie błędów Softra — Summary
|
||||
|
||||
**Panel admina pakietów ochronnych zyskał pola name_en/description_en, REST /protection-packages zwraca wariant per locale z fallbackiem, a błędy z Softra API przechodzą przez słownik 13 typowych komunikatów owinięty w __() — infrastruktura EN gotowa dla Phase 18.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~25min |
|
||||
| Tasks | 2 auto + 1 human-verify completed |
|
||||
| Files modified | 4 |
|
||||
| Delegation | 0 (inline — precyzyjne zmiany) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Panel pakietów obsługuje pola _en | Pass | 4 pola tekstowe per pakiet, sanitize + zapis w tej samej WP option, fallback gdy EN puste |
|
||||
| AC-2: REST endpoint zwraca wariant per locale | Pass | ?lang= param + determine_locale fallback; _en klucze nie wyciekają |
|
||||
| AC-3: Błędy Softra mapowane | Pass | 13 wpisów w słowniku + exact match + fuzzy prefix + graceful passthrough |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Admin panel:** 4 pola tekstowe per pakiet (PL + EN dla name i description) + placeholder + opis "Puste = fallback do wersji polskiej"
|
||||
- **`Carei_REST_Proxy::resolve_locale()`** — helper z priorytetem `?lang=pl|en` → `determine_locale()` → `get_locale()`
|
||||
- **`/protection-packages`** zwraca `name`/`description` rozwiązane per locale, `_en` klucze ukryte w payloadzie
|
||||
- **`Carei_Softra_API::extract_softra_message()`** — parser JSON odpowiedzi Softra (pola `message`/`error`/`details`/`description`)
|
||||
- **`Carei_Softra_API::map_error_message()`** — 13 typowych komunikatów PL → `__()` z textdomain, exact + fuzzy prefix match
|
||||
- **Integracja w `request()`** — błędy HTTP 4xx/5xx przechodzą przez extract→map przed `WP_Error`
|
||||
- **Frontend `loadProtectionPackages()`** dodaje `?lang=` na podstawie `document.documentElement.lang`
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `includes/class-admin-panel.php` | Modified | Pola _en + sanitize/save |
|
||||
| `includes/class-rest-proxy.php` | Modified | resolve_locale() + per-locale response |
|
||||
| `includes/class-softra-api.php` | Modified | extract_softra_message() + map_error_message() + integracja w request() |
|
||||
| `assets/js/carei-reservation.js` | Modified | ?lang= param w loadProtectionPackages() |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| `_en` keys nie w REST response | Czyste API — frontend nie wie o wariantach, dostaje już rozwiązany string | Brak zmian w JS poza lang= param |
|
||||
| `map_error_message` przez `__()` a nie statyczne EN | Użyj istniejącej infrastruktury textdomain — Phase 18 automatycznie dostarcza tłumaczenia | Jeden słownik tłumaczeń, jedno miejsce aktualizacji |
|
||||
| `document.documentElement.lang` zamiast Polylang-specific | Niezależne od konkretnego pluginu i18n; Polylang, TranslatePress, WPML wszystkie ustawiają html@lang | Future-proof przy zmianie wtyczki tłumaczeniowej |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 0 | — |
|
||||
| Scope additions | 1 | Dodano `extract_softra_message()` — plan zakładał prostsze podejście, ale Softra zwraca różne struktury JSON |
|
||||
| Deferred | 0 | — |
|
||||
|
||||
### Scope additions
|
||||
|
||||
**1. [Robustness] Helper `extract_softra_message()`**
|
||||
- Found during: Task 2b (integracja mapowania w request())
|
||||
- Issue: Softra API zwraca różne struktury błędu (message w root, zagnieżdżone w error, albo raw string) — prosty `$body['message']` by nie pokrył wszystkich przypadków
|
||||
- Fix: Dedykowany parser z iteracją przez typowe klucze (`message`, `error`, `details`, `description`) + rekurencja dla stringa JSON
|
||||
- Files: class-softra-api.php
|
||||
- Verification: Edge cases sprawdzone w głowie — array passthrough, string JSON, nested, empty fallback
|
||||
- Impact: +30 linii kodu, ale gwarantuje że mapowanie działa niezależnie od variantu odpowiedzi Softra
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Kompletna infrastruktura dla EN — Phase 18 tylko generuje `.po` → `.mo` i wrzuca w `languages/`
|
||||
- Pakiety ochronne (SOFT/PREMIUM) — admin wypełni pola EN, frontend natychmiast pokaże (nie czeka na .mo)
|
||||
- Błędy Softra — słownik 13 msgid gotowy do tłumaczenia w .po
|
||||
|
||||
**Concerns:**
|
||||
- Jeśli w przyszłości Softra doda nowy komunikat → słownik trzeba ręcznie rozszerzać (mamy fuzzy prefix match, ale nie pokrywa wszystkiego)
|
||||
- Performance `loadProtectionPackages()` — doszedł `?lang=` param; nie wpływa na caching (WordPress nie cachuje tego endpointu, Phase 13 save też inwaliduje)
|
||||
- Admin EN pole może zostać puste → fallback do PL jest poprawny, ale trzeba to wyraźnie komunikować w UI (już jest: "Puste = fallback do wersji polskiej")
|
||||
|
||||
**Blockers:** None. Phase 18 może ruszyć.
|
||||
|
||||
---
|
||||
*Phase: 17-bilingual-packages-and-softra-errors, Plan: 01*
|
||||
*Completed: 2026-04-22*
|
||||
283
.paul/phases/18-en-translation/18-01-PLAN.md
Normal file
283
.paul/phases/18-en-translation/18-01-PLAN.md
Normal 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 5–50 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 16–17 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 1–2 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>
|
||||
168
.paul/phases/18-en-translation/18-01-SUMMARY.md
Normal file
168
.paul/phases/18-en-translation/18-01-SUMMARY.md
Normal 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*
|
||||
303
.paul/phases/19-extras-translations-admin/19-01-PLAN.md
Normal file
303
.paul/phases/19-extras-translations-admin/19-01-PLAN.md
Normal file
@@ -0,0 +1,303 @@
|
||||
---
|
||||
phase: 19-extras-translations-admin
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["17-01"]
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Panel administratora do tłumaczenia nazw dodatkowych opcji (extras) zwracanych dynamicznie z API Softra. Plugin auto-zbiera wszystkie napotkane polskie nazwy extras do opcji WP (`carei_extras_seen`). W panelu `Rezerwacje → Tłumaczenia extras` admin widzi listę zebranych nazw PL i obok pole EN do wypełnienia. Gdy frontend pobiera pricelist w locale EN, REST endpoint automatycznie podmienia PL na EN override'y (fallback: PL gdy brak tłumaczenia).
|
||||
|
||||
## Purpose
|
||||
Phase 18 przetłumaczyła statyczne stringi pluginu. Phase 17 zrobiła słownik błędów Softra (13 ręcznych mapowań). Ale dynamiczne pozycje z `/pricelist/list` (fotelik, GPS, dodatkowy kierowca, łańcuchy śniegowe itp.) są nadal po polsku w wersji EN — bo backend Softra zawsze odpowiada po polsku. Phase 19 zamyka tę lukę: admin ma UI gdzie raz wpisze tłumaczenia, plugin używa ich automatycznie.
|
||||
|
||||
## Output
|
||||
- Option `carei_extras_seen` (array of unique PL names, updated runtime przy każdym pricelist request)
|
||||
- Option `carei_extras_translations` (map PL → EN, managed przez admin)
|
||||
- Submenu `wp-admin → Rezerwacje → Tłumaczenia extras` z formularzem: lista seen names + input EN + submit
|
||||
- Helper `Carei_Admin_Panel::translate_extra_name($pl_name, $locale)` — wg locale + override'u, z fallbackiem do PL
|
||||
- `Carei_REST_Proxy` przy zwracaniu pricelist: dla każdego `$item['name']` → dopisuje do seen + podmienia na EN jeśli locale = en
|
||||
- Frontend bez zmian (JS już ma `?lang=` w requeście)
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
@.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md
|
||||
@.paul/phases/18-en-translation/18-01-SUMMARY.md
|
||||
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Auto-collect PL names
|
||||
```gherkin
|
||||
Given frontend woła `/wp-json/carei/v1/pricelist` (PL lub EN)
|
||||
When Softra API zwraca `additionalItems` z polskimi nazwami
|
||||
Then każda unikalna nazwa jest dopisana do opcji `carei_extras_seen`
|
||||
And opcja jest arrayem stringów (deduplikowana)
|
||||
And zapis do `carei_extras_seen` NIE blokuje response (fire-and-forget lub szybki update_option)
|
||||
And kolejne żądania nie duplikują wpisów
|
||||
```
|
||||
|
||||
## AC-2: Panel admin zarządza tłumaczeniami
|
||||
```gherkin
|
||||
Given admin wchodzi w `wp-admin → Rezerwacje → Tłumaczenia extras`
|
||||
When widzi listę zebranych PL nazw
|
||||
Then dla każdej nazwy jest pole `text` z obecnym tłumaczeniem EN (lub puste)
|
||||
And istnieje przycisk `Zapisz tłumaczenia`
|
||||
And nagłówek strony i etykiety są po EN/PL zgodnie z admin locale (przez __())
|
||||
|
||||
When admin wpisuje tłumaczenia i klika Zapisz
|
||||
Then opcja `carei_extras_translations` zostaje zaktualizowana (sanitize_text_field na każdym value)
|
||||
And redirect z flagą `?carei_saved=1` pokazuje komunikat "Zapisano."
|
||||
And przy następnej wizycie panelu tłumaczenia są wyświetlone zgodnie z zapisem
|
||||
```
|
||||
|
||||
## AC-3: REST pricelist zwraca EN nazwy gdy są override
|
||||
```gherkin
|
||||
Given w `carei_extras_translations` są zapisane tłumaczenia np. "Fotelik dziecięcy" → "Child car seat"
|
||||
When frontend EN (Polylang = EN albo `?lang=en`) woła `/pricelist`
|
||||
Then response zawiera `additionalItems` z `name` podmienionym wg override
|
||||
And dla nazw bez override response zawiera oryginalny PL (graceful fallback)
|
||||
And inne pola pozycji (`price`, `code`, `maxPrice` itp.) nietknięte
|
||||
|
||||
When frontend PL woła ten sam endpoint
|
||||
Then response zawiera oryginalne PL nazwy (bez zmian względem obecnego zachowania — zero regresji)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Admin panel submenu + helper + zapisy w options</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-admin-panel.php</files>
|
||||
<action>
|
||||
1. Dodaj stałe klasy:
|
||||
```php
|
||||
const EXTRAS_SEEN_OPTION = 'carei_extras_seen';
|
||||
const EXTRAS_TRANSLATIONS_OPTION = 'carei_extras_translations';
|
||||
```
|
||||
2. Dodaj static helpery:
|
||||
```php
|
||||
public static function get_extras_seen() {
|
||||
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
|
||||
return is_array( $seen ) ? array_values( array_unique( array_filter( array_map( 'strval', $seen ) ) ) ) : array();
|
||||
}
|
||||
|
||||
public static function get_extras_translations() {
|
||||
$map = get_option( self::EXTRAS_TRANSLATIONS_OPTION, array() );
|
||||
return is_array( $map ) ? $map : array();
|
||||
}
|
||||
|
||||
public static function remember_extra_name( $pl_name ) {
|
||||
$pl_name = trim( (string) $pl_name );
|
||||
if ( $pl_name === '' ) return;
|
||||
$seen = self::get_extras_seen();
|
||||
if ( ! in_array( $pl_name, $seen, true ) ) {
|
||||
$seen[] = $pl_name;
|
||||
sort( $seen );
|
||||
update_option( self::EXTRAS_SEEN_OPTION, $seen, false ); // autoload=false (może być duża lista)
|
||||
}
|
||||
}
|
||||
|
||||
public static function translate_extra_name( $pl_name, $locale = null ) {
|
||||
if ( $locale === null ) {
|
||||
$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||
$locale = ( 0 === strpos( (string) $locale, 'en' ) ) ? 'en' : 'pl';
|
||||
}
|
||||
if ( $locale !== 'en' ) return $pl_name;
|
||||
$map = self::get_extras_translations();
|
||||
if ( isset( $map[ $pl_name ] ) && $map[ $pl_name ] !== '' ) {
|
||||
return $map[ $pl_name ];
|
||||
}
|
||||
return $pl_name; // fallback
|
||||
}
|
||||
```
|
||||
3. Zarejestruj submenu w `register_protection_packages_page()` lub osobną metodą `register_extras_translations_page()` (prefer osobną dla czystości):
|
||||
```php
|
||||
add_action( 'admin_menu', array( $this, 'register_extras_translations_page' ) );
|
||||
add_action( 'admin_post_carei_save_extras_translations', array( $this, 'handle_extras_translations_save' ) );
|
||||
```
|
||||
I w konstruktorze dopisz akcje.
|
||||
4. Metoda `register_extras_translations_page()`:
|
||||
```php
|
||||
public function register_extras_translations_page() {
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=' . self::POST_TYPE,
|
||||
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||
'manage_options',
|
||||
'carei-extras-translations',
|
||||
array( $this, 'render_extras_translations_page' )
|
||||
);
|
||||
}
|
||||
```
|
||||
5. Metoda `render_extras_translations_page()`:
|
||||
- Tytuł strony, komunikat `?carei_saved=1`
|
||||
- Formularz POST do `admin-post.php` z nonce
|
||||
- Tabela 2 kolumny: `Nazwa PL` (readonly text), `Nazwa EN` (input text)
|
||||
- Dla każdej nazwy z `get_extras_seen()` — wiersz z inputem `name="translations[PL_NAME]"` i obecnym value
|
||||
- Submit button `Zapisz tłumaczenia`
|
||||
- Info tekst: "Puste pole = fallback do wersji polskiej w wersji EN strony."
|
||||
- Jeśli `get_extras_seen()` pusty: info "Brak zebranych pozycji — otwórz formularz rezerwacji aby załadować pricelist."
|
||||
6. Metoda `handle_extras_translations_save()`:
|
||||
- Check nonce + capability
|
||||
- Iteracja po `$_POST['translations']` (array) — każdy klucz PL, value EN
|
||||
- Sanitize: `sanitize_text_field( wp_unslash( $value ) )`
|
||||
- Zbuduj clean map
|
||||
- `update_option( self::EXTRAS_TRANSLATIONS_OPTION, $clean, false )`
|
||||
- Redirect z `?carei_saved=1`
|
||||
7. Wszystkie user-facing stringi przez `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`.
|
||||
|
||||
Unikaj:
|
||||
- Blokowania request frontendu na zapis seen — `update_option` z autoload=false jest szybki, ale jeśli zaburza latency — użyj `wp_cache_set` przed `update_option` (mikro-optymalizacja, na razie pomiń)
|
||||
- Duplikowania nazw (array_unique)
|
||||
- Auto-tłumaczenia przez AI (scope tego planu: tylko admin override)
|
||||
</action>
|
||||
<verify>
|
||||
1. `wp-admin → Rezerwacje → Tłumaczenia extras` — strona się renderuje, widać info "Brak zebranych pozycji" (bo option jeszcze pusta)
|
||||
2. `php -l` class-admin-panel.php → No syntax errors
|
||||
3. Wywołanie helpera: `var_dump( Carei_Admin_Panel::translate_extra_name( 'Test', 'en' ) )` → `'Test'` (fallback)
|
||||
</verify>
|
||||
<done>AC-2 (częściowo — render panelu), infrastruktura dla AC-1 i AC-3.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: REST /pricelist auto-collect + per-locale name replacement</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-rest-proxy.php</files>
|
||||
<action>
|
||||
1. Znajdź handler endpointu `/pricelist` (prawdopodobnie `get_pricelist()` lub podobna metoda) w class-rest-proxy.php.
|
||||
2. Przed zwrotem response — iteracja po `additionalItems` (lub odpowiedniku):
|
||||
```php
|
||||
$locale = $this->resolve_locale( $request ); // helper z Phase 17
|
||||
$translations = Carei_Admin_Panel::get_extras_translations();
|
||||
foreach ( $pricelists as &$pricelist ) {
|
||||
if ( ! isset( $pricelist['additionalItems'] ) || ! is_array( $pricelist['additionalItems'] ) ) continue;
|
||||
foreach ( $pricelist['additionalItems'] as &$item ) {
|
||||
if ( ! isset( $item['name'] ) || ! is_string( $item['name'] ) ) continue;
|
||||
$pl_name = trim( $item['name'] );
|
||||
if ( $pl_name === '' ) continue;
|
||||
|
||||
// Auto-collect
|
||||
Carei_Admin_Panel::remember_extra_name( $pl_name );
|
||||
|
||||
// Translate if locale=en and override exists
|
||||
if ( $locale === 'en' && isset( $translations[ $pl_name ] ) && $translations[ $pl_name ] !== '' ) {
|
||||
$item['name'] = $translations[ $pl_name ];
|
||||
}
|
||||
}
|
||||
unset( $item );
|
||||
}
|
||||
unset( $pricelist );
|
||||
```
|
||||
3. Upewnij się że resolve_locale działa tutaj (jest publiczny lub private dostępny w tej samej klasie — Phase 17 dodał jako private, więc OK bo to ta sama klasa).
|
||||
4. Struktura response NIE zmienia się — frontend JS dostaje te same klucze, tylko `name` może być podmienione.
|
||||
5. Jeśli Softra zwraca `error` zamiast success — nie ruszamy, passthrough.
|
||||
|
||||
Unikaj:
|
||||
- Modyfikacji cached response (jeśli pricelist jest cached — lepiej żeby cache też uwzględniał locale, ale na razie zostaw cache PL a mapowanie w locie; jeśli `/pricelist` NIE ma cache po stronie PHP, to proste)
|
||||
- Dodawania `name_en` w response — zachowujemy schemat (tylko `name` rozwiązane per locale)
|
||||
</action>
|
||||
<verify>
|
||||
1. `php -l` class-rest-proxy.php → No syntax errors
|
||||
2. Wywołanie z fronta w PL: pricelist names po polsku (bez zmian)
|
||||
3. Wywołanie z fronta w EN: nazwy spotkane po raz pierwszy → zapisane w `carei_extras_seen`, nadal po polsku w response (bo brak tłumaczeń)
|
||||
4. Po wypełnieniu tłumaczeń w adminie i kolejnym requeście EN: nazwy z override podmienione
|
||||
</verify>
|
||||
<done>AC-1 i AC-3 satysfakcjonowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- Submenu `wp-admin → Rezerwacje → Tłumaczenia extras` z listą zebranych nazw + pola EN
|
||||
- Helpery w Admin_Panel: `remember_extra_name()`, `get_extras_seen()`, `get_extras_translations()`, `translate_extra_name()`
|
||||
- Auto-collect: każdy request `/pricelist` zbiera unikalne nazwy PL do option `carei_extras_seen`
|
||||
- Per-locale replacement: EN requesty dostają override z `carei_extras_translations` (fallback: PL)
|
||||
- Zero zmian w JS frontendu (już jest `?lang=` z Phase 17)
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Deploy 2 pliki PHP (`class-admin-panel.php`, `class-rest-proxy.php`) przez SFTP
|
||||
2. **Auto-collect test:**
|
||||
- Otwórz stronę z modalem rezerwacji w PL, wypełnij krok 1 (daty + oddział + klasa) — pricelist się załaduje
|
||||
- Wejdź `wp-admin → Rezerwacje → Tłumaczenia extras` → lista PL nazw jest wypełniona (powinno być ~5–15 pozycji, zależnie od Softra)
|
||||
3. **Admin zapis:**
|
||||
- Wypełnij kilka tłumaczeń EN (np. "Fotelik dziecięcy" → "Child car seat", "GPS / Nawigacja" → "GPS / Navigation", "Dodatkowy kierowca" → "Additional driver")
|
||||
- Kliknij "Zapisz tłumaczenia" → komunikat "Zapisano" pojawia się
|
||||
- Odśwież stronę → tłumaczenia wciąż tam
|
||||
4. **Frontend PL:**
|
||||
- Modal w PL, sekcja "Opcje dodatkowe" — nazwy po polsku (identyczne jak przed zmianą)
|
||||
5. **Frontend EN:**
|
||||
- Polylang switcher → EN
|
||||
- Modal otwiera się, "Additional options" → nazwy które wypełniłeś = EN, reszta = PL (fallback)
|
||||
- `document.documentElement.lang` = "en-*" → `?lang=en` w requeście → backend zwraca EN names
|
||||
6. **DevTools Network:**
|
||||
- `GET /wp-json/carei/v1/pricelist?...&lang=en` → response zawiera `additionalItems[i].name` z EN (gdzie jest override) lub PL (gdzie brak)
|
||||
- `GET ...&lang=pl` → wszystko po PL
|
||||
7. **Test pustego override:**
|
||||
- Wyczyść jedno pole EN w adminie, zapisz
|
||||
- Frontend EN → ta pozycja wraca do PL (fallback)
|
||||
8. **Zero regresji:**
|
||||
- Pełen flow rezerwacji w PL działa bez zmian
|
||||
- Panel pakietów ochronnych (Phase 13/17) bez zmian
|
||||
- Inne widgety (mapa, miasta, oddziały) bez zmian
|
||||
|
||||
**Kryterium przejścia:** admin może raz wypełnić tłumaczenia → frontend EN automatycznie używa. Zero regresji w PL.
|
||||
</how-to-verify>
|
||||
<resume-signal>Napisz "approved" aby zamknąć plan, albo opisz problemy.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Phase 17 struktura `Carei_REST_Proxy::resolve_locale()` (reuse, nie refactor)
|
||||
- Phase 17 helper `Carei_Softra_API::map_error_message()` — osobny mechanizm dla błędów
|
||||
- Phase 13 panel pakietów ochronnych — osobna strona w menu
|
||||
- Schema CPT `carei_reservation`, meta keys, slugi statusów
|
||||
- Struktura payloadów do Softra (klucze JSON `additionalItems`, `price`, `code` itp.) — tylko `name` per locale
|
||||
- Frontend JS — bez zmian (już pobiera z `?lang=`)
|
||||
- Phase 18 `.po`/`.mo` — statyczne stringi, nie ruszamy
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie tłumaczymy nazw krajów w sekcji wyjazdu zagranicznego (lookup keys, dane biznesowe COUNTRY_FLAGS)
|
||||
- Nie tłumaczymy nazw klas pojazdów z Softra (np. „Opel Astra Combi") — dane biznesowe
|
||||
- Nie auto-tłumaczymy przez AI — tylko admin override
|
||||
- Nie dodajemy CSV export/import tłumaczeń — jeśli user zgłosi potrzebę, osobna faza
|
||||
- Nie cacheujemy mapy tłumaczeń separately — WP options autoload cache wystarczy
|
||||
- Nie ruszamy `/protection-packages` endpoint (Phase 17 territory)
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed zamknięciem planu:
|
||||
- [ ] `grep -n "carei_extras_seen\|carei_extras_translations" includes/` → oba klucze w kodzie
|
||||
- [ ] Panel `wp-admin → Rezerwacje → Tłumaczenia extras` renderuje się bez błędów
|
||||
- [ ] Auto-collect działa (po jednym request pricelist, lista seen nie pusta)
|
||||
- [ ] Save tłumaczeń update'uje option z komunikatem success
|
||||
- [ ] REST pricelist PL = PL names, EN z override = EN, EN bez override = PL fallback
|
||||
- [ ] `php -l` na obu plikach → No syntax errors
|
||||
- [ ] Zero regresji w PL (human-verify)
|
||||
- [ ] AC-1, AC-2, AC-3 pass
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 2 auto tasks ukończone
|
||||
- Checkpoint human-verify zatwierdzony
|
||||
- Plugin pozwala adminowi zarządzać tłumaczeniami extras przez UI
|
||||
- Kompletna dwujęzyczność (Phase 16+17+18+19 razem) — żadna część UI nie pokazuje PL gdy user jest w EN (poza świadomymi wyjątkami: nazwy miast, krajów, klas pojazdów)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md`
|
||||
</output>
|
||||
129
.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md
Normal file
129
.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
phase: 19-extras-translations-admin
|
||||
plan: 01
|
||||
subsystem: i18n
|
||||
tags: [admin-panel, extras, polylang, softra-pricelist, bilingual]
|
||||
|
||||
requires:
|
||||
- phase: 17-bilingual-packages-and-softra-errors
|
||||
provides: resolve_locale() helper w REST proxy
|
||||
|
||||
provides:
|
||||
- Option carei_extras_seen (auto-collected PL names z Softra pricelist)
|
||||
- Option carei_extras_translations (admin override PL → EN)
|
||||
- Submenu wp-admin → Rezerwacje → Tłumaczenia extras
|
||||
- Helpery: remember_extra_name, get_extras_seen, get_extras_translations, translate_extra_name
|
||||
- REST /pricelist per-locale name replacement z fallbackiem do PL
|
||||
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Auto-collect + admin override: seen list zbiera się runtime, admin wypełnia override, fallback do oryginału"
|
||||
- "Option z autoload=false — lista seen może rosnąć, nie blokuje boot WP"
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
|
||||
key-decisions:
|
||||
- "Auto-collect przez update_option z autoload=false — prosty, brak wymogu osobnej tabeli DB"
|
||||
- "Sortowanie seen alphabetically z SORT_NATURAL | SORT_FLAG_CASE — przyjazna kolejność w UI"
|
||||
- "Zero zmian w JS frontendu — `?lang=` z Phase 17 wystarcza"
|
||||
- "Nonce + sanitize_text_field na EN inputach — standardowa walidacja WP"
|
||||
- "Fallback do PL dla pustych/niezdefiniowanych override'ów — graceful degradation"
|
||||
|
||||
patterns-established:
|
||||
- "Admin panel UI pattern: lista seen (readonly) + input override (editable) + submit z nonce"
|
||||
|
||||
duration: ~20min
|
||||
started: 2026-04-22
|
||||
completed: 2026-04-22
|
||||
---
|
||||
|
||||
# Phase 19 Plan 01: Extras translations admin panel — Summary
|
||||
|
||||
**Panel `wp-admin → Rezerwacje → Tłumaczenia extras` pozwala administratorowi zarządzać tłumaczeniami dynamicznych nazw opcji dodatkowych zwracanych z Softra API. Auto-collect PL nazw + override EN + fallback do PL. Milestone v0.8 — 100% complete.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~20min |
|
||||
| Tasks | 2 auto + 1 human-verify completed |
|
||||
| Files modified | 2 |
|
||||
| New options in WP DB | 2 (seen, translations) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Auto-collect PL names | Pass | Każdy request `/pricelist` dopisuje nowe nazwy do `carei_extras_seen` |
|
||||
| AC-2: Panel admin zarządza tłumaczeniami | Pass | Submenu, formularz z nonce, sanitize, redirect z komunikatem |
|
||||
| AC-3: REST pricelist zwraca EN gdy override | Pass | Per-locale replacement z fallbackiem do PL |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **2 nowe WP options:** `carei_extras_seen` (lista PL names) + `carei_extras_translations` (map PL → EN)
|
||||
- **4 static helpery** w `Carei_Admin_Panel` — czysta API do używania z innych klas
|
||||
- **Submenu admin** z pełnym UX: lista alfabetyczna, info placeholder dla pustej listy, komunikaty sukcesu, nonce protection
|
||||
- **REST `/pricelist` integration** — runtime auto-collect + per-locale replacement, bez dotykania JS frontendu
|
||||
- **Reuse Phase 17** `resolve_locale()` helper — brak duplikacji logiki
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `includes/class-admin-panel.php` | Modified | Stałe, helpery, submenu, render, save handler |
|
||||
| `includes/class-rest-proxy.php` | Modified | `get_pricelist()` auto-collect + per-locale replacement |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| WP options zamiast custom table | Lista seen i map override są małe (<100 wpisów) — nadmierna inżynieria | Szybki deploy, standardowy WP pattern |
|
||||
| `autoload=false` dla options | Lista seen rośnie z czasem — nie ładujemy jej przy każdym request | Zero impact na WP boot performance |
|
||||
| Fallback do PL dla pustego override | User może nie tłumaczyć wszystkiego — nigdy nie crashujemy | Graceful degradation, spójna z Phase 17 |
|
||||
| Sortowanie SORT_NATURAL | UX: "Fotelik 1+2" przed "Fotelik 10" w liście | Przyjazna kolejność w panelu admin |
|
||||
| Zero zmian w JS | Phase 17 `?lang=` już działa, frontend nie musi wiedzieć o override | Minimalna surface area zmian |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 0 | — |
|
||||
| Scope additions | 0 | — |
|
||||
| Deferred | 0 | — |
|
||||
|
||||
**Total impact:** Plan wykonany 1:1.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Milestone v0.8 COMPLETE.**
|
||||
|
||||
**System tłumaczeń teraz kompletny:**
|
||||
- Statyczne stringi PHP/JS: `.po`/`.mo` (Phase 16+18)
|
||||
- Pakiety ochronne: pola `_en` w DB (Phase 17)
|
||||
- Błędy Softra: słownik 13 wpisów w `map_error_message()` (Phase 17)
|
||||
- Dynamiczne extras: admin override (Phase 19)
|
||||
|
||||
**Co pozostaje po polsku w wersji EN (świadome):**
|
||||
- Nazwy miast w widgetach mapa/miasta/oddziały — dane biznesowe
|
||||
- Nazwy krajów w sekcji wyjazdu zagranicznego — lookup COUNTRY_FLAGS
|
||||
- Nazwy klas pojazdów z Softra (np. "Opel Astra Combi") — dane biznesowe
|
||||
|
||||
**Concerns:** None. Admin musi wypełnić override dla nowych Softra extras — akceptowalny manual overhead.
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 19-extras-translations-admin, Plan: 01*
|
||||
*Completed: 2026-04-22*
|
||||
Reference in New Issue
Block a user