Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 14 – Search, geospatial and probabilistic data structures

Search for Indian languages and mixed scripts

How to normalise Indian-script text before indexing, split words without breaking vowel signs, and match queries typed in Latin letters or two scripts.

  • Advanced
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 4 of 25 minutes free
Free: Two spellings that look the same. With a pass: the other 8 sections and 3 interview answers, about 21 minutes.

What you will learn

  • Normalise Unicode text in Indian scripts the same way at index time and at query time
  • Split Indian-script text into words without breaking vowel signs, viramas or nuktas
  • Handle transliterated and code-mixed queries with a Latin field and a phonetic key
  • Plan language coverage where built-in analysers are missing
  • Measure relevance per language and per script with judged test queries

Before you start

What you’ll learn · Before you start

What you will learn

  • Normalise Unicode text in Indian scripts the same way at index time and at query time
  • Split Indian-script text into words without breaking vowel signs, viramas or nuktas
  • Handle transliterated and code-mixed queries with a Latin field and a phonetic key
  • Plan language coverage where built-in analysers are missing
  • Measure relevance per language and per script with judged test queries

Before you start

On this page

Search for Indian languages fails in ways that English search rarely does, and three changes fix most of it. The same visible word can be stored as different sequences of Unicode code points, so every title and every query is first normalised to one form, NFC (Normalization Form C): The Unicode normal form that decomposes a string and then recomposes it into one standard sequence, so canonically equivalent spellings of the same text become the same code points.. Words are then split by Unicode’s rules, which keep vowel signs, viramas and nuktas inside their word, not by a pattern written for English. And many people type Hindi, Tamil or Bengali in Latin letters, “kurta” or “saree”, or type queries in mixed scripts, so the index also keeps a Latin form of every word. Elasticsearch ships analysers for Hindi and Bengali but not for Tamil, Telugu, Marathi, Kannada or Malayalam, so those languages are assembled from Unicode components and checked against real queries.

Two spellings that look the same

Unicode encodes many letters of Indian scripts in more than one way. Devanagari क़ (qa) is one code point, U+0958, and also क followed by the nukta sign. Tamil, Malayalam and Bengali write some vowel signs in two parts, typed as one code point or as the two parts. UAX #15 calls such sequences canonical equivalence: Unicode's rule that two sequences of code points, such as a precomposed letter and the letter followed by its mark, represent the same text and should look and behave the same.: they should look and behave the same, yet a comparison of code points sees different strings (UAX #15). Which form a title arrives in depends on the keyboard, the app and any converter it passed through. The example stores four words in one form and searches in the other:

Four words, two encodings each, before and after NFC Python · normalise_indic.py
import unicodedata

# Four words, each typed in two ways that look the same on screen. Escapes keep the code points visible.
WORDS = [
    ("Hindi, 'pen'", "\u0958\u0932\u092e", "\u0915\u093c\u0932\u092e"),
    ("Tamil, 'flag'", "\u0b95\u0bca\u0b9f\u0bbf", "\u0b95\u0bc6\u0bbe\u0b9f\u0bbf"),
    ("Bengali, 'bottle'", "\u09ac\u09cb\u09a4\u09b2", "\u09ac\u09c7\u09be\u09a4\u09b2"),
    ("Malayalam, 'Kochi'", "\u0d15\u0d4a\u0d1a\u0d4d\u0d1a\u0d3f", "\u0d15\u0d46\u0d3e\u0d1a\u0d4d\u0d1a\u0d3f"),
]


def code_points(text):
    return " ".join(f"U+{ord(c):04X}" for c in text)


def nfc(text):
    return unicodedata.normalize("NFC", text)


for label, a, b in WORDS:
    print(f"{label}: {a}")
    print(f"  typed as  {code_points(a)}")
    print(f"  or as     {code_points(b)}")
    print(f"  equal? {a == b}   equal after NFC? {nfc(a) == nfc(b)}   NFC: {code_points(nfc(a))}")

# Titles stored in the first form, queries typed in the second: raw comparison, then NFC on both sides.
titles = [a for _, a, _ in WORDS]
queries = [b for _, _, b in WORDS]
raw_hits = sum(q in titles for q in queries)
index = {nfc(t) for t in titles}
nfc_hits = sum(nfc(q) in index for q in queries)
print(f"raw strings: {raw_hits} of {len(queries)} queries match; NFC on both sides: {nfc_hits} of {len(queries)}")

