- Engineering
Mixed-script text before TTS
Retain original mixed-script text, derive explicit speech projections, and separate Unicode normalization from identifier identity.
- Author
Udit Goenka- Editorial date
- Reading time
- 25 min

TL;DR: Preserve original text and derive a separate speech projection under an explicit field policy. NFC normalization, compatibility folding, visual similarity and identity matching are different operations; safe TTS preparation must not erase their boundaries or interpret caller text as markup.
A cleanup function removes non-Latin characters before synthesis. Another strips combining marks and rewrites compatibility characters. The final sentence looks simpler in a debugger, but a name, identifier or joining sequence has changed. The code can no longer show what the caller actually supplied.
Begin with an owned original string and a declared field kind. Natural-language presentation can use an approved NFC projection, while literal identifiers remain under their owning comparison contract. Keep transformation evidence instead of replacing the original with whichever text happens to sound plausible.
Unicode normalization defines canonical and compatibility forms. Those text relations do not establish that two identifiers refer to the same entity. The Tamil-English alias method makes that distinction concrete for name tasks.
The runnable artifact below prepares synthetic strings and serializes them as literal XML text. It is not a transliterator, complete script detector, confusable classifier, browser renderer or speech benchmark for 60db Agent. Its useful output is an immutable preparation record that preserves the source and makes the chosen transformation inspectable.
Table of contents
- Separate script, language, and field meaning
- Choose an explicit normalization policy
- Preserve marks and controls deliberately
- Keep logical text separate from visual order
- Prepare an immutable speech projection
- Test idempotence without claiming reversibility
- Review synthesis and task outcomes separately
- Diagnose corruption at its owning boundary
- Frequently asked questions
- Admit a narrow preparation policy
Separate script, language, and field meaning
Script describes a writing system property. Language describes a different aspect of content, and field meaning belongs to the application. A mixed-script string can contain a name, a URL, a borrowed technical token or a literal code without giving a preparation function enough evidence to infer its intended use.
Do not derive a person's identity or language background from the scripts present in one string. A task can preserve script information for review while still requiring an independent identity or entity-selection process. The shared-phone identity boundary illustrates why convenient context does not establish who is acting.
Field kind should be supplied by a trusted owning adapter. A literal order identifier and a natural-language explanation require different preparation rules even when both contain letters and digits. Guessing the kind from the characters risks transforming a protected literal as if it were ordinary prose.
Use an explicit vocabulary such as natural_language and literal_identifier. Reject an unknown kind rather than silently choosing the more permissive operation. A policy is useful when another engineer can state exactly which fields it accepts and which values it leaves unchanged.
The source string should remain available before any transform. A debugger's display, a rendered page and a synthesized recording are different representations. A useful record preserves the original code points and the derived projection without claiming that either display proves how a listener understood it.
Brand and URL rendering supplies a related preservation rule. A speech-friendly alias can describe a canonical object while the stored object remains unchanged. Do not share a cleanup function across these fields merely because they eventually reach the same synthesis endpoint.
For digit-bearing identifiers, preserve their declared character contract. Postal-code capture explains why a numeric-looking string can still be an identifier. A mixed-script preparation layer should not infer missing digits, country, assignment or entity identity.
The first useful test is therefore a field-boundary test. Give the same synthetic string to the natural-language and literal policies, and inspect their explicit dispositions. If the literal policy changes the source without its owner's authority, the defect exists before any TTS call.

