🚗 Syntax-only license plate validator for European and nearby countries, including Moldova and Ukraine (Russia excluded). Supports Node.js, TypeScript, browser and PHP.
- ✅ 30 supported countries, including Moldova (
MD) — automatic or fixed-country mode - 🚫 Russia excluded by design
- 🔠 Normalizes input (spaces, hyphens, case)
- 🧠 Smart regex engine per-country (car/motorcycle aware)
- 🧩 Lightweight client SDK with UI, flags, dropdown, and
Inputmaskintegration ↕️ Accepts lowercase input, auto-coerced to UPPERCASE by mask tokenL- 🌐 Built-in i18n: IT and EN
- 🧯 Safe dependency autoload via CDN with configurable overrides
npm install @codecorn/euro-plate-validatorv1.0.16 CDN assets will be available after npm publishing. Use versioned URLs to avoid stale caches.
-
ESM (browser)
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.esm.js -
IIFE (global
window.EuroPlateValidator)https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.iife.min.js
-
ESM
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.mjs -
CJS (Node)
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.cjs
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/assets/css/styles.css- (compat alias)
…/assets/css/styles.css
import { validatePlate } from "@codecorn/euro-plate-validator";
// IT (car)
validatePlate("AB 123 CD", ["IT"], { vehicleType: "car" });
// → { isValid: true, matches:[{country:"IT", name:"Italy"}], checked:["IT"] }
// UK vs IE
validatePlate("AB12 CDE", ["UK", "IE"]);The client either generates the markup ( flag + input + country dropdown + status ) or attaches to an existing input .
type EuroPlateOptions = {
// DOM
input?: HTMLInputElement; // external input
wrapper?: string | HTMLElement | false; // if provided → auto-build UI
inputId?: string; // default: derived ( e.g., "epv-xxxx-plate" )
inputName?: string; // default: same
preserveInputAttrs?: boolean; // NEW: if true, does NOT overwrite existing id/name
autoFocusOnInit?: boolean; // NEW: default false ( no focus on init )
// ⚙️ UI configuration ( new: EVERYTHING under "ui" )
ui?: {
/**
* Where to show the status :
* - "block" → uses <div class="status"> under the input ( back-compat , default )
* - "inline" → overlays inside the input , does not change height
* - "off" → hides status text / icon
*/
statusMode?: "block" | "inline" | "off"; // default: "block"
/** Icon type for inline status ( ignored in "block" and "off" ) */
statusIcon?: "none" | "icon" | "pill"; // default: "none"
/** Whether to show status text */
showStatusText?: boolean; // default: block→true , inline→false
/** Inline icon position */
iconPosition?: "right" | "left"; // default: "right"
// Optional references to existing nodes ( when NOT using wrapper )
flagIcon?: HTMLElement;
flagLabel?: HTMLElement;
dropdown?: HTMLElement;
button?: HTMLElement;
status?: HTMLElement;
};
// UX / i18n
mode?: "AUTO" | string; // fixed country or AUTO ( default )
i18n?: "AUTO" | "IT" | "EN"; // default AUTO → navigator it / en
allowedCountries?: string[]; // whitelist ; default : all
vehicleType?: "any" | "car" | "bike"; // default : any
placeholders?: { auto?: string }; // placeholder for AUTO
// Normalization / formatting
normalize?: (code: string) => string; // default GB→UK
formatters?: Record<string, (s: string) => string>; // per country code
// Timings
timings?: { debounce?: number; clear?: number };
// Dependencies / logging
deps?: { inputmask?: any }; // manual inject ( e.g., window.Inputmask )
autoLoadDeps?: { inputmask?: boolean }; // default : true ( autoload UMD )
cdn?: { inputmask?: string }; // override Inputmask CDN URL
logger?: Logger;
debug?: boolean;
};| Key | Type | Values | Default | Notes |
|---|---|---|---|---|
ui.statusMode |
"block" | "inline" | "off" |
block / inline / off | "block" |
block uses <div class="status"> , inline overlays inside the input , off hides UI |
ui.statusIcon |
"none" | "icon" | "pill" |
none / icon / pill | "none" |
used only when statusMode is inline |
ui.showStatusText |
boolean |
true / false | block→true , inline→false |
short text in inline ; full text in block |
ui.iconPosition |
"right" | "left" |
right / left | "right" |
icon position for inline |
ui.status |
HTMLElement ( optional ) |
— | auto created / derived | existing host for status when you do not use wrapper |
ui.button / dropdown / flagIcon / flagLabel |
HTMLElement ( optional ) |
— | auto created if wrapper |
for re-using external DOM |
type EuroPlateInstance = {
setCountry(code: "AUTO" | string): void; // alias of setMode
setMode(m: "AUTO" | string): void; // set fixed country or AUTO
setAllowed(codes: string[]): void; // dynamic whitelist
setVehicleType(t: "any" | "car" | "bike"): void;
setI18n(code: "AUTO" | "IT" | "EN"): void;
setDebug(on: boolean): void;
validate(): { ok: boolean; country?: string; value: string };
destroy(): void;
getI18n(): "it" | "en";
};<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/assets/css/styles.css" />
<div id="plateBox"></div>
<script type="module">
import * as EuroMod from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.mjs";
// Common options used across examples
const common = {
mode: "AUTO",
i18n: "IT",
allowedCountries: ["IT", "FR", "DE", "ES"],
vehicleType: "any",
autoFocusOnInit: false,
ui: {
statusMode: "inline",
statusIcon: "icon",
showStatusText: true, // | false
iconPosition: "right",
},
// Autoload deps (UMD) from CDN
autoLoadDeps: { inputmask: true, jquery: true, toastr: true },
// Optional : route logs to Toastr if available
useToastrLogger: true,
debug: true,
};
createEuroPlate(EuroMod, {
wrapper: "#plateBox", // optional
...common,
});
</script>Notes :
- With
autoLoadDeps.jquery: trueandautoLoadDeps.toastr: truethe client will fetch UMD builds from CDN when missing .useToastrLogger: truesends SDK logs and validation notices to Toastr if it is present ( either auto-loaded or injected ) .- jQuery is not required by the core validator ; it is only pulled if your page / widgets rely on it .
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/assets/css/styles.css" />
<script src="https://cdn.jsdelivr.net/npm/inputmask@5.0.9/dist/inputmask.min.js"></script>
<script type="module">
import * as EuroMod from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.mjs";
createEuroPlate(EuroMod, {
wrapper: "#plateBox",
i18n: "AUTO",
autoFocusOnInit: false,
deps: { inputmask: window.Inputmask }, // manual inject
});
</script><script type="module">
import * as EuroMod from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.mjs";
createEuroPlate(EuroMod, {
wrapper: "#plateBox",
i18n: "AUTO",
autoFocusOnInit: false,
// No deps object → will autoload Inputmask UMD from jsDelivr
});
</script>createEuroPlate(EuroMod, {
wrapper: "#plateBox",
cdn: { inputmask: "https://unpkg.com/inputmask@5.0.9/dist/inputmask.min.js" },
});createEuroPlate(EuroMod, {
wrapper: "#plateBox",
autoLoadDeps: { inputmask: false },
});<input id="myPlate" />
<script type="module">
import * as EuroMod from ".../index.esm.js";
import { createEuroPlate } from ".../index.mjs";
createEuroPlate(EuroMod, {
input: document.getElementById("myPlate"),
inputName: "plate_number",
preserveInputAttrs: true, // do NOT overwrite existing id/name
i18n: "EN",
mode: "AUTO",
autoFocusOnInit: false,
});
</script>add_action('wp_enqueue_scripts', function () {
wp_register_script('epv-init', '', [], null, true);
wp_enqueue_style(
'epv-styles',
'https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/assets/css/styles.css',
[],
'1.0.16'
);
add_filter('script_loader_tag', function ($tag, $handle) {
if ($handle === 'epv-init') {
return '<script type="module">' .
'import * as EuroMod from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/browser/index.esm.js";' .
'import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.16/dist/client/index.mjs";' .
'window.__epvInit=(id,opts)=>createEuroPlate(EuroMod,Object.assign({wrapper:"#"+id,mode:"AUTO",i18n:"IT",debug:true,autoFocusOnInit:false},opts||{}));' .
'</script>';
}
return $tag;
}, 10, 2);
wp_enqueue_script('epv-init');
});You can enqueue the CSS and auto-inject a window.__epvInit bootstrapper.
Then, create a shortcode to instantiate it dynamically inside Elementor.
Full example available in the Italian README → “WordPress (WP) / Elementor” section.
npx @codecorn/euro-plate-validator "AB 123 CD" --countries IT,FR,DE --type car --prettyOptions:
--countries/-c→ comma-separated list of country codes--type/-t→car,motorcycle, orany--pretty/-p→ human-readable output
Exit codes:
0→ valid1→ invalid2→ bad arguments
The package supports 30 countries, including Moldova (MD).
🇮🇹 IT | 🇬🇧 UK | 🇩🇪 DE | 🇫🇷 FR | 🇪🇸 ES | 🇵🇹 PT | 🇳🇱 NL | 🇧🇪 BE | 🇨🇭 CH | 🇦🇹 AT 🇮🇪 IE | 🇱🇺 LU | 🇩🇰 DK | 🇸🇪 SE | 🇳🇴 NO | 🇫🇮 FI | 🇵🇱 PL | 🇨🇿 CZ | 🇸🇰 SK | 🇭🇺 HU 🇷🇴 RO | 🇧🇬 BG | 🇸🇮 SI | 🇭🇷 HR | 🇬🇷 GR | 🇱🇹 LT | 🇱🇻 LV | 🇪🇪 EE | 🇺🇦 UA | 🇲🇩 MD
| Category | Example |
|---|---|
| Current standard | ABC 123 |
| Four-letter legacy | AB CD 123 |
| Chișinău C | C AB 123 |
| Chișinău K | K AB 123 |
| Diplomatic | CD 123 AB |
| Police / MAI | MAI 1234 |
| Transnistria | T 123 AB |
import { validatePlate, getInputMask } from "@codecorn/euro-plate-validator";
validatePlate("ABC 123", ["MD"], { vehicleType: "car" }).isValid; // true
validatePlate("MAI 1234", ["MD"]).isValid; // true
validatePlate("ABC-123", ["MD"]).isValid; // false
getInputMask("MD"); // seven mask variantsLowercase and compact unspaced forms are supported, but hyphens are not. Validation checks syntax only, not actual registration or legal recognition. AUTO mode may report multiple countries for overlapping patterns. See issue #2.
- Moldova (
MD) support: seven plate formats, metadata, flag and IT/EN country labels. - PHP validator updated alongside the TypeScript core.
- Inputmask now preserves arrays of alternative masks.
- Dedicated positive/negative tests and CI regression coverage.
Upcoming: GitHub release and npm publish after merge. See CHANGELOG.md.
-
Inputmask
- Safe merge of
definitions(no hard override of defaults). - New
Ltoken (letter) accepts lowercase and forces UPPERCASE (casing: "upper"). - Avoid redefining
A/9; useH(IT) /C(ES) only when needed. applyMaskNowapplied immediately on country change (no debounce race).- Finalized placeholders via
finalizeInputMaskLayouts+scripts/test-placeholders.mjs.
- Safe merge of
-
Client SDK
- Centralized logging (
imLog,imPreLog,imMounted,imError) usingBADGE/LOGwhendebug: true. - Lowercase input supported across layouts with
Ltoken. - Examples updated with
autoLoadDeps: { inputmask: true, jquery: true, toastr: true }anduseToastrLogger: true.
- Centralized logging (
-
Docs
- CDN bumped to 1.0.15.
- Notes about lowercase acceptance and uppercase coercion.
- Guidance to not override
A/9and define only custom tokens for restricted alphabets.
-
Status inline configurabile
statusMode: "inline" | "block" | "off"statusIcon: "none" | "icon" | "pill"showStatusText: booleaniconPosition: "left" | "right"
-
Automatic layout space reservation via
:has([data-state]) -
No status shown for empty or partial input
-
Fixed country switching (IT↔FR) and Inputmask definitions merging (
A/H/9) -
Refactored status rendering with
setValidityUIas the single writer -
Added
clearStatusUI()for a neutral idle state
- NEW →
autoFocusOnInit(default:false) — prevents autofocus on init. - NEW →
preserveInputAttrs— keeps external inputid/nameintact. - UX → renamed CSS classes from
.iti__*→.epv__*. - Dependencies → auto-loads
InputmaskUMD via CDN; supports manual injection and CDN override. - I18n → IT / EN / AUTO (auto-detect from browser locale).
- UX stability → dynamic placeholder, country-based formatter, debounce system.
- CSS/Assets consolidation.
- Added Client SDK + autoload fallback.
- Updated README, docs, and npm package.
Federico Girolami Full-Stack Developer • System Integrator • Digital Solution Architect 🚀 🌐 codecorn.it 📧 f.girolami@codecorn.it 🐙 github.com/fgirolami29
Pull requests are welcome. For major changes, please open an issue first.
Powered by CodeCorn™ 🚀