Short answer

Compare folded text: lowercase, without accents, with full-width letters made ordinary, phone numbers reduced to their digits, and names in other alphabets transliterated to Latin. Local CRM builds that folded index whenever a contact is saved, so “jose”, “aleksandr” and “555 0123” all find the right person. Transliteration follows one standard reading, so record how people spell their own names in the nickname or phonetic fields.

People rarely search for a contact the way it was saved. They type what they remember, on whatever keyboard is in front of them: no accents, no capital letters, half a phone number, a name they've heard many times but never seen in its own alphabet. A search that compares those keystrokes letter for letter with what's stored misses exactly the people it's for.

What people actually type#

Over the life of an address book, the same person gets searched for in many forms. These are the gaps between what's stored and what's typed that a contact search has to close:

  • Accents. José is saved with an accent, but most people type jose. Zoë Müller becomes zoe muller.
  • Case and width. Phones capitalize the first letter, some people type in capitals, and Chinese and Japanese keyboards can produce full-width Latin letters such as jose, which look alike but are different characters.
  • Phone formatting. A number saved as +1 (555) 010-0123 gets searched as 5550100123, 010 0123 or just 0123.
  • Other alphabets. Many of us work with people whose names are written in Chinese, Cyrillic or Greek, and type them in Latin letters: aleksandr for Александр, nikos for Νίκος.
  • Scattered details. You remember a first name and a city, or a company and a word from a note, rarely one complete field.

Fold once, when the contact is saved#

The fix is to compare folded text: a normalized form of both the stored values and the query, with the differences that don't matter to people taken out. Local CRM folds three things away (case, diacritics and character width), with the folding rules in Apple's Foundation framework.

Folding a query is cheap; folding every field of every contact on every keystroke is not. So Local CRM builds a search index for each contact whenever it's saved: one block of folded text holding every field, from names and companies to addresses, tags and notes. Two more kinds of entry go in alongside:

  1. Phone numbers as digits only. +1 (555) 010-0123 is also stored as 15550100123, so any run of its digits matches.
  2. Latin transliterations of names in other scripts. Александр Петров is also stored as aleksandr petrov and aleksandrpetrov, and Νίκος Παπαδόπουλος as nikos papadopoulos.

A query is folded the same way and split into words, and a contact matches when every word appears somewhere in its index, in any field. That's why jose valencia finds José Álvarez, whose address is in Valencia, although no single field contains both words. Names that start with the query come first, then names that contain it, then everything else, with the most recently active contacts first among equals.

Try

10 sample contacts. Type to search them.

  • Olivia BennettNorthwind Studio
  • Wei ZhouLantern Tea Co.
  • José ÁlvarezÁlvarez Cerámica
  • Taro YamadaHinoki Joinery
  • Chloé DuboisAtelier Dubois
  • Zoë MüllerMüller & Lang
  • 김민준Hangang Labs
  • Νίκος ΠαπαδόπουλοςKyma Travel
  • Александр ПетровSever Logistics
  • Øystein BergFjord Supply
Figure 1. Local CRM's search running in your browser on ten sample contacts. Their index was built by the app's own code; only your query is folded here. Try the suggestions, including the two that find nothing.

When the match isn't in the name, Local CRM shows where it was, like Tag · VIP or mobile · +1 (555) 010-0123, so you can see at a glance why a contact turned up.

Where folding stops#

Folding and transliteration remove differences that don't matter. They can't guess what someone meant, and two of the searches in the figure come back empty on purpose.

Ø is a letter, not an o with a mark. In Danish and Norwegian, ø and æ are letters of their own, and Unicode treats them that way too. Diacritic folding leaves them alone, so oystein doesn't find Øystein Berg. Type the ø, or search for another part of the name.

Transliteration follows one reading. The standard Latin transliteration of Han characters uses Mandarin readings. That's right for a Chinese name, but a Japanese name written in kanji gets the Mandarin reading too: Yamada Tarō comes out as shan tian tai lang. Local CRM also indexes the phonetic name fields of the Contacts app, so when Yamada is filled in there, yamada finds him.

People spell their own names. The standard romanization of the Korean 김 is gim, so kim doesn't find 김민준, though Kim is how many people with that name write it in Latin letters. No rule can know that, but the person can tell you: add the spelling they use as a nickname, and search finds it from then on.

If you build search for names#

What we'd pass on from building this:

  • Fold at write time, show the original. Keep the folded index next to the record and never display it; people should always see names as they were written.
  • Add ways in, never replace. A transliteration or a digits-only number is an extra key, not a substitute for the original value.
  • Say why something matched. Showing the matched field makes a surprising result understandable instead of suspicious.
  • Let people teach the search. Nicknames and phonetic names are where spellings no rule can guess belong. Index them like everything else.

Local CRM's search covers every field of every contact, plus todos, notes, timeline entries and tags. The same folding decides which tags count as duplicates when two devices sync, part of how Local CRM merges records without a server.

Questions this note answers

Does Local CRM's search ignore accents and capital letters?
Yes. Every field is compared in a folded form without case, accents or width differences, so “jose”, “JOSE” and “jose” all find José Álvarez.
Can I find a contact by part of their phone number?
Yes. Local CRM also stores each phone number as digits only, so “0100123” or “555 0123” finds +1 (555) 010-0123 however the number was formatted.
How do I find a contact whose name is written in Chinese, Cyrillic or Greek?
Type it in Latin letters. Local CRM adds a Latin transliteration of names in other scripts to its search index (pinyin for Chinese), so “aleksandr” finds Александр and “nikos” finds Νίκος.
Why doesn't “kim” find 김민준?
The standard romanization of 김 is “gim”, and that's what the transliteration produces. Add the spelling the person uses, such as Kim, as a nickname or phonetic name and search finds it.

In Local CRM: Search, Contacts

Share this note

Select any passage to quote it with a link straight to it.