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