<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mmsalmanfaris</title>
    <description>The latest articles on DEV Community by mmsalmanfaris (@mmsalmanfaris).</description>
    <link>https://dev.to/mmsalmanfaris</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1232756%2F7116a700-f4c8-49cd-91d2-e437b960ce9f.jpeg</url>
      <title>DEV Community: mmsalmanfaris</title>
      <link>https://dev.to/mmsalmanfaris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mmsalmanfaris"/>
    <language>en</language>
    <item>
      <title>How to Validate Sri Lankan NIC Numbers, Phone Numbers, and LKR Amounts.</title>
      <dc:creator>mmsalmanfaris</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:13:39 +0000</pubDate>
      <link>https://dev.to/mmsalmanfaris/how-to-validate-sri-lankan-nic-numbers-phone-numbers-and-lkr-amounts-3fp0</link>
      <guid>https://dev.to/mmsalmanfaris/how-to-validate-sri-lankan-nic-numbers-phone-numbers-and-lkr-amounts-3fp0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://github.com/mmsalmanfaris/lanka-validations" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/lanka-validations" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="https://mmsalmanfaris.github.io/lanka-validations/" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; install &lt;a href="https://www.npmjs.com/package/lanka-validations" rel="noopener noreferrer"&gt;&lt;code&gt;lanka-validations&lt;/code&gt;&lt;/a&gt; from npm and call &lt;code&gt;isValidNic('921234567V')&lt;/code&gt; from &lt;code&gt;lanka-validations/nic&lt;/code&gt;. It returns &lt;code&gt;true&lt;/code&gt; for valid old-format (9 digits plus V or X) and new-format (12 digits) Sri Lankan NIC numbers. The same package validates phone numbers, passports, driving licences, vehicle plates, LKR amounts, postal codes, CBSL bank codes, and TIN/VAT numbers. It has no runtime dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;lanka-validations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parseNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;parseNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;dobString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// '1992-05-02'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rest of this guide explains how Sri Lankan identifiers are structured, how to validate each one, how to plug the checks into Zod, React Hook Form, and Express, and what format validation can and cannot prove.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is lanka-validations?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mmsalmanfaris/lanka-validations" rel="noopener noreferrer"&gt;lanka-validations&lt;/a&gt; is an open-source (MIT) TypeScript and JavaScript library for validating, parsing, and formatting Sri Lankan data. It covers National Identity Card (NIC) numbers, phone numbers, passports, driving licences, vehicle number plates, LKR currency, dates of birth, gender, postal codes, CBSL bank codes, TIN/VAT numbers, provinces, and districts. It runs in Node.js 20+, browsers, Deno, and Bun.&lt;/p&gt;

&lt;p&gt;Most Sri Lankan web forms ask for some of these values: sign-up pages, KYC onboarding, checkout, payroll, and government-service portals. Without a library, every team writes its own NIC regex and phone-prefix list, and those copies drift out of date. lanka-validations keeps the rules and reference tables in one tested place, and records the official source for each table.&lt;/p&gt;