Write the field contract before choosing an implementation. Identify which component owns the original value, which presentation changes are allowed, and which output will be used for confirmation. A speech layer should not discover those permissions by observing whether a synthesizer accepts the text.
The same string can legitimately have several derived views. A search index, a display label and a speech projection may use different policies under their owners. Keep those views named and do not allow one to overwrite the shared original merely because it is convenient to pass around a single cleaned string.
A prepared value should also preserve its policy identity when cached. If a caller corrects a value or an application changes the field kind, reuse must be reconsidered under the new task revision. Text equality alone cannot establish that the previous approval or context still applies.
Unknown field meaning should remain explicit. Rejecting an undeclared kind gives an adapter a concrete integration error to fix. Guessing natural language because a string contains non-ASCII characters would make script appearance decide a control value that belongs to the application.
Choose an explicit normalization policy
NFC and NFKC answer different text-equivalence questions. NFC uses canonical normalization, while NFKC also applies compatibility distinctions defined by Unicode. Do not present compatibility folding as a universal safe cleanup for arbitrary names, codes or application identifiers.
A natural-language field can use a declared NFC speech projection while retaining the original. This example offers only preserve and NFC policies for natural-language text. It rejects NFKC as unsupported rather than silently broadening the comparison contract.
Literal identifiers use preserve only in the reference implementation. Their owning system may define an appropriate normalization rule, but that authority does not come from the speech layer. Keep the reference policy deliberately narrow until another contract explicitly permits a transformation.
Python's unicodedata documentation exposes normalization and the runtime Unicode database version. Record unidata_version with the preparation result. Reading a newer Unicode specification does not change the tables used by an installed interpreter.
Use stable mechanical fixtures for text behavior. A composed character and its canonical decomposed representation can demonstrate an NFC equality property under the actual runtime. That fixture is not a natural-language pronunciation reference or proof that two personal names identify the same person.
Keep raw strings in lookup and audit records where authorized. If a later normalization policy changes, another engineer needs the original input to recompute it. A stored normalized value alone cannot establish which source distinctions were removed.
A dictionary policy must state whether its keys use exact text or a normalization form. Versioned pronunciation snapshots preserve those choices as released content. A new normalization rule can create collisions even when no authored alias changes.
Do not silently trim whitespace, fold case or remove punctuation as part of a function named NFC. Each operation changes the admitted equivalence relation. Give it an explicit name and tests if it is required, and preserve the original through every derived view.
A check in the table means the operation supports the narrow property stated; a cross means another contract or evidence is required. It is a criteria table, not measured synthesis quality.
| Operation | Declared text transformation | Original retained automatically | Entity identity established |
|---|---|---|---|
| NFC call alone | ✓ Canonical normalization | ✗ Caller must retain it | ✗ Separate identity contract |
| NFKC call alone | ✓ Compatibility normalization | ✗ Caller must retain it | ✗ Separate identity contract |
| This preparation record | ✓ Preserve or NFC only | ✓ Owned source field | ✗ No identity inference |
| Visual similarity review | ✗ Not normalization | ✗ Needs source record | ✗ Appearance is insufficient |

A normalization policy should explain both what it changes and what it preserves. The example uses exact source retention and an optional NFC projection for one field kind. It performs no hidden whitespace folding, punctuation removal, numeric conversion or pronunciation substitution.
Test the raw source as well as the projection. Two canonically related inputs can share one speech value while retaining distinct original representations. That is expected behavior under the declared policy, and it demonstrates why an equality test over the projection cannot stand in for source equality.
Record policy changes as new admitted preparation conditions. If a later implementation adopts another normalization form or adds a transform, rerun collision and preservation fixtures before reusing existing review evidence. A new policy can produce a different projection even when the original strings remain unchanged.
A reference corpus needs its own equivalence rules. A transcript scorer can intentionally consider some canonical variants equivalent while a literal identifier task preserves exact source characters. Do not reuse the scorer's convenient normalization function as authority to rewrite the task's stored values.
Preserve marks and controls deliberately
Combining marks can be meaningful parts of writing. A blanket rule that deletes every combining mark changes the source representation and can remove distinctions the task needs. Keep such operations outside a default TTS preparation function.
Joining controls need the same care. A control character can affect shaping or interpretation under an appropriate writing-system context. Its category alone is not evidence that it should be deleted, nor does preserving it establish that the target voice will handle it as intended.
Unicode's security mechanisms describes confusable and mixed-script mechanisms. A small Python function using names or categories does not automatically implement those mechanisms. Avoid claiming complete script detection or security conformance from a few hand-written examples.
The reference artifact records the presence of a small explicit set of joining and bidi controls for review. It does not remove them. Its non-ASCII identifier flag is a coarse declared review policy, not a claim that every non-ASCII identifier is suspicious or that ASCII identifiers are safe.
Use a synthetic confusable pair to test preservation, not to build a universal classifier by accident. A Latin letter and a visually similar Cyrillic letter remain different original strings under the literal policy. Their appearance does not grant the renderer authority to merge them.
Technical abbreviation selection deals with another misleading shortcut: a familiar substring does not necessarily identify a token in its intended context. Protect literal fields before selecting a presentation rule.
Control review should have explicit outcomes. Preserve, ask for clarification, reject unsupported input and route to a broader Unicode implementation are different decisions. A warning flag does not itself modify text or resolve what a caller intended.
Do not use control stripping to make a test pass. If a serializer cannot represent a supported input without changing it, either narrow the declared character subset or select an appropriate implementation. Retaining a raw original outside the serializer still matters for diagnosis.
The example rejects unsupported XML characters, including isolated surrogates and carriage returns, before serialization. That is a stated output-format boundary rather than silent linguistic cleanup. Valid joining and bidi controls inside the accepted character subset remain in the preserved source and projection.

