Skip to content

Commit 164287f

Browse files
authored
docs-i18n: avoid ambiguous body-only wrapper unwrap (openclaw#63808)
* docs-i18n: avoid ambiguous body-only wrapper unwrap * docs: clarify targeted testing tip * changelog: include docs-i18n follow-up thanks
1 parent 2954c72 commit 164287f

4 files changed

Lines changed: 29 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Docs: https://docs.openclaw.ai
77
### Changes
88

99
- macOS/Talk: add an experimental local MLX speech provider for Talk Mode, with explicit provider selection, local utterance playback, interruption handling, and system-voice fallback. (#63539) Thanks @ImLukeF.
10-
- Docs i18n: chunk raw doc translation, reject truncated tagged outputs, and recover from terminated Pi translation sessions without changing the default `openai/gpt-5.4` path. (#62969) Thanks @hxy91819.
10+
- Docs i18n: chunk raw doc translation, reject truncated tagged outputs, avoid ambiguous body-only wrapper unwrapping, and recover from terminated Pi translation sessions without changing the default `openai/gpt-5.4` path. (#62969, #63808) Thanks @hxy91819.
1111

1212
### Fixes
1313

docs/help/testing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Most days:
2626
- Faster local full-suite run on a roomy machine: `pnpm test:max`
2727
- Direct Vitest watch loop: `pnpm test:watch`
2828
- Direct file targeting now routes extension/channel paths too: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`
29+
- Prefer targeted runs first when you are iterating on a single failure.
2930
- Docker-backed QA site: `pnpm qa:lab:up`
3031

3132
When you touch tests or want extra confidence:

scripts/docs-i18n/doc_chunked_raw.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,21 @@ func sanitizeDocChunkProtocolWrappers(source, translated string) string {
231231
return body
232232
}
233233
}
234-
body, ok := stripBodyOnlyWrapper(trimmedTranslated)
234+
body, ok := stripBodyOnlyWrapper(source, trimmedTranslated)
235235
if !ok || strings.TrimSpace(body) == "" {
236236
return translated
237237
}
238238
return body
239239
}
240240

241-
func stripBodyOnlyWrapper(text string) (string, bool) {
241+
func stripBodyOnlyWrapper(source, text string) (string, bool) {
242+
sourceLower := strings.ToLower(source)
243+
// When the source itself documents <body> tokens, a bare body-only payload is
244+
// ambiguous: the trailing </body> can be literal translated content instead of
245+
// a real wrapper close. Keep it for validation/retry instead of truncating.
246+
if strings.Contains(sourceLower, strings.ToLower(bodyTagStart)) || strings.Contains(sourceLower, strings.ToLower(bodyTagEnd)) {
247+
return "", false
248+
}
242249
lower := strings.ToLower(text)
243250
bodyStartLower := strings.ToLower(bodyTagStart)
244251
bodyEndLower := strings.ToLower(bodyTagEnd)

scripts/docs-i18n/doc_mode_test.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,15 @@ func TestTranslateDocBodyChunkedStripsUppercaseBodyWrapper(t *testing.T) {
512512
}
513513
}
514514

515-
func TestSanitizeDocChunkProtocolWrappersStripsTopLevelWrapperEvenWhenSourceMentionsBodyTag(t *testing.T) {
515+
func TestSanitizeDocChunkProtocolWrappersKeepsBodyOnlyWrapperWhenSourceMentionsBodyTag(t *testing.T) {
516516
t.Parallel()
517517

518518
source := "Use `<body>` and `</body>` in examples, but keep the paragraph text plain.\n"
519519
translated := "<body>\nTranslated paragraph.\n</body>\n"
520520

521521
got := sanitizeDocChunkProtocolWrappers(source, translated)
522-
if strings.Contains(got, "<body>") || strings.Contains(got, "</body>") {
523-
t.Fatalf("expected top-level wrapper to be stripped, got %q", got)
524-
}
525-
if strings.TrimSpace(got) != "Translated paragraph." {
526-
t.Fatalf("unexpected sanitized body %q", got)
522+
if got != translated {
523+
t.Fatalf("expected ambiguous body-only wrapper to remain unchanged for retry\nwant:\n%s\ngot:\n%s", translated, got)
527524
}
528525
}
529526

@@ -539,6 +536,21 @@ func TestSanitizeDocChunkProtocolWrappersKeepsLegitimateTopLevelBodyBlock(t *tes
539536
}
540537
}
541538

539+
func TestSanitizeDocChunkProtocolWrappersStripsBodyOnlyWrapperWhenSourceHasNoBodyTokens(t *testing.T) {
540+
t.Parallel()
541+
542+
source := "Regular paragraph.\n"
543+
translated := "<body>\nTranslated paragraph.\n</body>\n"
544+
545+
got := sanitizeDocChunkProtocolWrappers(source, translated)
546+
if strings.Contains(got, "<body>") || strings.Contains(got, "</body>") {
547+
t.Fatalf("expected body-only wrapper to be stripped, got %q", got)
548+
}
549+
if strings.TrimSpace(got) != "Translated paragraph." {
550+
t.Fatalf("unexpected sanitized body %q", got)
551+
}
552+
}
553+
542554
func TestSanitizeDocChunkProtocolWrappersKeepsAmbiguousTaggedWrapperForRetry(t *testing.T) {
543555
t.Parallel()
544556

0 commit comments

Comments
 (0)