&lt;p&gt;Key facts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime dependencies&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtimes&lt;/td&gt;
&lt;td&gt;Node.js 20+, browsers, Deno, Bun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module formats&lt;/td&gt;
&lt;td&gt;ESM and CommonJS, with TypeScript types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size of &lt;code&gt;isValidNic&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;About 1 kB gzipped (enforced in CI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical validation time&lt;/td&gt;
&lt;td&gt;About 0.4 to 0.9 µs for NIC and phone checks (local benchmark)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Languages&lt;/td&gt;
&lt;td&gt;English, Sinhala, and Tamil for currency words, gender, regions, errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Licence&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How a Sri Lankan NIC number is structured
&lt;/h2&gt;

&lt;p&gt;A Sri Lankan NIC number encodes the holder's birth year, birthday, and gender. There are two formats, and both are still in circulation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Layout&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Old (before 2016)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;921234567V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2-digit year, 3-digit day, 3-digit serial, check digit, V or X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New (12 digits)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;199212304567&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4-digit year, 3-digit day, 4-digit serial, check digit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The day number counts from 1 January using a 366-day year, so day 123 is 2 May. For women, 500 is added to the day, so a woman born on the same day has 623 instead of 123. Converting old to new adds &lt;code&gt;19&lt;/code&gt; before the year and a &lt;code&gt;0&lt;/code&gt; before the serial.&lt;/p&gt;

&lt;p&gt;That is why a plain regex is not enough. &lt;code&gt;/^\d{9}[VX]$/&lt;/code&gt; accepts &lt;code&gt;929994567V&lt;/code&gt;, which has day 999 and cannot be a real birthday. A real validator has to decode the day number, check it against the calendar, and reject birth dates in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate and parse an NIC number
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;isValidNic&lt;/code&gt; returns a boolean. &lt;code&gt;parseNic&lt;/code&gt; returns the decoded details, or &lt;code&gt;null&lt;/code&gt; if the number is invalid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;parseNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;convertOldToNewNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;convertNewToOldNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;formatNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;199212304567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;9212345678&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1992&lt;/span&gt;
&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'male'&lt;/span&gt;
&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;dobString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// '1992-05-02'&lt;/span&gt;
&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;isVoter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;newNic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// '199212304567'&lt;/span&gt;

&lt;span class="nf"&gt;convertOldToNewNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '199212304567'&lt;/span&gt;
&lt;span class="nf"&gt;convertNewToOldNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;199212304567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '921234567V'&lt;/span&gt;
&lt;span class="nf"&gt;formatNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;199212304567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;spaced-new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '1992 123 0456 7'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you need an error message, use &lt;code&gt;validateNic&lt;/code&gt;. It returns either &lt;code&gt;{ isValid: true, data }&lt;/code&gt; or &lt;code&gt;{ isValid: false, error, errors, code }&lt;/code&gt;, so TypeScript narrows the type for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validateNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validateNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;92123456V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dobString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'NIC_INVALID_FORMAT'&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'Invalid NIC format. Must be 9 digits followed by V/X or 12 digits.'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;code&lt;/code&gt; is stable across versions, so you can branch on it or translate it without matching on English text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate Sri Lankan phone numbers
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;isValidMobile&lt;/code&gt; accepts Sri Lankan mobile numbers in local or &lt;code&gt;+94&lt;/code&gt; form, with spaces, dashes, brackets, dots, or slashes. &lt;code&gt;parsePhone&lt;/code&gt; tells you the line type and the operator that the prefix was originally allocated to. &lt;code&gt;formatPhone&lt;/code&gt; converts to E.164, national, or international format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isValidMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValidLandline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsePhone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formatPhone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/phone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;isValidMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;077 123 4567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidLandline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0112345678&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true (Colombo)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parsePhone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+94 77 123 4567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'Dialog'&lt;/span&gt;
&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// '0771234567'&lt;/span&gt;
&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'mobile'&lt;/span&gt;

&lt;span class="nf"&gt;formatPhone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0771234567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e164&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '+94771234567'&lt;/span&gt;
&lt;span class="nf"&gt;formatPhone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0771234567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;international&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '+94 77 123 4567'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store phone numbers in E.164 (&lt;code&gt;+94771234567&lt;/code&gt;). It is the format SMS gateways and most APIs expect, and it removes duplicates caused by spacing differences.&lt;/p&gt;

&lt;p&gt;Two details matter in production. First, &lt;code&gt;isValidPhone&lt;/code&gt; also accepts short codes such as &lt;code&gt;119&lt;/code&gt; (police emergency). For a "contact number" field, use &lt;code&gt;isValidMobile&lt;/code&gt; or &lt;code&gt;isValidContactPhone&lt;/code&gt;, which reject them. Second, the operator comes from the prefix's original allocation. Because of number portability, a &lt;code&gt;077&lt;/code&gt; number may now be on a different network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parse and format LKR currency, with lakh grouping and words
&lt;/h2&gt;

&lt;p&gt;Sri Lankan amounts are written in several ways: &lt;code&gt;Rs. 1,500.50&lt;/code&gt;, &lt;code&gt;LKR 1,50,000&lt;/code&gt;, or with the Sinhala &lt;code&gt;රු.&lt;/code&gt; and Tamil &lt;code&gt;ரூ.&lt;/code&gt; symbols. &lt;code&gt;parseLKR&lt;/code&gt; reads all of them. &lt;code&gt;formatLKR&lt;/code&gt; writes either Western grouping or South Asian lakh grouping, and &lt;code&gt;lkrToWords&lt;/code&gt; writes the amount in words for cheques and receipts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parseLKR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formatLKR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lkrToWords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parseLKRMinorUnits&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/currency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;parseLKR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rs. 1,500.50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1500.5&lt;/span&gt;
&lt;span class="nf"&gt;parseLKR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LKR 1,50,000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 150000&lt;/span&gt;

&lt;span class="nf"&gt;formatLKR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1500000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'Rs. 1,500,000.00'&lt;/span&gt;
&lt;span class="nf"&gt;formatLKR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lakhScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// 'Rs. 15,00,000.00'&lt;/span&gt;

&lt;span class="nf"&gt;lkrToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'One Thousand Two Hundred Fifty Rupees Only'&lt;/span&gt;
&lt;span class="nf"&gt;lkrToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1500.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;si&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// 'රුපියල් එක්දහස් පන්සියයයි ශත පනහයි'&lt;/span&gt;
&lt;span class="nf"&gt;lkrToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1500.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// 'ஆயிரத்து ஐந்நூறு ரூபாய் ஐம்பது சதம்'&lt;/span&gt;

&lt;span class="nf"&gt;parseLKRMinorUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rs. 1,500.50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 150050n (cents, as a bigint)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;parseLKR&lt;/code&gt; returns a JavaScript number, which is fine for display and range checks. For balances, invoices, and anything you add up, use &lt;code&gt;parseLKRMinorUnits&lt;/code&gt;, which returns exact cents as a &lt;code&gt;bigint&lt;/code&gt; and avoids floating-point rounding errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Sri Lankan identifiers and reference data
&lt;/h2&gt;

&lt;p&gt;The library uses the same pattern for every identifier: &lt;code&gt;isValidX&lt;/code&gt; for a boolean, &lt;code&gt;validateX&lt;/code&gt; for a result with an error code, and &lt;code&gt;parseX&lt;/code&gt; or a lookup function for details.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;th&gt;Example call&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Passport&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isValidPassport('N1234567')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driving licence&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isValidDrivingLicense('B1234567')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle plate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parseVehiclePlate('wp cab 1234')?.formatted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'WP CAB-1234'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postal code to district&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lookupPostalCode('20000')?.district&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'Kandy'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBSL bank code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getBankByCode('7010')?.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'Bank of Ceylon'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taxpayer Identification No.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isValidTIN('123456789')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;District in Sinhala or Tamil&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isValidDistrict('කොළඹ')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gender in any language&lt;/td&gt;
&lt;td&gt;&lt;code&gt;normalizeGender('ஆண்')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'male'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An NIC is not a TIN. Individuals register with the Inland Revenue Department using their NIC and receive a separate TIN, so &lt;code&gt;isValidTIN('921234567V')&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;. Validate NIC fields with &lt;code&gt;isValidNic&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that a form's date of birth and gender match the NIC
&lt;/h2&gt;

&lt;p&gt;KYC forms often ask for the NIC, date of birth, and gender separately. Because the NIC encodes the other two, you can catch typos and mismatched entries before they reach a manual review queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;matchesNicDob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/dob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;matchesNicGender&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/gender&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// encodes a man born on 1992-05-02&lt;/span&gt;

&lt;span class="nf"&gt;matchesNicDob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1992-05-02&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;matchesNicDob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1995-01-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nf"&gt;matchesNicGender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;male&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;matchesNicGender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;female&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate a whole form with the schema validator
&lt;/h2&gt;

&lt;p&gt;For forms with several Sri Lankan fields, &lt;code&gt;createSchemaValidator&lt;/code&gt; validates the whole object in one call, runs cross-field rules, and infers the parsed output type in TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSchemaValidator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSchemaValidator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mobile&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;minAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchesNicDob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchesNicGender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1992-05-02&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;male&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCodes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// { phone: 'PHONE_UNSUPPORTED' }&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'Phone number prefix or short code is not in the bundled reference data.'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Show errors in Sinhala or Tamil
&lt;/h3&gt;

&lt;p&gt;Every failure carries a stable code, so you can show messages in the user's language. &lt;code&gt;localizeValidationResult&lt;/code&gt; translates a single result, and &lt;code&gt;localizeSchemaResult&lt;/code&gt; translates a whole form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validateNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;localizeValidationResult&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;localizeValidationResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validateNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;si&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// 'ජාතික හැඳුනුම්පත් ආකෘතිය වලංගු නොවේ.'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use it with Zod, React Hook Form, and Express
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;isValidX&lt;/code&gt; functions return plain booleans, so they work as custom checks in any validation library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zod&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isValidNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isValidMobile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/phone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter a valid Sri Lankan NIC number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValidMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter a Sri Lankan mobile number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;React Hook Form&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-hook-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isValidNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid NIC number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Express&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validateNic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/nic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/customers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validateNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;dateOfBirth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dobString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the same checks on the server even when the browser already ran them. Client-side validation helps the user; server-side validation protects your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safe on untrusted input
&lt;/h2&gt;

&lt;p&gt;Form fields and API bodies can contain anything. Every validator in lanka-validations accepts &lt;code&gt;unknown&lt;/code&gt; and never throws:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nf"&gt;isValidNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Objects and arrays are rejected without calling their &lt;code&gt;toString&lt;/code&gt;. Strings longer than 1,024 characters are rejected rather than truncated, which keeps validation time bounded. Property-based tests check these guarantees across thousands of generated inputs. The one deliberate exception is &lt;code&gt;lkrToWords&lt;/code&gt;, which throws a &lt;code&gt;RangeError&lt;/code&gt; for amounts too large to write in words.&lt;/p&gt;

&lt;p&gt;When you log identifiers, mask them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;maskNic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maskPhone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lanka-validations/privacy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;maskNic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;921234567V&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '******567V'&lt;/span&gt;
&lt;span class="nf"&gt;maskPhone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0771234567&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '******4567'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What format validation does not prove
&lt;/h2&gt;

&lt;p&gt;A valid format is not proof of identity. lanka-validations checks that a value is well-formed and consistent with published rules. It does not check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an NIC, passport, or licence number was issued, or belongs to the person entering it;&lt;/li&gt;
&lt;li&gt;a phone number is assigned and reachable;&lt;/li&gt;
&lt;li&gt;a bank account exists or is open;&lt;/li&gt;
&lt;li&gt;a TIN is registered with the IRD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NIC check digits are also not verified, because the Department for Registration of Persons has not published the algorithm. For KYC and payments, treat these checks as input hygiene, then confirm identity and accounts with the issuing authority or a verification provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the reference data comes from
&lt;/h2&gt;

&lt;p&gt;Bank codes, postal codes, phone prefixes, and district names change over time, so each bundled table records its official source and the date it was checked:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;Official source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provinces and districts&lt;/td&gt;
&lt;td&gt;Department of Census and Statistics (Census 2024)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bank codes&lt;/td&gt;
&lt;td&gt;Central Bank of Sri Lanka (CBSL) bank reference manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postal codes&lt;/td&gt;
&lt;td&gt;Sri Lanka Post code book&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone prefixes, short codes&lt;/td&gt;
&lt;td&gt;TRCSL National Numbering Plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driving licence classes&lt;/td&gt;
&lt;td&gt;Department of Motor Traffic (DMT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TIN and VAT&lt;/td&gt;
&lt;td&gt;Inland Revenue Department (IRD) registration guidelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;getReferenceDataMetadata()&lt;/code&gt; returns the source, as-of date, coverage, and known gaps for each table at runtime, so you can show or log which data version made a decision. The full list, with links to each source, is in the &lt;a href="https://github.com/mmsalmanfaris/lanka-validations#data-sources" rel="noopener noreferrer"&gt;README on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use something else
&lt;/h2&gt;

&lt;p&gt;lanka-validations is for Sri Lanka-specific data. For phone numbers from many countries, use &lt;code&gt;libphonenumber-js&lt;/code&gt;. For generic checks such as email addresses and URLs, use Zod or a general-purpose validator. The libraries combine well: Zod for the form shape, lanka-validations for the Sri Lankan fields.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Old and new NIC&lt;/th&gt;
&lt;th&gt;Decodes DOB and gender&lt;/th&gt;
&lt;th&gt;LKR lakh format and words&lt;/th&gt;
&lt;th&gt;Sinhala and Tamil&lt;/th&gt;
&lt;th&gt;Sources recorded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hand-written regex&lt;/td&gt;
&lt;td&gt;Partly&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;libphonenumber-js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (phones only)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lanka-validations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I validate a Sri Lankan NIC number in JavaScript?
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;lanka-validations&lt;/code&gt; and call &lt;code&gt;isValidNic(value)&lt;/code&gt; from &lt;code&gt;lanka-validations/nic&lt;/code&gt;. It returns &lt;code&gt;true&lt;/code&gt; for valid old-format (9 digits plus V or X) and new-format (12 digits) numbers. Use &lt;code&gt;validateNic(value)&lt;/code&gt; to also get the date of birth, age, gender, and an error code when the number is invalid.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I get the date of birth from a Sri Lankan NIC?
&lt;/h3&gt;

&lt;p&gt;Call &lt;code&gt;parseNic(nic)&lt;/code&gt;. It returns &lt;code&gt;dobString&lt;/code&gt; (for example &lt;code&gt;'1992-05-02'&lt;/code&gt;), &lt;code&gt;dob&lt;/code&gt; as a UTC &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;, and &lt;code&gt;gender&lt;/code&gt;. The birth year and the day of the year are encoded in the number. For women, 500 is added to the day, so &lt;code&gt;927234567V&lt;/code&gt; decodes to a woman born on 10 August 1992.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I convert an old NIC to the new 12-digit format?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;convertOldToNewNic('921234567V')&lt;/code&gt;, which returns &lt;code&gt;'199212304567'&lt;/code&gt;. It prefixes &lt;code&gt;19&lt;/code&gt; to the year and adds a &lt;code&gt;0&lt;/code&gt; before the serial number. &lt;code&gt;convertNewToOldNic&lt;/code&gt; converts back for people born between 1900 and 1999.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it work in React, Next.js, Deno, and Bun?
&lt;/h3&gt;

&lt;p&gt;Yes. It has no runtime dependencies and uses no Node.js-only APIs, so it runs in browsers, React and Next.js (client and server components), Node.js 20+, Deno, and Bun. Import from subpaths such as &lt;code&gt;lanka-validations/nic&lt;/code&gt; to keep the browser bundle small.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does validating an NIC confirm someone's identity?
&lt;/h3&gt;

&lt;p&gt;No. It checks the number's format and whether the encoded birth date is a real date in the past. It cannot tell whether the number was issued or who it belongs to. For KYC, verify identity with the issuing authority or a verification provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the bank and postal code data up to date?
&lt;/h3&gt;

&lt;p&gt;Each table records its official source and the date it was last checked. Call &lt;code&gt;getReferenceDataMetadata()&lt;/code&gt; to read them at runtime. If you find an outdated entry, &lt;a href="https://github.com/mmsalmanfaris/lanka-validations/issues" rel="noopener noreferrer"&gt;open an issue on GitHub&lt;/a&gt; with a link to the current official source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;lanka-validations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub (source code, issues, contributions):&lt;/strong&gt; &lt;a href="https://github.com/mmsalmanfaris/lanka-validations" rel="noopener noreferrer"&gt;github.com/mmsalmanfaris/lanka-validations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm package:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/lanka-validations" rel="noopener noreferrer"&gt;npmjs.com/package/lanka-validations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API documentation:&lt;/strong&gt; &lt;a href="https://mmsalmanfaris.github.io/lanka-validations/" rel="noopener noreferrer"&gt;mmsalmanfaris.github.io/lanka-validations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For AI coding assistants:&lt;/strong&gt; &lt;a href="https://mmsalmanfaris.github.io/lanka-validations/llms.txt" rel="noopener noreferrer"&gt;llms.txt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;lanka-validations is maintained by M.M. Salman Faris. If it saves you time, star the repository on &lt;a href="https://github.com/mmsalmanfaris/lanka-validations" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Bug reports and pull requests are welcome.&lt;/p&gt;

</description>
      <category>srilanka</category>
      <category>npm</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How I Built a Smart Parking System with ESP32 and FastAPI</title>
      <dc:creator>mmsalmanfaris</dc:creator>
      <pubDate>Wed, 04 Jun 2025 18:18:16 +0000</pubDate>
      <link>https://dev.to/mmsalmanfaris/how-i-built-a-smart-parking-system-with-esp32-and-fastapi-1e3h</link>
      <guid>https://dev.to/mmsalmanfaris/how-i-built-a-smart-parking-system-with-esp32-and-fastapi-1e3h</guid>
      <description>&lt;p&gt;Project Overview&lt;br&gt;
Finding a secure and available parking space in urban areas is a daily struggle. My final year project aimed to address this issue by creating a Smart Parking System using ESP32, IR sensors, FastAPI, Firebase, and React. The system automates vehicle parking, slot booking, mismatch alerts, display operations and monitoring using a real-time dashboard and IoT devices, ensuring a smoother, more secure parking experience.&lt;/p&gt;

&lt;p&gt;Key Features&lt;br&gt;
Real-time slot detection using IR sensors.&lt;br&gt;
Automatic number plate recognition using computer vision.&lt;br&gt;
Smart gate control via servo motors.&lt;br&gt;
User-friendly web interface for booking, tracking, and payments.&lt;br&gt;
Admin dashboard for system monitoring and device management.&lt;br&gt;
Alerts and mismatched parking notifications.&lt;br&gt;
Technology Stack&lt;br&gt;
Frontend — React, Tailwind CSS, Flowbite&lt;br&gt;
Backend — FastAPI, Python, JWT Auth, Pydantic , C++&lt;br&gt;
Database — Firebase Firestore&lt;br&gt;
IoT Devices — ESP32, IR sensors, SG90 Servo Motor, 16x2 LCD Display&lt;br&gt;
ML Model — OpenCV with custom-trained number plate detection&lt;br&gt;
Deployment — Localhost testing via Wifi and static IP&lt;/p&gt;

&lt;p&gt;System Architecture&lt;br&gt;
The system follows a 3-tier architecture:&lt;/p&gt;

&lt;p&gt;Presentation Layer (React App)&lt;br&gt;
Logic Layer (FastAPI Backend)&lt;br&gt;
Hardware Layer (IoT + Computer Vision)&lt;/p&gt;

&lt;p&gt;IoT Workflow&lt;/p&gt;

&lt;p&gt;Security &amp;amp; Access Control&lt;br&gt;
User and Admin roles with protected endpoints.&lt;br&gt;
JWT authentication ensures session control.&lt;br&gt;
Route-level protection on the frontend using token validation.&lt;br&gt;
Admin Dashboard Features&lt;br&gt;
View system usage (slots, bookings, users, alerts).&lt;br&gt;
Manage packages, slots, and devices.&lt;br&gt;
Access full system reports with export options (CSV, PDF).&lt;br&gt;
Real-time alerts for mismatch or functional errors.&lt;/p&gt;

&lt;p&gt;Machine Learning Component&lt;br&gt;
A lightweight OpenCV-based solution is implemented for Number Plate Recognition (NPR). The model filters blurry or low-confidence results, only sending high-confidence data to the backend. This integrates smoothly into the FastAPI pipeline.&lt;/p&gt;

&lt;p&gt;Handling Network &amp;amp; WiFi Changes&lt;br&gt;
ESP32 uses the router’s IP to send/receive data. When switching networks (e.g., to a mobile hotspot / Wifi), I updated the ESP32 IP and ensured the host device (backend) shared the same network range. This ensured stable communication across components.&lt;/p&gt;

&lt;p&gt;Challenges Faced&lt;br&gt;
Real-time sync between ESP32 and backend over WiFi required careful timeout tuning.&lt;br&gt;
Limited camera quality affected plate recognition, which was resolved with pre-processing.&lt;br&gt;
The Firebase structure had to be carefully designed for scalability and speed.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
This smart parking system merges the power of IoT, computer vision, and web development to tackle real-world challenges. It’s scalable and secure, and solves a common urban problem with automation and innovation.&lt;/p&gt;

&lt;p&gt;GitHub Repository: Link&lt;/p&gt;

&lt;p&gt;Let’s connect on LinkedIn if you’re hiring or have feedback!&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>programming</category>
      <category>react</category>
      <category>iot</category>
    </item>
  </channel>
</rss>