# Checks: both forms of every word normalise to one string, and normalising twice changes nothing.
assert all(nfc(a) == nfc(b) for _, a, b in WORDS)
assert all(nfc(nfc(a)) == nfc(a) for _, a, _ in WORDS)
print("checks passed")

Output

Hindi, 'pen': क़लम
  typed as  U+0958 U+0932 U+092E
  or as     U+0915 U+093C U+0932 U+092E
  equal? False   equal after NFC? True   NFC: U+0915 U+093C U+0932 U+092E
Tamil, 'flag': கொடி
  typed as  U+0B95 U+0BCA U+0B9F U+0BBF
  or as     U+0B95 U+0BC6 U+0BBE U+0B9F U+0BBF
  equal? False   equal after NFC? True   NFC: U+0B95 U+0BCA U+0B9F U+0BBF
Bengali, 'bottle': বোতল
  typed as  U+09AC U+09CB U+09A4 U+09B2
  or as     U+09AC U+09C7 U+09BE U+09A4 U+09B2
  equal? False   equal after NFC? True   NFC: U+09AC U+09CB U+09A4 U+09B2
Malayalam, 'Kochi': കൊച്ചി
  typed as  U+0D15 U+0D4A U+0D1A U+0D4D U+0D1A U+0D3F
  or as     U+0D15 U+0D46 U+0D3E U+0D1A U+0D4D U+0D1A U+0D3F
  equal? False   equal after NFC? True   NFC: U+0D15 U+0D4A U+0D1A U+0D4D U+0D1A U+0D3F
raw strings: 0 of 4 queries match; NFC on both sides: 4 of 4
checks passed

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 normalise_indic.py

Raw comparison finds none of the four. Normalisation Form C decomposes a string and recomposes it into one standard sequence, so both spellings become the same string and all four queries match. The Hindi word shows a detail worth knowing: NFC turns U+0958 into two code points, क and the nukta, because precomposed nukta letters are excluded from composition. Normalise when you index and again when you search, or the two sides drift apart. Step through two of the words:

Interactive · step by step Two spellings of one word, and the one string NFC makes of them Step through the two words. Each box shows a word's code points in hexadecimal; NFC turns both spellings into one sequence.
Two spellings of one word, and the one string NFC makes of them, step 5 of 5. Showing क़लम, one way, क़लम, the other way, NFC of either, கொடி, one way, கொடி, the other way, NFC of either. In focus: NFC of either, NFC of either.क़लम, one way0958 0932 092Eक़लम, the other way0915 093C 0932 092ENFC of either0915 093C 0932 092Eகொடி, one way0B95 0BCA 0B9F 0BBFகொடி, the other way0B95 0BC6 0BBE 0B9F 0BBFNFC of either0B95 0BCA 0B9F 0BBF

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Hindi, two ways. The Hindi word for pen can start with the one code point U+0958, or with क followed by the nukta sign. On screen the two look the same; to an index they are different strings.
  2. NFC on the Hindi word. NFC first decomposes, so U+0958 becomes क and the nukta. It would then recompose, but precomposed nukta letters are excluded from composition, so both spellings end as four code points.
  3. Tamil, two ways. The Tamil word for flag has a two-part vowel sign. It can be typed as the one code point U+0BCA, or as its two parts, U+0BC6 and U+0BBE.
  4. NFC on the Tamil word. Here NFC recomposes the two parts into U+0BCA, so the second spelling becomes the first. Either way the word is one string of four code points.
  5. One string each. Once titles and queries both go through NFC, each pair is one string, and in the program all four queries match. Normalise on one side only and the other side keeps its own spelling.

Titles and queries pass through NFC, word splitting and folding, then each word is indexed in its own script and as a Latin key.A listing title or a queryin Devanagari, Tamil or another script,in Latin letters, or in two at once1. Normalise to NFCtwo spellings of one characterbecome one code-point sequence2. Split into wordsvowel signs, viramas and nuktasstay inside their word3. Fold what NFC keeps apartnative digits, zero-width joiners,older Malayalam chillu spellings4. Index every word twiceA query word matches a titlewhen either form matchesin its own scriptas a Latin key:kurta, kurtha and kurtaabecome one term

