Skip to content

i18n audit: contentDescription set inside a semantics {} lambda is still invisible (a11y copy stays English) #571

Closed

Description

@ryanbr

Follow-up to #540 / #558. #558 fixed the conditional-literal blind spot (Text(if x "a" else "b")), but a different axis remains invisible: a UI-text kwarg set inside a lambda.

The gap

ANDROID_KWARG_PATTERN includes contentDescription, and the audit catches it as a normal call argument:

Foo(contentDescription = if (hr) "Recovery ready" else "Recovery, no data yet")   // CAUGHT

But the standard Compose accessibility idiom sets it inside a semantics {} lambda, and that brace is opaque (correctly — semantics isn't a transparent keyword, so the anti-489-explosion split skips it):

Modifier.semantics { contentDescription = if (hr) "Recovery ready" else "Recovery, no data yet" }   // MISSED

Verified against scan_android() on merged main: the plain-arg form is flagged, the semantics {} form is not, and the strings pass is_probably_ui_text — so it's a genuine scan gap, not the filter.

Why it matters

This is exactly the real bug behind the earlier review finding: CoupledScreen.kt builds three hardcoded-English a11y strings ("Recovery calibrating, $n of $seed nights", "Recovery, no data yet", …) and assigns them via .semantics { contentDescription = a11y }. A German TalkBack user hears English there. The Apple twin localizes it (String(localized:)); Android doesn't. After #558 the audit still reports clean for this class, so the baseline can't track it and it silently never gets localized.

Suggested fix

A targeted rule rather than reopening all braces: scan contentDescription = (and any other genuine UI-text kwarg) even inside a trailing/semantics lambda. That catches the a11y idiom without sweeping unrelated lambda content back in (the case #558 deliberately kept opaque). A regression test should assert Modifier.semantics { contentDescription = if (x) "A" else "B" } is flagged while AlertDialog(text = { Column { … } }) still isn't.

Not urgent — no correctness impact, only untranslated a11y copy — but worth tracking so it doesn't hide behind a green audit.

Activity

  1. ryanbr commented on Jul 18, 2026

    @ryanbr
    OwnerAuthor

    Scoping note after implementing the fix (#581): this issue splits into two cases, and #581 only handles the first.

    So keeping this open for the val-laundered variant after #581 lands. The CoupledScreen a11y strings remain untracked/unlocalized until that's done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions