+**class-furniture-data.php** - Wrap room names and furniture item names in `__()`:
+
+Room names in `get_rooms()`:
+```php
+return array(
+ 'wohnzimmer' => __( 'Living Room', 'siegel-umzugsliste' ),
+ 'schlafzimmer' => __( 'Bedroom', 'siegel-umzugsliste' ),
+ 'arbeitszimmer' => __( 'Study', 'siegel-umzugsliste' ),
+ 'bad' => __( 'Bathroom', 'siegel-umzugsliste' ),
+ 'kueche_esszimmer' => __( 'Kitchen/Dining Room', 'siegel-umzugsliste' ),
+ 'kinderzimmer' => __( 'Children\'s Room', 'siegel-umzugsliste' ),
+ 'keller' => __( 'Basement/Storage/Garage', 'siegel-umzugsliste' ),
+);
+```
+
+Furniture item names in `get_all_furniture_data()`: Wrap every `'name'` value in `__()`. For example:
+```php
+array( 'name' => __( 'Sofa, Couch, per seat', 'siegel-umzugsliste' ), 'cbm' => 0.4, 'montage' => true ),
+```
+
+Do this for ALL furniture items across ALL rooms. Use English source strings. There are ~90 items total. Be thorough -- every single `'name' => '...'` must become `'name' => __( '...', 'siegel-umzugsliste' )`.
+
+Additional work section labels and field names in `get_additional_work()`: Wrap all `'label'` and `'name'` values in `__()`. For example:
+```php
+'montage' => array(
+ 'label' => __( 'Assembly Work', 'siegel-umzugsliste' ),
+ 'fields' => array(
+ array( 'name' => __( 'No assembly work required', 'siegel-umzugsliste' ), 'type' => 'checkbox' ),
+ ...
+ ),
+),
+```
+
+Do NOT wrap the `'type'` or `'key'` values -- only `'label'` and `'name'`.
+
+**class-form-renderer.php** - Wrap all hardcoded UI strings using English source strings:
+
+- `'Umzugsliste'` (h1) -> `esc_html__( 'Moving List', 'siegel-umzugsliste' )`
+- `'Voraussichtlicher Umzugstermin'` (legend) -> `esc_html__( 'Expected Moving Date', 'siegel-umzugsliste' )`
+- The privacy policy paragraph: Leave the company address and contact info as-is (not translatable -- company-specific). Wrap only the translatable text: `'In unserer'` -> not needed, this is a static paragraph with a link. Actually, wrap the full sentence text around the link. Use `sprintf` with `__()`:
+ ```php
+ printf(
+ /* translators: %s: link to privacy policy */
+ esc_html__( 'In our %s you can learn how Siegel Umzuege GmbH & Co. KG collects and uses your data.', 'siegel-umzugsliste' ),
+ '' . esc_html__( 'Privacy Policy', 'siegel-umzugsliste' ) . ''
+ );
+ ```
+ Wait -- actually, since the privacy paragraph is interleaved with HTML and company info, it's simpler to wrap just the key phrases. But the CONTEXT.md says English is source. Do the printf approach above.
+- `'Beladeadresse'` (h3) -> `esc_html__( 'Loading Address', 'siegel-umzugsliste' )`
+- `'Entladeadresse'` (h3) -> `esc_html__( 'Unloading Address', 'siegel-umzugsliste' )`
+- Address field labels: `'Name*'`, `'Strasse*'`, `'PLZ/Ort*'`, `'Geschoss'`, `'Telefon*'`, `'Telefax'`, `'Mobil'`, `'E-Mail*'`, `'Telefon'` -- wrap each in the `render_address_field()` call. Since labels are passed as parameters, wrap at the CALL SITE:
+ ```php
+ self::render_address_field( __( 'Name*', 'siegel-umzugsliste' ), 'bName', true );
+ ```
+ Actually, since these labels go into `esc_html()` inside `render_address_field()`, wrapping at the call site with `__()` is correct.
+- `'Lift'` label -> `__( 'Elevator', 'siegel-umzugsliste' )`
+- `'Nein'`/`'Ja'` radio labels in lift field -> `esc_html__( 'No', 'siegel-umzugsliste' )` / `esc_html__( 'Yes', 'siegel-umzugsliste' )`
+- `'*Pflichtfelder'` -> `esc_html__( '*Required fields', 'siegel-umzugsliste' )`
+- Table headers: `'Anzahl'`, `'Bezeichnung'`, `'qbm'`, `'Montage?'` -> wrap each in `esc_html__()`
+- `'Summe '` prefix in room totals footer -> `esc_html__( 'Total ', 'siegel-umzugsliste' )` (the room label is already translated from get_rooms)
+- `'Gesamtsumme'` heading -> `esc_html__( 'Grand Total', 'siegel-umzugsliste' )`
+- `'Gesamtsumme aller Zimmer'` -> `esc_html__( 'Grand total all rooms', 'siegel-umzugsliste' )`
+- `'Anfrage absenden'` button -> `esc_html__( 'Submit Request', 'siegel-umzugsliste' )`
+- `'Bitte korrigieren Sie folgende Fehler:'` -> `esc_html__( 'Please correct the following errors:', 'siegel-umzugsliste' )`
+- `'Sonstiges'` heading -> `esc_html__( 'Other', 'siegel-umzugsliste' )`
+- `'Weitere Hinweise oder Wuensche:'` label -> `esc_html__( 'Additional notes or requests:', 'siegel-umzugsliste' )`
+- `'Weitere Hinweise oder Wuensche...'` placeholder -> `esc_attr__( 'Additional notes or requests...', 'siegel-umzugsliste' )`
+- Radio labels in additional work: `'Abbau'`, `'Aufbau'`, `'Beides'` -> wrap in `esc_html__()`
+ - `'Abbau'` -> `esc_html__( 'Disassembly', 'siegel-umzugsliste' )`
+ - `'Aufbau'` -> `esc_html__( 'Assembly', 'siegel-umzugsliste' )`
+ - `'Beides'` -> `esc_html__( 'Both', 'siegel-umzugsliste' )`
+- `'Anz.'` placeholder -> `esc_attr__( 'Qty.', 'siegel-umzugsliste' )`
+
+Note: The `render_address_field()` method receives `$label` and uses `esc_html( $label )`. Since labels are now passed through `__()`, they'll be translated. No change needed to the method internals.
+
+Similarly, `render_lift_field()` has inline labels -- update those inline.
+
+**class-form-handler.php** - Wrap validation messages and error strings:
+
+- `'Security verification failed. Please try again.'` -- this is already English, just wrap: `__( 'Security verification failed. Please try again.', 'siegel-umzugsliste' )`
+- Captcha error: `'Captcha-Verifizierung fehlgeschlagen...'` -> `__( 'Captcha verification failed. Please try again.', 'siegel-umzugsliste' )`
+- Required field labels in `$required_fields` array -- wrap each label:
+ ```php
+ $required_fields = array(
+ 'bName' => __( 'Name (Loading Address)', 'siegel-umzugsliste' ),
+ 'bStrasse' => __( 'Street (Loading Address)', 'siegel-umzugsliste' ),
+ 'bort' => __( 'ZIP/City (Loading Address)', 'siegel-umzugsliste' ),
+ 'bTelefon' => __( 'Phone (Loading Address)', 'siegel-umzugsliste' ),
+ 'eName' => __( 'Name (Unloading Address)', 'siegel-umzugsliste' ),
+ 'eStrasse' => __( 'Street (Unloading Address)', 'siegel-umzugsliste' ),
+ 'eort' => __( 'ZIP/City (Unloading Address)', 'siegel-umzugsliste' ),
+ );
+ ```
+- Validation error messages:
+ - `'Pflichtfeld fehlt: '` -> Use `sprintf( __( 'Required field missing: %s', 'siegel-umzugsliste' ), $label )`
+ - `'Ungueltige E-Mail-Adresse'` -> `__( 'Invalid email address', 'siegel-umzugsliste' )`
+ - `'Umzugstermin fehlt'` -> `__( 'Moving date is missing', 'siegel-umzugsliste' )`
+ - `'Bitte geben Sie mindestens eine Moebelmenge ein'` -> `__( 'Please enter at least one furniture quantity', 'siegel-umzugsliste' )`
+- Email failure wp_die message: Wrap the HTML strings. Use `sprintf` and `__()` for the error page:
+ ```php
+ wp_die(
+ '' . esc_html__( 'Email could not be sent', 'siegel-umzugsliste' ) . '
'
+ . '' . esc_html__( 'Your request has been saved, but the email could not be sent.', 'siegel-umzugsliste' ) . '
'
+ . '' . esc_html__( 'Please contact us by phone:', 'siegel-umzugsliste' ) . '
'
+ . 'Wiesbaden: (06 11) 2 20 20
'
+ . 'Mainz: (0 61 31) 22 21 41
'
+ . '' . esc_html__( 'Back to homepage', 'siegel-umzugsliste' ) . '
',
+ esc_html__( 'Email Error', 'siegel-umzugsliste' )
+ );
+ ```
+- CPT post title: `'Anfrage vom '` -> Use `sprintf( __( 'Request from %s - %s', 'siegel-umzugsliste' ), $date_string, $customer_name )`. Actually, since CPT titles are internal/admin, keep them simple. But they should still be translatable. Actually, the CPT title is just stored data -- it doesn't need translation. But for admin display it's nice. Use sprintf with `__()`.
+
+**Email locale forcing in class-form-handler.php** - In the `send_email()` method, wrap the email generation call in locale switching:
+```php
+private function send_email( $entry_id, $data ) {
+ // Force German locale for email generation
+ switch_to_locale( 'de_DE' );
+
+ // Generate email HTML (all __() calls now return German)
+ $email_html = Umzugsliste_Email_Generator::generate( $data );
+
+ // Email subject also in German
+ $subject = 'Internetanfrage - Anfrage vom ' . date( 'd.m.Y H:i' );
+
+ // Restore original locale
+ restore_previous_locale();
+
+ // ... rest of send_email (get receiver, headers, wp_mail) stays the same
+}
+```
+
+The email subject line stays hardcoded in German -- do NOT wrap it in `__()`. The CONTEXT.md says email content always stays in German.
+
+**class-email-generator.php** - Do NOT wrap strings in this file with gettext. The email generator's strings (table headers like 'Anzahl', 'Bezeichnung', 'qbm', 'Gesamt', 'Montage?', 'Beladeadresse', 'Entladeadresse', 'Gesamtsummen', 'Summe', 'Sonstiges', 'Voraussichtlicher Umzugstermin', the HTML title) are email content that must ALWAYS be in German. Since `send_email()` switches to German locale before calling `generate()`, any `__()` calls in the data arrays (furniture names, room labels from `get_rooms()`) will automatically return German translations. The static strings in the email generator are already in German and should stay that way -- do not touch them.
+
+**class-shortcode.php** - Add `wp_localize_script()` to pass translated JS validation messages:
+
+In the `enqueue_assets()` method, AFTER the `wp_enqueue_script()` call for `'umzugsliste-form'`, add:
+
+```php
+wp_localize_script( 'umzugsliste-form', 'umzugslisteL10n', array(
+ 'fieldRequired' => __( 'This field is required', 'siegel-umzugsliste' ),
+ 'invalidEmail' => __( 'Please enter a valid email address', 'siegel-umzugsliste' ),
+ 'selectMovingDate' => __( 'Please select a complete moving date', 'siegel-umzugsliste' ),
+ 'enterFurnitureItem' => __( 'Please enter at least one furniture item', 'siegel-umzugsliste' ),
+) );
+```
+
+**assets/js/form.js** - Replace hardcoded German validation messages with the localized strings:
+
+- `'Dieses Feld ist erforderlich'` -> `umzugslisteL10n.fieldRequired`
+- `'Bitte geben Sie eine gueltige E-Mail-Adresse ein'` -> `umzugslisteL10n.invalidEmail`
+- `'Bitte waehlen Sie ein vollstaendiges Umzugsdatum'` -> `umzugslisteL10n.selectMovingDate`
+- `'Bitte geben Sie mindestens ein Moebelstueck ein'` -> `umzugslisteL10n.enterFurnitureItem`
+
+Check that the `umzugslisteL10n` variable is accessed safely -- it should always be defined since `wp_localize_script` runs before the script loads, but a defensive fallback is good practice:
+```javascript
+var l10n = typeof umzugslisteL10n !== 'undefined' ? umzugslisteL10n : {
+ fieldRequired: 'This field is required',
+ invalidEmail: 'Please enter a valid email address',
+ selectMovingDate: 'Please select a complete moving date',
+ enterFurnitureItem: 'Please enter at least one furniture item'
+};
+```
+Place this at the top of the IIFE, then use `l10n.fieldRequired` etc.
+
+