Skip to main content

Reject canonical Unicode name collisions

2026-11-13 14:12:18.509109+00:00
Reject canonical Unicode name collisions

AI assisted the preparation of this article, and its Python example was tested locally with synthetic fixtures.

Two names can look the same while using different Unicode code point sequences. If an import contract requires unique canonical spellings, compare a chosen normalization key and stop for manual review when keys collide. Keep the originals; a matching key does not establish that two people are the same.

Flow: Original names, then NFC keys, then Collision groups, then Reject or accept.

Reject the whole fixture import

This small function groups names by NFC comparison key. It returns every original pair that collides and marks the whole import unaccepted. The input list and its strings are not modified. Even two identical original strings are a collision under this explicit fixture policy.

import unicodedata

def check_names(names):
    originals = list(names)
    groups = {}
    for name in originals:
        if not isinstance(name, str) or not name:
            raise ValueError("each name must be a nonempty string")
        key = unicodedata.normalize("NFC", name)
        groups.setdefault(key, []).append(name)
    collisions = []
    for group in groups.values():
        if len(group) > 1:
            for index, first in enumerate(group):
                for second in group[index + 1:]:
                    collisions.append((first, second))
    return {"accepted": not collisions, "collisions": collisions}

For example, precomposed café and cafe followed by a combining acute accent share an NFC key. The collision report retains both original strings, and the caller should send the batch for review instead of choosing a winner. An empty list is accepted by this fixture; an empty name is invalid.

NFC uses canonical equivalence. A circled digit and an ordinary digit are compatibility-related but remain distinct under NFC in the tested Unicode database. NFKC has a different purpose and can collapse distinctions that matter to an application. Do not switch forms merely to suppress more duplicate warnings.

The fixture ran on CPython 3.13.5 with unicodedata.unidata_version 15.1.0. Python 3.13 unicodedata documents the forms; Unicode Standard Annex 15, revision 58 describes canonical and compatibility equivalence. Record the database version when reproducing a result.

Keep identity and privacy decisions separate

NFC is not case folding or a defense against visually confusable characters. It does not authorize access, establish account identity or justify merging people, organizations or tenant records. Existing databases may have different rules, so a migration needs its own collision review and rollback plan.

The fixture collects all names and can produce many collision pairs for a large repeated group. Bound input size and protect the review record under the application's privacy policy. No remote directory, production account or identity merge is exercised by these synthetic tests.