Files
carei.pagedev.pl/.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md
2026-04-22 22:00:50 +02:00

20 KiB
Raw Blame History

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
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)
false off
## 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 ~80150 wpisów gotowych do tłumaczenia
  • Strona po polsku działa identycznie jak przed zmianami (żaden tekst się nie zmienia — tylko jest owinięty)
@.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

<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
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ę

<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>
Po zakończeniu: `.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md`