Skip to content

transliterate and transliterate_tokens disagree on unsupported input #16

Description

@nsssayom

Summary

transliterate and transliterate_tokens disagree on unsupported input.

transliterate is strict: a single character outside the sanitizer's allowed set makes it
return the entire input unchanged. transliterate_tokens carries no such guard — it renders the
supported tokens and passes the rest through.

input   transliterate   transliterate_tokens(tokenize(input))
-----   -------------   -------------------------------------
"ké"    "ké"            "কé"
"k~a"   "k~a"           "ক~আ"
"日k"    "日k"            "日ক"

Why it matters

CLAUDE.md documents the strictness contract for transliterate and the drop-then-render contract
for transliterate_lenient, but says nothing about transliterate_tokens. Yet the token path is
what incremental integrations use — src/wasm/mod.rs and any keyboard that tokenizes once and
re-renders the active word. Those callers silently get partial transliteration where the text API
would have refused.

Note this is not the tokenizer's fault: tokenize faithfully reassembles its input. The asymmetry
is that render_text_inner::<CHECK_INPUT = true> validates each character while render_tokens
has no equivalent.

How it was found

Adding a dirty corpus (inputs containing characters the sanitizer rejects) to the property tests.
The transliterate(x) == transliterate_tokens(tokenize(x)) property held over 45k clean inputs and
broke immediately on unsupported ones.

The same exercise showed the lenient == strict(clean) property had been vacuous: every input
in the clean corpus already satisfies clean(x) == x, so the assertion never had teeth. It now
asserts a minimum number of inputs actually had something to clean.

Options

  1. Make transliterate_tokens strict too — return the input unchanged if any token contains an
    unsupported character. Consistent, but changes the token API's behavior and may break the
    incremental keyboard path, which arguably wants best-effort rendering.
  2. Document the split explicitlytransliterate is strict, transliterate_tokens is
    best-effort, transliterate_lenient drops. Add transliterate_tokens_checked for callers that
    want strictness.
  3. Leave as is, with the behavior pinned by a test.

Currently (3): tests/property_tests.rs::strict_text_path_and_token_path_diverge_on_unsupported_input
pins it so any change is deliberate. Deciding between (1) and (2) is a public-API call.

Activity

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions