2 Commits

Author SHA1 Message Date
0902c5e1a5 fix: decode WordPress title entities before Twig to prevent double-encoding (v1.0.10)
All checks were successful
Create Release Package / PHP Lint (push) Successful in 1m10s
Create Release Package / Build Release (push) Successful in 1m50s
WordPress's get_the_title() pre-encodes & as &. Twig autoescape
re-encoded the & in & to &, rendering as literal &
in the browser. Wrapped all 6 get_the_title() calls in ContextBuilder
with wp_specialchars_decode() so Twig can properly re-encode once.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 20:20:19 +01:00
1a0a1fa63a i18n: add full translations for 13 locales (v1.0.10)
- Regenerated wp-bootstrap.pot with updated extractable strings
- Translated 13 locales: de_CH, de_CH_informal, de_DE, de_DE_informal,
  en_GB, es_ES, fr_CH, fr_FR, it_CH, it_IT, nl_NL, pl_PL, pt_PT
- German variants: Swiss (ss) vs Standard (ß), formal (Sie) vs informal (du)
- All 359 translatable strings covered per locale
- Documented fast translation workflow in CLAUDE.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 12:26:36 +01:00
18 changed files with 24920 additions and 10965 deletions

View File

@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
## [1.0.10] - 2026-02-25
### Fixed
- **Title double-encoding in Twig templates** (`inc/Template/ContextBuilder.php`): WordPress's `get_the_title()` pre-encodes `&` as `&#038;`. When passed to Twig with autoescape enabled, the `&` in `&#038;` was escaped again to `&amp;#038;`, rendering as literal `&#038;` in the browser (e.g. "Bewerbungen &#038; Nachrichten" instead of "Bewerbungen & Nachrichten"). Fixed by wrapping all 6 `get_the_title()` calls with `wp_specialchars_decode()` to decode WordPress entities before Twig. Twig autoescape then properly re-encodes `&``&amp;`. This is XSS-safe because Twig still escapes all output.
## [1.0.9] - 2026-02-19
### Performance

View File

@@ -98,6 +98,29 @@ Compiled .mo files are built by the Gitea CI/CD pipeline during releases. For lo
for po in languages/wp-bootstrap-*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
```
#### Updating Translations
When new strings are added to PHP sources, use the fast JSON workflow documented in
`wp-jobroom-theme/CLAUDE.md → Updating Translations (Fast JSON Workflow)`. That
document contains the full step-by-step process including the `patch-po.py` patcher script
(located in `wp-jobroom-theme/languages/patch-po.py`) which patches **both** `wp-bootstrap`
and `wp-jobroom-theme` `.po` files in a single pass.
**Quick reference for wp-bootstrap POT regeneration:**
```bash
docker exec jobroom-wordpress wp i18n make-pot \
/var/www/html/wp-content/themes/wp-bootstrap \
/var/www/html/wp-content/themes/wp-bootstrap/languages/wp-bootstrap.pot \
--allow-root
# Then merge into all .po files:
for locale in de_CH de_CH_informal de_DE de_DE_informal en_GB es_ES fr_CH fr_FR it_CH it_IT nl_NL pl_PL pt_PT; do
msgmerge --update --backup=none --no-fuzzy-matching \
languages/wp-bootstrap-${locale}.po languages/wp-bootstrap.pot
done
```
### Create Releases
**Important Git Notes:**
@@ -211,6 +234,20 @@ Build steps (in order):
## Session History
### Session 16 — v1.0.10 Title Double-Encoding Fix (2026-02-25)
**Completed:** Fixed double-encoding of HTML entities in page titles rendered through Twig.
**Root cause:** WordPress's `get_the_title()` returns titles with HTML entities pre-encoded (e.g. `&``&#038;`). `ContextBuilder` passed these pre-encoded strings to Twig as template variables. Twig's autoescape then re-encoded the `&` in `&#038;` to `&amp;#038;`, which browsers rendered as the literal text `&#038;` instead of `&`. Affected all pages with `&` in their title (e.g. help pages "Bewerbungen & Nachrichten", "Konto & Sicherheit", "Abonnements & Abrechnung").
**Fix:** Wrapped all 6 `get_the_title()` calls in `ContextBuilder.php` with `wp_specialchars_decode()`. This decodes WordPress entities back to raw characters before Twig, allowing Twig autoescape to properly encode them once. XSS-safe because Twig still escapes all output.
**Files modified:**
- `inc/Template/ContextBuilder.php``wp_specialchars_decode()` on all 6 `get_the_title()` calls
- `style.css` — version bump to 1.0.10
- `CHANGELOG.md` — v1.0.10 entry
### Session 15 — v1.0.9 Performance Optimization (2026-02-19)
**Completed:** Two targeted performance fixes for production environments.

View File

@@ -153,7 +153,7 @@ class ContextBuilder
return [
'id' => $post->ID,
'title' => get_the_title(),
'title' => wp_specialchars_decode( get_the_title() ),
'url' => get_permalink(),
'content' => apply_filters('the_content', get_the_content()),
'excerpt' => get_the_excerpt(),
@@ -184,7 +184,7 @@ class ContextBuilder
$wp_query->the_post();
$posts[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'title' => wp_specialchars_decode( get_the_title() ),
'url' => get_permalink(),
'excerpt' => get_the_excerpt(),
'date' => get_the_date(),
@@ -349,14 +349,14 @@ class ContextBuilder
if ($prev) {
$navigation['previous'] = [
'title' => get_the_title($prev),
'title' => wp_specialchars_decode( get_the_title($prev) ),
'url' => get_permalink($prev),
];
}
if ($next) {
$navigation['next'] = [
'title' => get_the_title($next),
'title' => wp_specialchars_decode( get_the_title($next) ),
'url' => get_permalink($next),
];
}
@@ -384,7 +384,7 @@ class ContextBuilder
$query->the_post();
$posts[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'title' => wp_specialchars_decode( get_the_title() ),
'url' => get_permalink(),
'date' => get_the_date(),
'date_iso' => get_the_date('c'),
@@ -438,7 +438,7 @@ class ContextBuilder
while ($query->have_posts()) {
$query->the_post();
$posts[] = [
'title' => get_the_title(),
'title' => wp_specialchars_decode( get_the_title() ),
'url' => get_permalink(),
'date' => get_the_date(),
];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ Description: A modern WordPress Block Theme built from scratch with Bootstrap 5.
Requires at least: 6.7
Tested up to: 6.7
Requires PHP: 8.3
Version: 1.0.9
Version: 1.0.10
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: wp-bootstrap