Review flags need an owner and a disposition. A record indicating a listed bidi control can be routed to an appropriate field review without changing the underlying text. If the application cannot handle that input safely within its current contract, it should return an explicit unsupported or clarification result instead of silently removing the character.
The coarse non-ASCII flag in the code is intentionally not a security verdict. It exists to show how a narrow preparation policy can expose a review need while preserving data. A production system should not describe every flagged identifier as malicious or every unflagged identifier as verified.
Avoid inventing a complete script detector from Unicode character names. Name prefixes and broad categories can help with specific diagnostics but do not provide all Script or Script_Extensions behavior. Keep a bounded teaching fixture bounded and use a separately verified implementation when the actual requirement is broader.
A normalization-only fixture also should not be presented as a natural spoken example. The fullwidth-character case demonstrates compatibility behavior mechanically. It says nothing about how a particular language community writes a name or how a selected voice will pronounce that character.
Keep logical text separate from visual order
The Unicode Bidirectional Algorithm describes display ordering for bidirectional text. Logical storage order and visual position are different properties. Do not reverse a stored string merely because its rendered layout contains right-to-left content.
A text fixture can preserve code points while a browser still displays them confusingly. Conversely, a screenshot can look plausible while the underlying string differs from the intended value. Keep logical-text tests and rendered-interface checks separate.
Arabic-English voice UI handling develops the display boundary. A speech-preparation function does not establish cursor behavior, screen-reader order, font shaping or keyboard interaction in that UI.
Use code-point inspection in controlled diagnostics when it is needed to explain a difference. Do not publish real names or identifiers merely because they are displayed as hexadecimal values. Encoding a personal value differently does not necessarily remove its identifying content.
Bidi controls require a review policy appropriate to the field. The reference flags a listed set while preserving the original. It does not claim that all controls are prohibited, that every valid display is harmless, or that flagging establishes the intended reading order.
JSON serialization also belongs to the transport boundary. RFC 8259 describes strings and their representation. A JSON escape sequence is a representation of data; an extra decode or cleanup step can change the string that reaches XML construction.
Keep the original and projected text attached to their field kind and policy. Without those values, a downstream adapter can mistakenly treat an identifier projection as natural prose or attempt another transform whose effects are no longer visible in the log.
A useful UI review asks which exact text was displayed and which exact projection was sent for synthesis. If those differ intentionally, show that relationship. Do not let a renderer's visual order become the stored canonical order by copying a screenshot-derived string back into the task.
A rendered review should include the actual field container and surrounding direction context, rather than only an isolated string in a text editor. Layout and markup can affect how the value is presented. Preserve the logical original beside the captured screen evidence so another reviewer can distinguish source content from visual positioning.
A historical Chrome SSML question asks why markup did not control the selected browser voice. It motivates checking the actual interface, but it does not establish current behavior across browsers or prove that a text projection will be consumed as SSML by every speech API.
Transport encoding deserves another independent check. Compare the supported source string with the decoded request body after each serialization layer. A JSON representation and an XML representation can both be valid while an adapter accidentally sends the wrong field or applies an extra decode.
This is why the preparation result contains explicit fields rather than a single ambiguous payload string. The application can select the intended transport representation deliberately and retain the other values as evidence, instead of guessing whether a string is raw text, XML or already escaped data.
Prepare an immutable speech projection
The artifact admits a supported Unicode string, field kind and normalization policy. It returns an immutable original, speech projection, transformation log, review flags, runtime database version and generated XML. It permits no arbitrary markup or external resource references.
SSML 1.1 defines a broader synthesis-control language. This example constructs only a trusted speak element with literal text. An adapter must verify the exact provider's accepted root configuration before using it in a real synthesis request.
ElementTree handles serialization of text inside that trusted structure. Caller strings that resemble tags stay data. This construction policy is separate from accepting and parsing arbitrary caller-supplied XML, which the example does not do.
Save the complete fence as mixed_script_preparation.py and run it with Python 3.12 or newer. The script prints actual synthetic preparation records. No audio, browser layout or natural-language pronunciation is measured by its assertions.
from dataclasses import dataclass, FrozenInstanceError
import unicodedata as ud
from xml.etree import ElementTree as ET
BIDI = frozenset(chr(n) for n in (*range(0x202A, 0x202F), *range(0x2066, 0x206A)))
JOINING = frozenset(("\u200c", "\u200d"))
def supported_text(value):
if type(value) is not str or not value:
raise ValueError("nonempty string required")
for c in value:
n = ord(c)
if not (n in (9, 10) or 0x20 <= n <= 0xD7FF or
0xE000 <= n <= 0xFFFD or 0x10000 <= n <= 0x10FFFF):
raise ValueError("outside supported XML character subset")
return value
@dataclass(frozen=True)
class Prepared:
original: str
speech: str
field_kind: str
policy: str
transformations: tuple
flags: tuple
unicode_version: str
xml: str
def prepare(value, field_kind, policy):
original = supported_text(value)
if field_kind not in {"natural_language", "literal_identifier"}:
raise ValueError("declared field kind required")
if policy not in {"preserve", "NFC"}:
raise ValueError("unsupported normalization policy")
if field_kind == "literal_identifier" and policy != "preserve":
raise ValueError("identifier owner must authorize any transform")
speech = ud.normalize("NFC", original) if policy == "NFC" else original
log = (("NFC", original, speech),) if speech != original else ()
flags = []
if any(c in BIDI for c in original): flags.append("listed_bidi_control")
if any(c in JOINING for c in original): flags.append("joining_control_preserved")
if field_kind == "literal_identifier" and any(ord(c) > 127 for c in original):
flags.append("non_ascii_identifier_review")
root = ET.Element("speak", {"xmlns": "http://www.w3.org/2001/10/synthesis", "version": "1.1"})
root.text = speech
return Prepared(original, speech, field_kind, policy, log, tuple(flags),
ud.unidata_version, ET.tostring(root, encoding="unicode"))
a = prepare("synthetic e\u0301", "natural_language", "NFC")
b = prepare("synthetic \u00e9", "natural_language", "NFC")
assert a.speech == b.speech and a.original != b.original
assert a.transformations == (("NFC", a.original, a.speech),)
assert prepare(a.speech, "natural_language", "NFC").speech == a.speech
assert a.original == "synthetic e\u0301"
compatibility = "\uff21" # mechanical fullwidth character fixture, not a spoken example
assert prepare(compatibility, "natural_language", "NFC").speech == compatibility
assert ud.normalize("NFKC", compatibility) != compatibility
latin, cyrillic = "a", "\u0430"
assert prepare(latin, "literal_identifier", "preserve").speech != prepare(
cyrillic, "literal_identifier", "preserve").speech
assert "non_ascii_identifier_review" in prepare(cyrillic, "literal_identifier", "preserve").flags
for raw in ("\u0628\u064e", "x\u200dy", "x\u2066y\u2069"):
p = prepare(raw, "natural_language", "NFC")
assert p.original == raw and p.speech == ud.normalize("NFC", raw)
for raw in ("A & B < C", "literal &", "</speak><audio src='https://example.com'/>"):
p = prepare(raw, "natural_language", "preserve")
tree = ET.fromstring(p.xml)
assert len(tree) == 0 and tree.text == raw
for args in (("x", "unknown", "preserve"), ("x", "literal_identifier", "NFC"),
("x", "natural_language", "NFKC"), ("", "natural_language", "NFC"),
("x\x00", "natural_language", "preserve"),
("x\ud800", "natural_language", "preserve")):
try: prepare(*args)
except ValueError: pass
else: raise AssertionError("unsupported input accepted")
try: a.original = "changed"
except FrozenInstanceError: pass
else: raise AssertionError("original rebinding accepted")
print("Synthetic preparation:", a)
print("Synthetic review flags:", prepare("x\u2066y\u2069", "literal_identifier", "preserve").flags)
print("Preparation fixtures passed; no synthesis, layout or identity claims.")
The original string remains a separate immutable field. A changed NFC projection records its original and derived values in a tuple log, while an unchanged projection needs no fabricated transformation event. Review flags describe the explicit limited policy and do not modify the content.
The listed control sets are intentionally incomplete as a Unicode security implementation. Python's database exposes useful character properties, but this artifact supplies no complete Script property engine or UTS 39 skeleton. A broader requirement needs a separately scoped implementation and evidence.
The generated XML is checked by parsing only the program's own output. A literal entity-looking string must remain the original text after that round trip. The script imports no networking library and contains no external audio, lexicon or document-loading element.
The immutable result prevents later property rebinding, but the broader application still needs correct ownership around it. If a caller's mutable request mapping changes while an asynchronous adapter waits, the adapter must not reread that mapping and call it the previously approved source.
Capture the source, field kind, policy and relevant task revision before waiting. Immediately before final local submission, compare current generation, cancellation and age under the surrounding controller's contract. The pure preparation function does not implement that lifecycle and should not be credited with preventing remote work after cancellation.
Keep historical preparation records separate from live approval. An earlier projection can explain what happened without becoming eligible for another task. A repair that changes spelling or material context requires the owning workflow to determine which prior confirmations are invalidated.
If a report aggregates preparation outcomes, keep missing policy metadata distinct from every real policy string. The program's explicit kind and policy rejection avoids a string sentinel that could be mistaken for admitted content. A later export schema should preserve those typed distinctions instead of silently coercing them.
Test idempotence without claiming reversibility
Idempotence means repeating the declared transform produces the same projection. It does not mean the projection can recover every original representation. The fixture applies NFC to the already projected string and compares speech values while retaining the first record's original independently.
A caller that passes the projected value as a new source has created a new input record. Do not claim that its original field somehow still represents the earlier decomposed source. Preserve the prior record if that source distinction is needed for reproduction.
Transformation logs should be honest about the operation performed. Do not append a normalization event when nothing changed merely to make every record look busy. Conversely, do not erase a real change because the resulting strings look identical in the chosen font.
A lossy alias or compatibility transform needs the same distinction. Repeating it can be stable even though several originals map to one output. Name alias evaluation still needs the intended entity and reviewed scope rather than a reverse-normalization guess.
Keep test expectations at the code-point level for deterministic text properties. A rendered visual comparison is another layer. A listening response is another again. Label the evidence type so a passing equality assertion cannot become a claim about voice quality or accessibility behavior.
Language-policy pairing uses the same principle for raw transcripts and reviewed fields. A text normalization rule can affect a transcript score without proving a changed task outcome. Keep the scorer's version and its equivalence policy in the report.
Use boundary fixtures that challenge preservation directly. Include canonical alternatives, a compatibility character, a known visually similar pair, valid marks and controls, and XML-looking text. Add invalid-character rejection cases so a serializer failure cannot be hidden by a blanket deletion step.
The example prints the preparation record and review flags, so readers receive the advertised inspectable artifact rather than only a final success line. Those values remain explicitly synthetic and should not be described as a completed speech evaluation.

