20 KiB
20 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
| phase | plan | type | wave | depends_on | files_modified | autonomous | delegation | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16-i18n-plugin-refactor | 01 | execute | 1 |
|
false | off |
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ładujeload_plugin_textdomainnaplugins_loaded carei-reservation.jsnie ma hardkodowanych stringów PL — używa obiektucareiI18nclass-elementor-widget.phpenqueuewp_localize_script('carei-reservation', 'careiI18n', [...])z kluczami zmapowanymi 1:1 na użycia w JSlanguages/carei-reservation.potzawiera ~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)
<acceptance_criteria>
AC-1: PHP i18n kompletny, textdomain załadowany
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
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
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>
Task 1: PHP i18n — wrap stringów + textdomain bootstrap 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 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.
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
AC-1 satysfakcjonowane.
Task 2: JS i18n — migracja stringów do careiI18n przez wp_localize_script
wp-content/plugins/carei-reservation/assets/js/carei-reservation.js,
wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
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
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)
AC-2 satysfakcjonowane.
Task 3: Generate .pot file w languages/
wp-content/plugins/carei-reservation/languages/carei-reservation.pot
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.
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 ""`
AC-3 satysfakcjonowane: .pot gotowy dla Phase 18 (tłumaczenie).
- 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
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.
Napisz "approved" aby zamknąć plan, albo opisz które miejsca pokazują nietłumaczone / regresyjne stringi.
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
<success_criteria>
- Wszystkie 3 auto tasks zakończone
- Checkpoint human-verify zatwierdzony ("approved")
- Brak regresji w języku polskim
.potgotowy do Phase 18 </success_criteria>