update
This commit is contained in:
@@ -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*
|
||||
Reference in New Issue
Block a user