Use one fixture to challenge each advertised property. The canonical pair challenges projection equality, the compatibility character challenges an overbroad cleanup assumption, the literal pair challenges identity conflation, and the markup-looking string challenges structure construction. Together they make the intended boundary inspectable without pretending to cover every writing system.
Add a positive case beside every rejection. Rejecting unsupported XML controls should not erase valid combining marks. Refusing identifier normalization should not block the preserve policy. A function that discards all difficult input can satisfy isolated negative tests while failing its useful task.
Keep assertions about emitted output too. If the artifact promises transformation evidence and flags, inspect those fields directly and print a reusable synthetic record. A success line proves that tests reached the end, while the record shows a reader the actual shape they can integrate and review.
Review synthesis and task outcomes separately
After deterministic preparation passes, connect an authorized adapter to a documented target voice. Preserve the exact projection, XML payload, synthesis settings and generated audio identity. A voice or locale change can alter the sound without changing the text record.
Confirmation design helps define a useful listening task. Ask whether the material value can be distinguished under the intended interaction, rather than treating naturalness preference as proof that an identifier was understood.
Speaking-rate changes can add another variable. If the goal is to test normalization, keep rate and other synthesis settings fixed or document their changes as part of a joint configuration comparison.
For names, caller-preferred pronunciation remains separate from spelling, entity identity and retention permission. A text transform cannot establish any of those business or personal boundaries by itself.
Real audio needs a declared format, sample clock, channels, codec, decoder and capture point. A streaming boundary defect can corrupt the heard value after text preparation succeeded. Do not change Unicode policy to compensate for missing audio evidence.
Use consented collection practices when gathering recordings or listener responses. Synthetic text fixtures do not establish permission to record real callers or publish their speech. Keep purpose-specific records and uncertainty visible.
A correctly spoken value does not prove a completed action. Outcome metrics requires the appropriate independent receipt. A task can fail in entity selection, confirmation or execution after every Unicode preparation assertion passes.
The article reports no acoustic or listener result. The next useful action is a bounded, qualified review with preserved artifacts, not a numerical quality claim derived from text equality. Keep cohort denominators explicit if the review later compares participants or conditions.

