npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lacspace/nepali-match

v1.1.0

Published

Nepali and English matching for names and keywords in running text: normaliseNe() folds spelling variants (chandrabindu → anusvara, half nasal + consonant → anusvara, long/short vowels, nukta/ZWJ/ZWNJ, NFC, Devanagari digits); whole-word matching that let

Readme

@lacspace/nepali-match

Find names and keywords in Nepali and English text the way a Nepali reader would:

  • Spelling variants match: काठमाडौँ, काठमाडौं and काठमाण्डौ, or चन्द्र and चंद्र.
  • Words with a postposition match: झापाको is Jhapa and चितवनमा is Chitwan.
  • Different words don't match: पर्वतारोही (climber) is not Parbat.
  • Short English acronyms are exact-case: "SEE results" counts, "Come and see" doesn't.

It is pure JS with no dependencies, and it is safe for React Native.

import { createMatcher, districtTerms, near, normaliseNe } from "@lacspace/nepali-match";

const areas = createMatcher(districtTerms());
areas.ids("चितवनमा बाढी, झापाको मेचीनगरमा पहिरो");   // ["chitwan", "jhapa"]
areas.test("दुई पर्वतारोही बेपत्ता");                  // false
areas.find("काठमाण्डौबाटै आएका")[0];
// { id: "kathmandu", term: "काठमाण्डौ", lang: "ne", index: 0, end: 13, text: "काठमाण्डौबाटै", suffix: "बाटै" }

const exam = ["परीक्षा", "नतिजा", "विज्ञापन", "exam", "result"];
near("लोकसेवा आयोगले निजामती विधेयकमा राय दियो", "लोकसेवा", exam, 60);   // null: not exam news
near("लोकसेवा आयोगको खरिदार परीक्षाको नतिजा", "लोकसेवा", exam, 60);    // { a, b, gap }

createMatcher([{ id: "see", en: "SEE", ne: "एसईई" }]).test("Come and see");   // false
normaliseNe("काठमाडौँ") === normaliseNe("काठमाडौं");                          // true

How matching works

normaliseNe(text, { loose?, digits? }) applies these steps in order:

  1. NFC.
  2. Chandrabindu → anusvara.
  3. A half nasal before a consonant (ङ्, ञ्, ण्, न्, म्) → anusvara.
  4. ई/ी → इ/ि and ऊ/ू → उ/ु.
  5. Nukta, ZWJ, ZWNJ and soft hyphens are removed.
  6. Devanagari digits → ASCII.
  7. Whitespace collapses.

loose: true also folds श/ष → स, व → ब and ण → न. hyphenAsSpace: true treats hyphens as spaces, so "Solu Khumbu" matches "Solu-Khumbu" and "Bardaghat-Susta". Latin text is left as it is.

Nepali terms match as whole words:

  • Nothing may stand before the word except a space or punctuation.
  • After the word, a chain of up to three POSTPOSITIONS may follow: मा, को, का, की, ले, लाई, बाट, सँग, देखि, सम्म, तिर, भित्र, हरू, मै, बाटै, नै … Anything else after it means it's a different word.
  • Turn the built-in list off with postpositions: false. Add your own with extraSuffixes; with postpositions: false they become the only suffixes allowed.
  • "Like" (जस्तो) is deliberately not in the list: "काठमाडौंजस्तो ठूलो सहर" is a comparison, not a location.
  • With joinNepali: true, any space in a multi-word Nepali term is optional, so "बर्दघाट सुस्ता पूर्व" also matches "बर्दघाट सुस्तापूर्व".

English terms match as whole words, ignoring case. Exceptions:

  • Under the default caseSensitive: "auto" rule, an all-caps term of 2–5 letters (SEE, NEB, PSC) must match its case exactly.
  • Set caseSensitive per term or per matcher to change this.
  • An exact-case term longer than 5 letters also matches in ALL CAPS, so "KATHMANDU" in a headline counts. Turn this off with allCaps: false.
  • Short acronyms can't tell a shouted headline apart: "COME AND SEE" still matches SEE. Check the case of the surrounding text if your input has shouty headlines.

Overlaps: the longest match wins, so "Nawalparasi West" beats "Nawalparasi". Pass overlaps: true to keep both.

Every match carries index/end offsets into the original text (after NFC), so you can highlight or cut it.

API

  • createMatcher(terms, options?) returns { find(text), test(text, id?), ids(text), prepare(text), size }. Compile it once and reuse it.
  • prepare(text, options?) / matcher.prepare(text) normalise text once. Every find, test, ids, findTerms, contains and near accepts the result in place of a string, so a rules loop over one article normalises it once, not once per rule. A prepared text made with different options is re-prepared automatically.
    • A term is either a string, or { id?, en?, ne?, aliases?, caseSensitive? }. en and ne each take one spelling or an array.
  • findTerms(text, terms, options?) and contains(text, terms, options?) are one-off shortcuts.
  • near(text, a, b, maxChars | options, sameSentence?) returns the closest { a, b, gap } pair of matches within maxChars (default 60), in either order, or null.
    • a and b can be terms, a compiled Matcher, or a Match[] you already found in the same text.
    • By default both must sit in the same sentence. A sentence ends at । ॥ ? ! or a newline, or at "." before a space.
    • Dotted abbreviations such as ने.क.पा. and U.S. never end a sentence.
  • sentenceSpans(text) returns the sentence boundaries near uses.
  • splitSuffix(word): for example, "जिल्लाहरूमा" → { stem: "जिल्ला", suffixes: ["हरु", "मा"] }.
  • districtTerms() returns all 77 districts.
    • Names and ids: names come from @lacspace/nepali-utils. Ids are slugs such as "nawalparasi-east", and province is a number.
    • Spellings: each district carries the spellings seen in real copy, plus every alias in @lacspace/nepali-utils: Kavre/काभ्रे, Rukum East/Rukum Purba/रुकुम पूर्व, Parasi/परासी, Bardaghat Susta West, Kapilbastu, मोरंग/मोरङ…
    • Case: English district names are exact-case, so "dang it" is not Dang. Names over 5 letters also match in ALL CAPS.
    • Known edge: a place that shares a district's name still matches it. For example, Nawalpur in Sarlahi matches the Nawalpur alias of Nawalparasi East.
    • ambiguous: true: marks Parbat, because पर्वत also means "mountain". Confirm it with near() or with district/area context before you tag a story.

Speed

A ~1,500-character article checked against all 77 districts (258 spellings) takes about 0.16 ms once prepared, and about 0.3 ms from a raw string.

Limits

  • The matcher is rule-based and does no stemming beyond postpositions. Verb forms and compounds won't match, which is the point.
  • Spellings that differ in more than the folded letters (for example गोर्खा and गोरखा) need an alias.

Built by Lacspace. Licensed under the Lacspace Free Licence v1.0 (see LICENSE).