Unicode transforms

Type or paste text below. Conversion runs locally in your browser.

The samples exercise rules that ASCII-only conversion misses. ΣΟΦΟΣ ends with the final sigma ς; straße becomes STRASSE; and İ becomes (an i followed by a combining dot). Garay casing requires Unicode 16 or newer.

Why the Unicode version is explicit #

Browsers, operating systems, programming languages, databases, and server distributions ship different revisions of Unicode data. Many use ICU and CLDR, but they do not all update together. The same application can therefore see different case mappings, normalization, collation, or locale formatting depending on where it runs. Newer scripts are a visible example: an older runtime may leave their letters unchanged because it does not yet know they have case.

These components carry their own Unicode 17 data. The revision is part of each filename—unicode-17-lowercase.wasm and unicode-17-uppercase.wasm—so upgrading to Unicode 18 will be an explicit component change rather than an incidental platform update.

Both implement Unicode Default Case Conversion for the root locale. Locale-specific casing for Turkish, Azerbaijani, and Lithuanian belongs in separate, explicitly named components.

Use the components #

echo "ΘΕΣΣΑΛΟΝΊΚΗ ΣΟΦΟΣ" | qip run modules/utf8/unicode-17-lowercase.wasm
# θεσσαλονίκη σοφος

echo "straße" | qip run modules/utf8/unicode-17-uppercase.wasm
# STRASSE

In a browser, the helper used by this page wraps the component's small memory-and-render contract:

import { contentComponent, contentContract } from "/qip-runner.js";

const text = contentContract({ encoding: "utf-8" });
const module = await WebAssembly.compileStreaming(
  fetch("/components/utf8/unicode-17-lowercase.wasm"),
);
const lowercase = contentComponent(text, module, text);

lowercase("ΘΕΣΣΑΛΟΝΊΚΗ ΣΟΦΟΣ"); // "θεσσαλονίκη σοφος"

The same component runs in Node or any other WebAssembly host. You can also import it directly as a WebAssembly ES module.

Check another implementation #

The uppercase component has an executable compliance component at compliance/unicode-17-uppercase.comply.wasm. Its 100 deterministic cases combine examples, seeded fuzzing, and properties such as idempotence. A failure can be reproduced from the component hash, seed, and case ordinal.

The repository includes Node and Go examples. Node's toUpperCase, backed here by ICU 78 and Unicode 17, passes all 100 cases. Go's standard library fails 38 because it uses Unicode 15 tables and simple per-rune mappings rather than full mappings. See examples/comply-uppercase-node/ and examples/comply-uppercase-go/.

Normalization and locale-aware date, number, and currency components can follow the same model: the Unicode or CLDR revision belongs in the artifact rather than being inherited silently from the environment.