Bangla addresses are weird. Not because Bangla is weird — because the systems we get from international vendors don't understand any of the conventions a Bangladeshi will type into a search box. Gulshon is Gulshan with a phonetic vowel swap. House 23, Road 11, Banani is structured information, not free text. সড়ক ১১, ধানমন্ডি uses Bangla numerals. hospital near Banani is a POI category query, not a street.
On our benchmark of 30 curated BD queries, plain Nominatim gets 77% Hit@5. Banglish queries: 0%. Landmark phrases: 33%. We get 100% / 75% / 100% by running every query through a normalisation pipeline called bdnlp before it hits Nominatim. Here's how it works.
The pipeline
Six steps. The whole thing is <500 LOC of Go + two YAML data files.
- Bangla → ASCII numerals. ১২ → 12. Pre-built map; one pass over the string.
- Holding-number parser. Regex matches over
House X, Road Y, Block Z, Sector Nand the BN equivalents (বাড়ি, সড়ক, ব্লক, সেক্টর). - Landmark detector. Catches preposition tokens (near, কাছে, beside, পাশে) followed by a category (hospital, school, মসজিদ, মাদ্রাসা). Translates Bangla categories to English so Nominatim can match POIs.
- Locality matcher. Lowercase + match against 1,400+ curated BD place spellings — Banglish, Bangla, abbreviations. Gulshon → canonical Gulshan.
- Variant generation. Build 1–3 alternative queries to fan out to Nominatim. E.g. for "House 23, Road 11, Banani": try the locality alone, the full address, and a variant with the parsed components reordered.
- Re-rank. Merge the per-variant results by
(osm_type, osm_id). Score each result by how many parsed components match the returned address dict. Sort.
The data
Two embedded YAML files do most of the heavy lifting:
places.yaml— 1,407 BD places with their canonical name, Bangla spelling, and known variants. Harvested from OSM + manually curated for the top 200 by population.landmarks.yaml— the preposition + category vocabulary, in both languages, plus a BN→EN translation table for landmark categories.
A typical row:
- canonical: Gulshan bangla: গুলশান variants: [Gulshon, Gulshan-1, Gulshan-2, গুলশন] - canonical: Dhanmondi bangla: ধানমন্ডি variants: [Dhanmondi R/A, Dhanmondi residential, ধানমণ্ডি]
The benchmark
We test against 30 hand-picked queries spanning Banglish, Bangla, landmark, and holding-number cases. The benchmark is checked into the repo at tools/benchmark/. CI runs it on every PR to internal/bdnlp.
| Category | MAP | Nominatim raw | |
|---|---|---|---|
| All 30 queries | 100% | 77% | 87% |
| Banglish phonetics | 75% | 0% | 50% |
| Bangla script | 100% | 83% | 100% |
| Landmark phrases | 67% | 33% | 50% |
| Holding numbers | 100% | 100% | 100% |
What we're still bad at
- Mixed-script abbreviations. Things like "DOHS 4/A রোড 12" — we get the locality right but lose the numbers.
- Misspelled landmarks. "hospitle" vs "hospital". We don't do fuzzy POI matching yet.
- Long form addresses with multiple ambiguous tokens. Anything with three or more candidate localities is a coin-flip.
Why we built it ourselves
The honest answer: there's no off-the-shelf normaliser for Bangla addresses. libpostal does international address parsing brilliantly but doesn't know Bangla place names. Indic NLP toolkits target Hindi/Tamil/Telugu, not Bangla addresses. We could have trained a model — but for 30 query categories and ~1,400 places, regex + a lookup table runs in microseconds and is debuggable. The dataset is the moat. The code is straightforward.
The whole pipeline is in the public repo: services/gateway/internal/bdnlp/. PRs welcome — especially for places we missed.