fix(theme): read translation source lang from source post, not site default#171
Conversation
…efault
cdcf_process_translation() assembles the OpenAI prompt using
pll_default_language('slug') as the source language — which is always
the site default (EN). For a Polylang-linked source post in a different
language, this produces a "translate from English to X" prompt body
even though the source is actually German/Italian/etc.
It happens to work today because every existing entry point seeds the
source in EN before fanning out, but it would silently mistranslate any
non-EN source. Issue #2 (team-member bio self-edit) needs source-lang
parity for the author's chosen drafting language.
Read pll_get_post_language($source_id) first, falling back to
pll_default_language('slug') for posts not yet linked into a Polylang
group — preserves legacy behaviour without a flag.
The linking layer in cdcf_translate_link_under_lock already reads the
source's actual language correctly (translate.php:45); this brings the
prompt layer in line with it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe translation system now determines OpenAI source language from each post's assigned Polylang language, falling back to the site default when the post has no language assignment. Implementation changes the language detection logic, and test updates add setup stubs and two validation cases confirming both the post-specific and fallback paths. ChangesPost-level source language detection
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@coderabbitai review |
✅ Action performedReview finished.
|
…ints
GET /cdcf/v1/my-team-member
PATCH /cdcf/v1/my-team-member/{lang}
The authenticated user's bio self-edit surface. Discovery resolves the
caller's author_team_member ACF link, pulls the full Polylang group,
and reports which language versions are editable. Edit accepts a
partial PATCH against any language in that group, persists to the
target post, and fans out re-translation to the OTHER 5 langs from
the just-saved source — reusing cdcf_enqueue_post_translation() so
all the existing redis-queue + Polylang-link + advisory-lock infra
works as-is.
Authorization model
-------------------
- Bearer-validated session via the Phase 1b authenticator (or cookie
/ Application Password for non-frontend callers).
- author_team_member ACF user-field is the canonical ownership signal.
It's admin-managed (set via /cdcf/v1/author-team-member, PR #160),
so end users can never widen their own access from inside this
endpoint.
- Permission gate: logged-in AND has any link → admits.
- Per-language gate inside the handler: the link must point at SOME
post in the {lang} target's Polylang group. Covers the case where
the admin set the link at the EN post but the user edits the DE
sibling.
Translation strategy (issue #2 locked decision #3)
--------------------------------------------------
- Edit in any language — no privileged "primary language". Whichever
language gets saved becomes the new source-of-truth for that cycle.
- Re-translation fan-out covers the other 5 langs in the group, NOT
including the just-saved one. Each enqueue is independent; a single
enqueue failure is recorded in `errors` without aborting siblings
(the just-saved source-language post is already persisted at that
point — we don't roll it back for a downstream queue blip).
- The OpenAI prompt reads source-lang from the source post (PR #171
fix), so editing in any locale produces correct translations.
URL host allowlist
------------------
- member_linkedin_url: linkedin.com (or any subdomain) or empty.
- member_github_url: github.com (or any subdomain) or empty.
- esc_url_raw sanitization runs first via the args block; the
hostname constraint can't be expressed there so it's enforced in
the handler body, returning 400 on violation.
- Suffix match is dot-boundary anchored — linkedin.com.evil.org
does NOT pass.
Partial PATCH semantics
-----------------------
- A field is updated only when the request actually carried a string
value for it (caught via `is_string($request->get_param(…))`). A
PATCH that only changes member_title does NOT clobber post_content.
- Empty string IS a valid value and clears the field (used by URL
fields and member_title alike).
Tests (27 new in MyTeamMemberHandlerTest)
-----------------------------------------
- resolve_link: scalar id / array of ids / WP_Post / false / anonymous
- collect_group: normal / fallback to pll_get_post_language / invalid
- url_host_ok: empty / exact / subdomain / unrelated / suffix-
lookalike / malformed
- permission: 401 anonymous / 403 no link / true linked
- GET happy: full group with two languages
- GET filter: polluted group with non-team_member post stripped
- PATCH happy: DE edit fans out to en/it/es/fr/pt, all three ACF
fields written to the DE post, wp_update_post
carries the right ID + content
- PATCH partial: member_title-only PATCH leaves wp_update_post
uncalled
- PATCH 404: lang not in Polylang group
- PATCH 400: LinkedIn URL pointing at evil.example.org
- PATCH 400: GitHub URL pointing at gitlab.com
- PATCH 500: wp_update_post WP_Error bubbles up
- PATCH partial-fail: one enqueue WP_Error recorded, siblings still
queued, no exception
- PATCH ownership: link points outside the resolved Polylang group →
403
Test bootstrap also gains a minimal WP_Post shim (4 fields) and the
new handler include is required-up-front like the others.
Theme suite: 420 → 447 tests (+27), 970 → 1033 assertions (+63).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
cdcf_process_translation()was assembling the OpenAI prompt with the source language hardcoded to the site default (EN) viapll_default_language('slug'). For a Polylang-linked source post in any other language, this silently produced a "translate from English to X" prompt body even though the source post was actually German/Italian/Portuguese/etc.It happens to work today because every existing entry point seeds the source in EN before fanning out (the
/cdcf/v1/team-memberand/cdcf/v1/community-channeletc. handlers all create EN first and then call translate per target lang). The bug was latent.The linking layer in
cdcf_translate_link_under_lock(translate.php:45) already reads the source's actual language correctly viapll_get_post_language($source_id). This brings the prompt-assembly layer in line.Why now
Precondition for issue #2 — letting team members edit their bios via passkey login. The plan is to let authors draft in their native language (a German author drafts in German, an Italian author in Italian) and fan out re-translation to the other 5 langs from whatever they save. That UX requires the source-lang detection to actually read the source post.
Ships standalone, regardless of when the rest of issue #2 lands.
Change
The
?:fallback preserves legacy behaviour for posts not yet linked into a Polylang group — no flag, no callsite churn.Tests
Two new PHPUnit tests in
ProcessTranslationTest:test_source_language_read_from_source_post_not_site_default— German source post → captured$source_name === 'German'in the OpenAI calltest_source_language_falls_back_to_site_default_when_unset— emptypll_get_post_language→ falls back to'English'Full theme suite passes locally:
Test plan
messagesfield shows "from German to ..." rather than "from English to ..." (error_logdebug or worker log inspection)Deploy
Theme change — needs
gh workflow run deploy.yml -f environment=productionto ship. Astaging-target run skips theme/plugin steps and the bug would persist on live.Summary by CodeRabbit
Bug Fixes