diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 8df1658..f5b7fe0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -18,7 +18,7 @@ None - [x] **Phase 6: Email System** - Legacy HTML table format generation and wp_mail() integration - [x] **Phase 7: Captcha & Validation** - reCAPTCHA v2/v3, hCaptcha, inline validation, i18n - [x] **Phase 8: Bug Fixes & Legacy Parity** - Session bug fix, additional work sections, Sonstiges free text -- [ ] **Phase 9: Internationalization** - i18n with gettext, German/English translation files +- [x] **Phase 9: Internationalization** - i18n with gettext, German/English translation files ## Phase Details @@ -110,12 +110,12 @@ Plans: **Depends on**: Phase 8 **Research**: Unlikely (WordPress i18n is well-documented) **Gap Closure**: Closes REQ-7 (i18n support) from v1.0 audit -**Plans**: 2 plans -**Status**: Planning complete +**Plans**: 2/2 complete +**Status**: Complete Plans: -- [ ] 09-01-PLAN.md -- i18n infrastructure, text domain loading, and admin/infrastructure string wrapping -- [ ] 09-02-PLAN.md -- Form-facing string wrapping, JS localization, email locale forcing, translation files +- [x] 09-01: i18n infrastructure, text domain loading, and admin/infrastructure string wrapping +- [x] 09-02: Form-facing string wrapping, JS localization, email locale forcing, translation files ## Progress @@ -129,4 +129,4 @@ Plans: | 6. Email System | 1/1 | Complete | 2026-01-16 | | 7. Captcha & Validation | 1/1 | Complete | 2026-01-16 | | 8. Bug Fixes & Legacy Parity | 2/2 | Complete | 2026-02-06 | -| 9. Internationalization | 0/2 | Planning complete | - | +| 9. Internationalization | 2/2 | Complete | 2026-02-07 | diff --git a/.planning/phases/09-i18n/09-VERIFICATION.md b/.planning/phases/09-i18n/09-VERIFICATION.md new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/.planning/phases/09-i18n/09-VERIFICATION.md @@ -0,0 +1,180 @@ +--- +phase: 09-i18n +verified: 2026-02-07T00:00:00Z +status: gaps_found +score: 6/7 must-haves verified +gaps: + - truth: "All form-facing strings are wrapped in gettext functions" + status: partial + reason: "wp_die error message in form handler contains hardcoded German strings" + artifacts: + - path: "includes/class-form-handler.php" + issue: "Lines 120-127: wp_die error page has hardcoded German (E-Mail konnte nicht versendet werden, Ihre Anfrage wurde gespeichert, Bitte kontaktieren Sie uns telefonisch, Zurück zur Startseite, E-Mail-Fehler)" + missing: + - "Wrap wp_die title and message in gettext: esc_html__('Email could not be sent', ...), esc_html__('Your request has been saved...', ...), etc." + - "Wrap 'Please contact us by phone:', 'Back to homepage', 'Email Error' strings" +--- + +# Phase 09: Internationalization Verification Report + +**Phase Goal:** Wrap all user-facing strings in gettext functions, create .pot/.po/.mo translation files, load text domain, provide German and English translations + +**Verified:** 2026-02-07T00:00:00Z +**Status:** gaps_found +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 1 | Plugin text domain is 'siegel-umzugsliste' matching folder name convention | ✓ VERIFIED | Plugin header line 7 declares `Text Domain: siegel-umzugsliste` | +| 2 | Text domain loads on init hook via load_plugin_textdomain() | ✓ VERIFIED | umzugsliste.php lines 24-31: function siegel_umzugsliste_load_textdomain() hooked on init with priority 1 | +| 3 | change_locale hook reloads plugin text domain (workaround for WP core bug #39210) | ✓ VERIFIED | umzugsliste.php lines 37-44: unload_textdomain + load_plugin_textdomain on change_locale action | +| 4 | All admin-facing strings are wrapped in gettext functions | ✓ VERIFIED | CPT labels (13 strings), admin menu (3 strings), settings page (20+ strings), date helpers (3 strings) all wrapped | +| 5 | All form-facing strings are wrapped in gettext functions | ⚠️ PARTIAL | 189+ form strings wrapped EXCEPT wp_die error message has 5 hardcoded German strings (lines 120-127 in class-form-handler.php) | +| 6 | JavaScript validation messages come from PHP via wp_localize_script | ✓ VERIFIED | class-shortcode.php line 85-90: wp_localize_script with 4 validation messages; form.js line 13 uses umzugslisteL10n | +| 7 | Email content is always generated in German regardless of site locale | ✓ VERIFIED | class-form-handler.php line 329: switch_to_locale('de_DE'), line 354: restore_previous_locale(); email generator has 0 gettext calls (hardcoded German) | + +**Score:** 6/7 truths verified (1 partial) + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|----------|--------|---------| +| `umzugsliste.php` | Text domain loading, change_locale hook, updated plugin header | ✓ VERIFIED | 46 lines, text domain 'siegel-umzugsliste', both hooks present, syntax valid | +| `includes/class-cpt.php` | Translatable CPT labels | ✓ VERIFIED | 70 lines, 13 gettext calls for labels (Entries, Entry, Add New, etc.), all with text domain | +| `includes/class-admin-menu.php` | Translatable admin menu strings | ✓ VERIFIED | 80 lines, 6 gettext calls (Moving List, Entries, Settings), all wrapped in __() | +| `includes/class-settings.php` | Translatable settings page strings | ✓ VERIFIED | 280+ lines, 20+ gettext calls for labels/descriptions (Email Settings, Receiver Email, etc.) | +| `includes/class-date-helpers.php` | Translatable date label strings | ✓ VERIFIED | 90 lines, 3 gettext calls (Day, Month, Year) wrapped in esc_html__() | +| `includes/class-furniture-data.php` | Translatable furniture names and room labels | ✓ VERIFIED | 296 lines, 163 gettext calls (7 rooms + 90+ furniture items + 50+ additional work labels) | +| `includes/class-form-renderer.php` | Translatable form UI strings | ✓ VERIFIED | 450+ lines, 43 gettext calls (headers, labels, buttons, validation messages, placeholders) | +| `includes/class-form-handler.php` | Translatable validation errors, email locale forcing | ⚠️ PARTIAL | 365 lines, switch_to_locale present, 14 validation messages wrapped BUT wp_die error message (5 strings) hardcoded German | +| `includes/class-shortcode.php` | wp_localize_script for JS translation strings | ✓ VERIFIED | 100+ lines, wp_localize_script present with 4 message keys (fieldRequired, invalidEmail, selectMovingDate, enterFurnitureItem) | +| `assets/js/form.js` | Uses localized strings from PHP instead of hardcoded German | ✓ VERIFIED | 320+ lines, line 13 defines l10n from umzugslisteL10n, 4 usages in validation code (lines 235, 242, 293, 306) | +| `languages/siegel-umzugsliste.pot` | POT template for Loco Translate | ✓ VERIFIED | 19KB, 224 msgid entries, UTF-8 encoding, proper header | +| `languages/siegel-umzugsliste-de_DE.po` | German translations source file | ✓ VERIFIED | 23KB, 222/224 strings translated (only empty msgid/metadata remain), proper German umlauts (Wohnzimmer, Küche, ä/ö/ü/ß) | +| `languages/siegel-umzugsliste-de_DE.mo` | Compiled German translations | ✓ VERIFIED | 14KB binary file, compiled from PO with msgfmt | + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|------|-----|--------|---------| +| umzugsliste.php | languages/ | load_plugin_textdomain path | ✓ WIRED | Line 28: dirname(plugin_basename(__FILE__)) . '/languages' | +| umzugsliste.php | change_locale action | add_action hook | ✓ WIRED | Line 37: add_action('change_locale', function...) with unload/reload | +| class-form-handler.php | switch_to_locale('de_DE') | locale switch before email | ✓ WIRED | Line 329: switch_to_locale('de_DE') before email generation | +| class-form-handler.php | restore_previous_locale() | locale restore after email | ✓ WIRED | Line 354: restore_previous_locale() after wp_mail | +| class-shortcode.php | assets/js/form.js | wp_localize_script passing strings | ✓ WIRED | Lines 85-90: wp_localize_script with umzugslisteL10n object | +| assets/js/form.js | umzugslisteL10n | references global for translations | ✓ WIRED | Line 13: defines l10n from global, 4 references in validation (235, 242, 293, 306) | +| class-form-renderer.php | class-furniture-data.php | get_rooms(), get_furniture_items() | ✓ WIRED | Lines 221, 235, 387: calls to Umzugsliste_Furniture_Data static methods | +| class-form-handler.php | class-email-generator.php | generate() | ✓ WIRED | Line 332: Umzugsliste_Email_Generator::generate($data) within locale switch | + +### Requirements Coverage + +| Requirement | Status | Blocking Issue | +|-------------|--------|----------------| +| REQ-7 (i18n support) | ⚠️ PARTIAL | wp_die error message gap prevents complete i18n coverage | + +### Anti-Patterns Found + +| File | Line | Pattern | Severity | Impact | +|------|------|---------|----------|--------| +| includes/class-form-handler.php | 120-127 | Hardcoded German strings in wp_die message | 🛑 Blocker | Error page cannot be translated; shows German to all users regardless of site locale | + +**Details:** +```php +// Lines 120-127: NOT WRAPPED IN GETTEXT +wp_die( + '
Ihre Anfrage wurde gespeichert, aber die E-Mail konnte nicht versendet werden.
+Bitte kontaktieren Sie uns telefonisch:
+Wiesbaden: (06 11) 2 20 20
+ Mainz: (0 61 31) 22 21 41