Diagnose corruption at its owning boundary
Begin with the earliest retained original and compare each representation in order. Inspect decoding, field assignment, normalization, alias selection, XML serialization, provider request and captured output separately. A string that prints correctly in one place can still change at the next boundary.
A historical Bing TTS developer question reports non-ASCII content disappearing in a request path. It is a real encoding symptom from an older interface, not evidence that a current service universally drops those characters.
If the original is already wrong, inspect decoding and source capture. If the original is correct but the projection differs unexpectedly, inspect the declared policy and transformation log. Preserve a small synthetic reproducer containing the exact affected code points.
If XML-looking text becomes structure, inspect construction and transport escaping. Do not treat a safe dictionary source as permission for arbitrary SSML. A reviewed alias can still contain punctuation that must remain text inside a trusted element.
If the projection is correct but the display is confusing, inspect the UI's bidi and shaping behavior. The text function cannot establish the screen's visual order. Keep that rendered evidence attached to the actual interface rather than reverse-engineering canonical text from a screenshot.
If the request and capture are correct but the sound is unclear, preserve voice and dictionary identities for review. Output consistency testing offers a separate rubric for properties that normalization does not control.
Bind any cached projection to the owning policy and scope. The cache-key method prevents an old rendering rule from silently reappearing in another language or task context. Retained historical evidence should not become current permission after cancellation or repair.
Keep public reports synthetic. A name represented as code points remains a name, and a unique identifier remains potentially sensitive after an encoding change. Publish the minimal logical defect and retain real originals only under their authorized access and retention boundary.
Before releasing a policy, have another engineer reconstruct one changed projection from the retained original, field kind and runtime version. They should be able to identify the exact operation without relying on a screenshot or the current state of a mutable helper. Repeat the exercise for a literal identifier that must remain unchanged.
Then review one rejected input. The rejection should identify an unsupported character or policy boundary rather than conceal a deletion. A caller-facing adapter can request clarification or use an approved alternative while the controlled diagnostic record preserves what actually arrived.
Keep the release note limited to the observed result. Passing these fixtures establishes the declared software properties on synthetic values. It supplies no dialect ranking, rendering certification or evidence that real listeners preferred the resulting speech. That separation lets another team reuse the preparation contract without inheriting an unsupported claim about its own voices or users.
Frequently asked questions
What does mixed-script text mean before TTS?
It means the input contains characters associated with more than one writing-system context. That observation does not determine language, field meaning or identity. Preserve the original and let a trusted application field policy decide whether a speech projection may be derived.
How do script and language differ?
Script concerns writing, while language concerns content and use. A string can include borrowed tokens, names or identifiers without revealing a person's language identity. Do not infer a caller's nationality, first language or intended entity from the script mixture in one field.
What does NFC normalize?
NFC applies Unicode canonical normalization under the implementation's database. It can make canonically equivalent representations share a form. It does not transliterate, select a pronunciation or identify a person, and the original remains necessary when the exact submitted representation matters.
How is NFKC different?
NFKC also applies compatibility normalization defined by Unicode. That can remove distinctions an owning identifier contract needs. This artifact rejects NFKC rather than offering it as a generic cleanup option; a broader policy needs explicit authority, collision review and its own fixtures.
Can normalization establish identifier identity?
No. It establishes a text relation under a chosen policy. Entity identity belongs to another application contract with its own scope and evidence. Preserve literal identifiers unless their owner explicitly permits a transform, and do not merge visually or canonically related values into people.
Should Arabic diacritics be removed?
Do not remove them by default. A blanket combining-mark deletion changes content without establishing that the task permits it. Use qualified linguistic review and a declared field policy if a particular transformation is needed; the reference retains valid marks and claims no pronunciation benefit.
Why preserve joining controls carefully?
They can affect writing-system behavior, so their presence is not enough to justify deletion. The reference preserves its supported text and records listed controls for review. That flag is not a complete shaping analysis or proof of how a particular voice will speak the string.
Are visually similar characters equal?
Visual similarity does not establish equality or identity. The synthetic Latin/Cyrillic fixture preserves distinct code points under the literal policy. A complete confusable analysis requires another scoped implementation; this program's coarse review flag is not a universal classifier.
How should mixed digit scripts be treated?
Keep their original representation and apply the owning field contract. An identifier and a quantity can require different rules. Do not silently convert numeric-looking text, infer country or repair lost characters merely because the result seems easier for a synthesizer to pronounce.
What is the original text used for?
It supports reproduction, policy changes and review of source distinctions. Retain it under the authorized access boundary, alongside derived projections and transformation evidence. A normalized value alone cannot reveal which original representation was supplied before a potentially lossy presentation operation.
What belongs in the speech projection?
Only the result of the declared preparation policy for that field. Keep policy, field kind and runtime version attached to it. A projection should not become the stored canonical identifier or imply that its content was approved for a business action.
Does normalization change display order?
Normalization and bidirectional display are different operations. A logical string can render in another visual order under the Unicode bidi algorithm. Verify the actual UI separately and avoid reversing stored text or copying a visual representation back into the canonical field.
How do bidi controls affect review?
They can make a displayed value require closer inspection under the field policy. Preserve exact code points and use appropriate rendered checks. The artifact flags only a listed set and does not decide intended reading order, prohibit all controls or certify display security.
Can Python identify every script directly?
The small unicodedata interface and this example do not implement a complete Script property or UTS 39 engine. Do not claim full coverage from name-prefix checks or a few examples. A broader requirement needs an explicitly scoped implementation with corresponding conformance evidence.
Why record the Unicode database version?
It identifies the actual tables used by the runtime. A current specification link does not establish the interpreter's data version. Preserve the policy and implementation version too, so another engineer can recompute the projection under the same declared conditions.
What should an idempotence test prove?
It should prove that reapplying the specified transform to its projection yields the same projection. It does not prove that every original can be recovered. Keep original retention as a separate tested property and avoid relabeling a projected value as the earlier source.
Can a lossy transform be reversed?
Not in general. Several originals can map to the same derived form, even when repeated application is stable. Preserve the original and transformation record rather than promising reverse conversion from speech text, normalized text or the sound produced by a synthesizer.
How do I keep untrusted text out of SSML?
Keep it as text inside an application-selected structure and serialize it once. Reject unsupported XML characters and allow no caller-selected elements or external references. Text escaping and markup permission are separate controls, both of which need explicit negative fixtures.
What still requires a rendered or listening check?
UI ordering, shaping, cursor behavior and actual speech intelligibility need their corresponding observed evidence. The text program validates source preservation and construction only. Keep captured output identities and qualified review responses separate from deterministic assertions, and report unmeasured properties honestly.
When does the example need a broader Unicode implementation?
When the actual requirement includes complete script properties, confusable detection, broader normalization policies or display behavior outside its contract. Define that requirement first. Do not expand a tiny preparation helper by adding undocumented deletion rules that make difficult inputs disappear.
Admit a narrow preparation policy
Run the complete synthetic preservation and escaping fixtures before connecting a voice adapter. Declare the field owner, retain the original and use only the transformations that owner permits.
Then capture the actual rendering or speech evidence needed for the task. A small preparation record is useful because it preserves what happened, including the boundaries where text equality cannot establish identity, appearance or comprehension.
