🚗 European license plate validator (Russia excluded). Multi-country, regex-based validation for EU/EEA license plates. Works in Node.js, TypeScript, and the browser (with a lightweight UI client).
- ✅ Multi-country support (25+ EU/EEA)
- 🚫 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
Inputmask
integration ↕️ 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-validator
Use versioned URLs to avoid stale CDN caches.
-
ESM (browser)
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/dist/browser/index.esm.js
-
IIFE (global
window.EuroPlateValidator
)https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/dist/browser/index.iife.min.js
-
ESM
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/dist/client/index.mjs
-
CJS (Node)
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/dist/client/europlate.client.cjs
https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/dist/assets/css/styles.css
- (compat alias)
…/assets/css/styles.css
import { validatePlate } from "@codecorn/euro-plate-validator";
// Italy (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 browser client auto-generates a full UI (flag + input + dropdown + status
) or attaches to an existing <input>
. ecco la versione EN tradotta 1 : 1 . Ho lasciato il codice invariato , e ho applicato lo spazio prima / dopo la punteggiatura solo al testo in prosa .
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.14/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.14/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/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: true
andautoLoadDeps.toastr: true
the client will fetch UMD builds from CDN when missing .useToastrLogger: true
sends 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.14/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.14/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/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.14/dist/browser/index.esm.js";
import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/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.14/dist/assets/css/styles.css',
[],
'1.0.14'
);
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.14/dist/browser/index.esm.js";' .
'import { createEuroPlate } from "https://cdn.jsdelivr.net/npm/@codecorn/euro-plate-validator@1.0.14/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 --pretty
Options:
--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
🇮🇹 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
-
Inputmask
- Safe merge of
definitions
(no hard override of defaults). - New
L
token (letter) accepts lowercase and forces UPPERCASE (casing: "upper"
). - Avoid redefining
A
/9
; useH
(IT) /C
(ES) only when needed. applyMaskNow
applied 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/LOG
whendebug: true
. - Lowercase input supported across layouts with
L
token. - Examples updated with
autoLoadDeps: { inputmask: true, jquery: true, toastr: true }
anduseToastrLogger: true
.
- Centralized logging (
-
Docs
- CDN bumped to 1.0.14.
- Notes about lowercase acceptance and uppercase coercion.
- Guidance to not override
A
/9
and define only custom tokens for restricted alphabets.
-
Status inline configurabile
statusMode: "inline" | "block" | "off"
statusIcon: "none" | "icon" | "pill"
showStatusText: boolean
iconPosition: "left" | "right"
-
Riserva spazio automatica via
:has([data-state])
-
Nessuno stato su campo vuoto / parziale
-
Fix cambio paese (IT↔FR) e Inputmask merge (
A/H/9
) -
Refactor UI status (
setValidityUI
unico writer) -
Add
clearStatusUI()
per idle neutro
- NEW →
autoFocusOnInit
(default:false
) — prevents autofocus on init. - NEW →
preserveInputAttrs
— keeps external inputid
/name
intact. - UX → renamed CSS classes from
.iti__*
→.epv__*
. - Dependencies → auto-loads
Inputmask
UMD 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™ 🚀