Document Workflow
Normalize Unicode before comparing text across systems
Choose NFC, NFD, NFKC, or NFKD deliberately, inspect code-point and byte changes, separate encoding loss from normalization, and validate the destination.
Written and tested by SimpleWebUtilsPublished: Reviewed:
How this workflow was checked
The review loaded the unmodified “Normalize a multilingual lookup key without compatibility folding” sample into Unicode Normalizer. We exercised the path from “Preserve the original representation” to “Investigate every warning”, checked the displayed or downloaded result against the example, and separately examined “Using NFKC as universal cleanup” and “Assuming hidden characters are removed”.
NFC produced “Café / 각” as canonically equivalent text, reported no compatibility fold, and left unrelated full-width or mathematical characters distinct.
Problem
Two strings can render alike while using different code-point sequences, so binary equality, filenames, cache keys, imports, routes, or deduplication may fail. Blind normalization can create a second problem: NFKC and NFKD may erase meaningful width, mathematical, presentation, superscript, or circled-character distinctions. Normalization also cannot recover bytes decoded with the wrong character set, remove every zero-width character, prevent script-confusable identifiers, or guarantee that separately normalized fragments remain normalized after concatenation. The safe workflow identifies the actual layer of failure, chooses a form from the destination contract, records exact changes, and tests the completed result.
Sources and standards
These authoritative references define the formats or security boundaries used in this workflow. Tool-specific verification is documented separately above.
- Unicode Standard Annex #15: Unicode Normalization Forms
Unicode Consortium
When to use this
- Visually identical values fail equality, lookup, route, cache, or duplicate checks.
- Filenames move between macOS, Windows, Linux, an archive, or version control.
- Multilingual labels with accents, combining marks, or Hangul jamo enter a system with a documented normalization rule.
- A restricted search key may intentionally fold fullwidth forms, ligatures, circled values, or presentation variants.
- A migration needs evidence of code-point and UTF-8 changes before stored values are rewritten.
Steps
- Step 1
Preserve the original representation
Keep the source text and, for imported files, the original bytes, filename, declared encoding, and decode path. A U+FFFD replacement character or mojibake pattern indicates earlier decoding loss that normalization cannot reverse. Diagnose the character set before rewriting text.
- Step 2
Identify the destination contract
Check whether the database, filesystem, protocol, programming language, signature, identifier policy, or search index requires a specific form. Do not change a signed value, checksum input, security token, or externally specified identifier just because NFC is common on the Web.
- Step 3
Choose canonical or compatibility behavior
Use NFC when canonically equivalent sequences should be composed, or NFD when they should remain decomposed. Choose NFKC or NFKD only when the application intentionally treats width variants, ligatures, circled characters, superscripts, presentation forms, and related compatibility values as equivalent.
- Step 4
Run a bounded sample first
Test representative positive and negative examples in Unicode Normalizer. Include accents, Hangul, emoji, combining marks, expected width variants, line endings, and any identifiers from the real workflow. Review input and output code-point counts, UTF-8 bytes, and the bounded previews.
- Step 5
Investigate every warning
A compatibility-folding warning means the result is not merely another canonically equivalent encoding. Remaining format characters need separate inspection, and U+FFFD means the original bytes should be decoded again if possible. None of these warnings should be hidden by a successful run.
- Step 6
Compare exact output and normalize completed values
Use Diff Checker and code-point evidence to account for every changed sequence. If the value is assembled from fragments, concatenate first and normalize the completed value because normalized strings are not guaranteed to stay normalized across a new boundary.
- Step 7
Validate storage, lookup, and round trip
Test the reviewed result in a disposable copy of the real database, import pipeline, filename operation, route matcher, or search index. Confirm expected equality and distinctness, re-read stored bytes, and retain the chosen form and test evidence without retaining sensitive text unnecessarily.
Example
Normalize a multilingual lookup key without compatibility folding
Input
Source code points: C a f e U+0301 / U+1100 U+1161 U+11A8
Target contract: NFC
Hidden-character count: 0
Keep the original import bytes and compare the completed key.Output
Result: Café / 각
Canonical equivalent: yes
Compatibility folded: no
Then test equality against the destination index and verify that unrelated fullwidth or mathematical forms remain distinct.Common mistakes
Using NFKC as universal cleanup
Compatibility normalization can remove distinctions that matter in mathematics, identifiers, typography, legacy round trips, and user-visible content. Apply it only to a restricted field with an explicit equivalence policy.
Confusing decoding with normalization
Wrong character-set decoding changes or loses information before normalization starts. Preserve and re-decode the bytes instead of repeatedly normalizing mojibake or U+FFFD.
Assuming hidden characters are removed
Unicode normalization normally preserves zero-width and other format characters. Inspect their count and use a dedicated removal workflow only after deciding which characters are invalid.
Normalizing each fragment only once
A new concatenation boundary can form a composeable or reorderable sequence. Normalize the complete value that will actually be compared or stored.
Treating canonical equivalence as application equality
An application may use locale collation, case folding, security profiles, or exact binary identity in addition to normalization. Validate the target behavior rather than inferring it from one normalization result.
FAQ
Why can identical-looking strings have different bytes?
Unicode can encode some abstract characters as a precomposed code point or as a base character plus combining marks. Correct rendering may look the same even though binary comparison differs.
Which form is safest for ordinary Web text?
NFC is widely recommended for interoperable Web content, but the destination contract still controls. Preserve source evidence and do not apply a default to signatures, identifiers, or protocol fields without checking their rules.
When is NFKC appropriate?
It can be appropriate for a restricted search or identifier profile that explicitly wants compatibility variants to compare alike. It is not appropriate as an automatic rewrite of arbitrary prose or mathematically meaningful content.
Will normalization remove spoofing risks?
No. Script-confusable characters can remain after normalization. Security-sensitive identifiers need an appropriate Unicode security profile, script policy, and application-specific review.
Why normalize after concatenation?
Unicode normalization forms are not closed under string concatenation. Characters at the new boundary may compose or combining marks may reorder, so the final value needs normalization.
Can this workflow prove a migration is safe?
It provides local transformation and exact evidence, but safety also depends on the destination's comparison, indexing, uniqueness, signing, and round-trip behavior. Test those systems with a reversible migration plan.