One chain for titles and queries

Text description of the diagram

The diagram is a column of steps, from top to bottom. The same steps run on every listing title and on every query.

  1. The input: a listing title or a query, written in Devanagari, Tamil or another script, in Latin letters, or in two scripts at once.
  2. Normalise to NFC, so that two spellings of one character become one sequence of code points.
  3. Split into words, keeping vowel signs, viramas and nuktas inside the word they belong to.
  4. Fold what NFC keeps apart: native digits, zero-width joiners and the older spelling of Malayalam chillu letters.
  5. Index every word twice: in its own script, and as a Latin key in which spellings such as kurta, kurtha and kurtaa become one term.

At the bottom: a query word matches a title when either form matches.

Search for Indian languages chains NFC with further steps, each removing another source of mismatch:

  • Unicode word splitting, as in the ICU tokenizer, keeps every vowel sign, virama and nukta inside its word.
  • Folding turns native digits into 0 to 9, removes zero-width joiners and merges both spellings of Malayalam chillu letters.
  • A Latin field stores every word transliterated, so queries in Latin letters or in mixed scripts find Devanagari titles.
  • A phonetic key folds spelling variants such as kurta, kurtha and kurtaa into one term.
  • Per-language analysers add stop words and stemming where Elasticsearch has them.
  • Judged test queries per language and per script show whether each change helped.

Checkpoint

Check: One string from two spellings

  1. Question 1 of 2 The Hindi word क़लम can start with the one code point U+0958 or with क and a nukta. How many code points does it have after NFC, whichever way it was typed?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Four, क, the nukta, ल and म, because precomposed nukta letters are excluded from composition

    Normalization Form C decomposes a string and then recomposes it into one standard sequence. U+0958 is excluded from recomposition, so both spellings become U+0915 U+093C U+0932 U+092E, and the comparison then matches.

  2. Question 2 of 2 Titles are normalised to NFC when they are indexed, but queries are searched as they were typed. What happens?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A query typed in the other spelling still misses, because it is compared with the titles' NFC form

    Which form text arrives in depends on the keyboard, the app and any converter it passed through. Normalise when you index and again when you search, or the two sides drift apart.

Splitting words: the regex failure reproduced Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Folding what NFC keeps apart, in a fixed order
  • Matching Latin-letter and mixed-script queries, three designs measured
  • Choosing analysers for the languages that have none
  • Measuring relevance per language and per script
  • Practise: match titles in any script
    • Match Indian-script titles from queries in any script
  • Interview questions
    • Why must search normalise text to one Unicode form, such as NFC, both when it indexes a title and when it reads a query?
    • Shoppers search a catalogue of Hindi product titles by typing words in Latin letters, such as kurta or saree, and sometimes mix both scripts in one query. Design the indexing and the query path so that these searches find the Devanagari listings, and say how you would know it works.
    • After a popular keyboard app updates, some Hindi and Malayalam searches return nothing, although the listings exist and look identical to the queries on screen. How do you find the cause, fix it and stop it from coming back?
    • Your marketplace adds search in ten Indian languages to an Elasticsearch cluster, and only Hindi and Bengali have built-in analysers. Plan the analysis chains, the index layout and how you decide that each language is ready to launch.
  • Key takeaways

4 diagrams · 2 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 43 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Design a unique ID generator

Terms in this lesson

canonical equivalence
Unicode's rule that two sequences of code points, such as a precomposed letter and the letter followed by its mark, represent the same text and should look and behave the same.
chillu (Malayalam)
A Malayalam consonant written without its inherent vowel as a letter of its own. It has a single code point, and older text writes it as the consonant, a virama and a zero-width joiner.
combining mark
A Unicode character, such as a vowel sign, a virama or a nukta in Indian scripts, that attaches to the character before it instead of standing alone.
mean reciprocal rank (MRR)
The average, over a set of queries, of one divided by the rank of the first relevant result, so a relevant result in first place scores 1 and one in third place scores 1/3.
NFC (Normalization Form C)
The Unicode normal form that decomposes a string and then recomposes it into one standard sequence, so canonically equivalent spellings of the same text become the same code points.
phonetic key
A folded spelling of a word, built by rules such as dropping vowel length or aspiration, so that variant spellings like kurta, kurtha and kurtaa share one term in the index.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.