License plate
Brazilian vehicle license plates, in the Mercosul (LLLNLNN) and pre-Mercosul (LLLNNNN) patterns.
Validate
- JavaScript library
- Python library10 cases fail
- Go library5 cases fail
- Ruby library9 cases fail
- Rust library7 cases fail
- .NET library5 cases fail
- Erlang library17 cases fail
Validates a license plate in the old format (ABC1234) or the Mercosul format (ABC1D23), in any case, with or without a hyphen or space.
options.formatrestricts the check to one format:"LLLNNNN"(old,ABC1234) or"LLLNLNN"(Mercosul,ABC1D23), the codeslicensePlate.getFormatreturns andlicensePlate.generatetakes. Without it, or with any other value, both formats are accepted. Until 2.4.0 the option did not exist and both formats were always accepted.- Any other sequence of letters and digits, or extra characters, makes it invalid.
- The mask (whitespace,
.,-or/, alone or in a run) is accepted only between the third character and the last four, whitespace around the plate is ignored, and the check is case-insensitive. - A mask character anywhere else, or any other character (
A@BC1234, an emoji), makes the plate invalid instead of being stripped. Until 2.4.0 such characters were stripped (A@B#C1$2%3^4wastrue). licensePlate.getFormatreturnsnullexactly when this returnsfalse.- The withdrawn motorcycle sequence
LLLNNLN(ABC12D3) is invalid.
| Parameter | Type | Required |
|---|---|---|
value | string | yes |
options | IsValidLicensePlateOptions | no |
options.format | LicensePlateFormat | no |
| returns | boolean |
Check if a license plate is valid. Accepts the old Brazilian format (ABC-1234) and the Mercosul format (ABC1D23), with or without a mask, in any case. The mask (whitespace, ., - or /, alone or in a run) is accepted only between the third character and the last four; any other character (@, an emoji, a separator anywhere else) makes the plate invalid instead of being stripped.
The optional format of the second argument (IsValidLicensePlateOptions) restricts the check to one of them: "LLLNNNN" for the old format or "LLLNLNN" for the Mercosul one, the names getFormatLicensePlate returns. It works like the type argument of the Python library's is_valid, whose values are named "old_format" and "mercosul" there. Those names are not formats here: without format, or with any other value, a plate in either format is valid.
import { isValidLicensePlate } from '@brazilian-utils/brazilian-utils';
isValidLicensePlate('ABC1234'); // true (Brazilian format)
isValidLicensePlate('ABC-1234'); // true (Brazilian format with hyphen)
isValidLicensePlate('ABC 1234'); // true (whitespace mask)
isValidLicensePlate('ABC1D23'); // true (Mercosul format)
isValidLicensePlate('ABC12D3'); // false (not a Mercosul sequence)
isValidLicensePlate('ABC1234EXTRA'); // false (too many characters)
isValidLicensePlate('A-BC1234'); // false (the mask sits after the third character only)
isValidLicensePlate('ABC1234!'); // false (any other character is rejected)
isValidLicensePlate('ABC1D23', { format: 'LLLNLNN' }); // true
isValidLicensePlate('ABC1234', { format: 'LLLNLNN' }); // false (an old format plate)
isValidLicensePlate('ABC-1234', { format: 'LLLNNNN' }); // trueSource: Resolução CONTRAN nº 969/2022, Anexos.
Code: brazilian-utils/javascriptTry it with JavaScript isValidLicensePlate
Shared test cases (48) and the result in each library licensePlate.isValid
Format
- JavaScript library
- Python library17 cases fail
- Go library8 cases fail
- Ruby library15 cases fail
- Rust library17 cases fail
- .NET library17 cases fail
- Erlang library17 cases fail
Formats a license plate in upper case. Old-format plates (LLLNNNN) get a hyphen (ABC-1234). Mercosul plates (LLLNLNN) have no separator.
- A partial value is formatted as far as it goes, so the function works as an input mask: the hyphen shows up as soon as the fourth character is a digit (
abc1givesABC-1), and a fifth character that is a letter keeps the Mercosul form (abc1dgivesABC1D). - Any character that is not an ASCII letter or digit is dropped first, and only the first 7 remain. Unlike
licensePlate.isValid, the mask may sit anywhere. - Returns an empty string when the value cannot start a valid plate (
1234567,abc12d3,abc1da2). - A value that is not a string returns an empty string.
| Parameter | Type | Required |
|---|---|---|
value | string | yes |
| returns | string |
Format a license plate. Old Brazilian plates (LLLNNNN) get a hyphen; Mercosul plates (LLLNLNN) are returned without a separator.
- Returns
''when the value cannot start a valid plate. - A partial value is formatted progressively, so the function works as an input mask: the hyphen shows up as soon as the fourth character is a digit, and a fifth character that is a letter keeps the Mercosul form.
import { formatLicensePlate } from '@brazilian-utils/brazilian-utils';
formatLicensePlate('abc1234'); // 'ABC-1234'
formatLicensePlate('abc1d23'); // 'ABC1D23'
formatLicensePlate('abc1'); // 'ABC-1' (a partial value is formatted as far as it goes)
formatLicensePlate('abc1d'); // 'ABC1D'Try it with JavaScript formatLicensePlate
Shared test cases (28) and the result in each library licensePlate.format
Parse
Removes every character that is not an ASCII letter or digit from a license plate, upper-cases it and caps it at 7 characters (abc-1234 gives ABC1234).
- It does not validate: use
licensePlate.isValidfor that. The mask may sit anywhere, unlike inlicensePlate.isValid. - A value that is not a string returns an empty string.
| Parameter | Type | Required |
|---|---|---|
value | string | yes |
| returns | string |
Remove separators from a license plate, normalize it to uppercase, and cap it to 7 characters.
import { parseLicensePlate } from '@brazilian-utils/brazilian-utils';
parseLicensePlate('abc-1234'); // 'ABC1234'Try it with JavaScript parseLicensePlate
Shared test cases (11) and the result in each library licensePlate.parse
Generate
- JavaScript library
- Python library4 cases fail
- Go library3 cases fail
- Ruby library4 cases fail
- Rust library2 cases fail
- .NET library3 cases fail
- Erlang library4 cases fail
Generates a valid random license plate, unformatted.
formatisLLLNLNN(Mercosul, the default) orLLLNNNN(old format). Any other value falls back to the default.- The fifth character of a Mercosul plate is drawn from
KtoZ.AtoJis used only to convert an old-format plate (Resolução CONTRAN nº 969/2022, Anexo II, item 2), so a new plate never carries it. Until 2.4.0 it could be any letter. - A format outside the two literals falls back to the default, so the result is always a plate
licensePlate.isValidaccepts. Version 2.3.0 used an unknown string verbatim. - It uses a non-cryptographic random source.
| Parameter | Type | Required |
|---|---|---|
format | GenerateLicensePlateFormat | no |
| returns | string |
Generate a valid random license plate in the chosen format.
format(GenerateLicensePlateFormat, an alias ofLicensePlateFormat):'LLLNLNN'(Mercosul, the default) or'LLLNNNN'(the old Brazilian format). Any other value falls back to the default.- The letter in the fifth position of a Mercosul plate is drawn from
KtoZ:AtoJis used only to convert an old format plate (Anexo II, item 2, of Resolução CONTRAN nº 969/2022), so a new plate never carries it.
import { generateLicensePlate } from '@brazilian-utils/brazilian-utils';
generateLicensePlate(); // 'ABC1K23' (Mercosul, the default)
generateLicensePlate('LLLNNNN'); // 'ABC1234'
generateLicensePlate('LLLNNLN'); // 'ABC1K23' (a format outside the two in circulation falls back to the default)Source: Resolução CONTRAN nº 969/2022.
Code: brazilian-utils/javascriptTry it with JavaScript generateLicensePlate
Shared test cases (6) and the result in each library licensePlate.generate
Convert to mercosul
- JavaScript library
- Python library8 cases fail
- Go library
- Ruby library
- Rust library
- .NET library
- Erlang library8 cases fail
Converts an old-format plate (LLLNNNN) to the Mercosul format (LLLNLNN). The 5th character, a digit, becomes a letter, with 0 to 9 mapping to A to J (ABC1234 becomes ABC1C34). The result is upper-cased and has no mask.
The conversion follows the table of Anexo II of Resolução CONTRAN nº 969/2022. Every other character stays as it is.
- The plate is read like
licensePlate.isValidreads it: any case, the mask (ABC-1234,ABC 1234) accepted, whitespace around it ignored, and a mask character or any other character in another position makes it invalid. Until 2.4.0 such characters were stripped, soA-BC1234was converted. - It returns an empty string, not
null, when the value is not a valid old-format plate: a plate that is already Mercosul (ABC1D23), the withdrawnLLLNNLNsequence, a wrong length or an invalid value.
Pending decision
The reference (JS) also accepts the hyphen mask (ABC-1234). Other libraries reject it. See the open decision in docs/findings.md.
Pending decision
For a plate that is already Mercosul or is not a valid old-format plate, the reference (JS) returns an empty string. Most libraries return null. See the open decision in docs/findings.md.
| Parameter | Type | Required |
|---|---|---|
value | string | yes |
| returns | string |
Convert an old format Brazilian license plate (LLLNNNN) to the Mercosul format (LLLNLNN). The 5th digit becomes a letter, 0 through 9 mapping to A through J.
- Returns
""when the value is not a valid old format license plate.
import { convertLicensePlateToMercosul } from '@brazilian-utils/brazilian-utils';
convertLicensePlateToMercosul('ABC1234'); // 'ABC1C34'
convertLicensePlateToMercosul('abc-1234'); // 'ABC1C34'
convertLicensePlateToMercosul('ABC1D23'); // '' (already Mercosul)Source: Resolução CONTRAN nº 969/2022, Anexo II.
Code: brazilian-utils/javascriptTry it with JavaScript convertLicensePlateToMercosul
Shared test cases (20) and the result in each library licensePlate.convertToMercosul
Get format
- JavaScript library
- Python library
- Go library3 cases fail
- Ruby library
- Rust library
- .NET library
- Erlang library11 cases fail
Detects the format of a license plate: LLLNNNN for the old format, LLLNLNN for Mercosul.
- The plate is read like
licensePlate.isValidreads it: any case, whitespace around it ignored, and the mask (whitespace,.,-or/) accepted only between the third character and the last four. A mask character anywhere else, or any other character, makes the value invalid. Until 2.4.0 such characters were stripped, soA-BC1234gaveLLLNNNN. - Returns
nullwhen the value is not 7 letters and digits in one of the two formats, including the withdrawnLLLNNLNsequence. - It returns
nullexactly whenlicensePlate.isValidreturnsfalse.
Pending decision
Erlang returns named formats instead of the patterns. See the open decision in docs/findings.md.
| Parameter | Type | Required |
|---|---|---|
value | string | yes |
| returns | LicensePlateFormat | null |
Detect the normalized format of a license plate: 'LLLNNNN' for the old Brazilian format, 'LLLNLNN' for Mercosul.
- Returns
nullwhen the value, separators removed, is not 7 letters and digits in one of the two formats. - Exports the
LicensePlateFormattype, whichgenerateLicensePlatere-exports asGenerateLicensePlateFormat.
import { getFormatLicensePlate } from '@brazilian-utils/brazilian-utils';
getFormatLicensePlate('ABC-1234'); // 'LLLNNNN'
getFormatLicensePlate('ABC1D23'); // 'LLLNLNN'
getFormatLicensePlate('ABC12D3'); // null (not a Mercosul sequence)
getFormatLicensePlate('INVALID'); // null
getFormatLicensePlate('ABC1234EXTRA'); // null (too many characters)Try it with JavaScript getFormatLicensePlate
Shared test cases (14) and the result in each library licensePlate.getFormat
Specification
Summary
Vehicle license plates are the front and rear plates fixed to a vehicle. A plate has 7 letters and digits.
Validation rules
Mercosul standard
- The plate has 7 letters and digits in the
LLLNLNNpattern.
Pre-Mercosul standard
- The plate has 7 letters and digits in the
LLLNNNNpattern, in two groups:- The first group is 3 letters (
AtoZ). - The second group is 4 digits.
- The first group is 3 letters (
Algorithm
- Remove the whitespace at the start and at the end, and the mask characters (whitespace,
.,-or/, alone or in a run) between the third character and the last four. A mask character anywhere else, or any other character, makes the plate invalid. - Check that 7 characters remain.
- Check that all characters are alphanumeric.
- Check that the input follows one of the valid patterns:
- Mercosul:
LLLNLNN - Pre-Mercosul:
LLLNNNN
- Mercosul:
- If the input follows neither pattern, the license plate is invalid.
Regex
- Raw input (the mask sits between the third character and the last four):
^\s*[A-Za-z]{3}[\s.\-/]*[0-9A-Za-z]{4}\s*$ - Characters only (pre-Mercosul or Mercosul pattern):
^(?:[A-Z]{3}[0-9]{4}|[A-Z]{3}[0-9][A-Z][0-9]{2})$
Examples
- Valid:
ABC1234(pre-Mercosul standard) - Valid:
ABC1D23(Mercosul standard) - Invalid:
AB12345(does not follow any valid format) - Invalid:
ABCD123(incorrect number of letters) - Invalid:
ABC123(fewer than 7 characters) - Invalid:
ABC12D4(incorrect character order)
Official sources
- Resolução CONTRAN nº 969, de 20 de junho de 2022, Anexo I: Especificações do Sistema de Placas de Identificação Veicular (PIV)
- RESOLUÇÃO Nº 780, DE 26 DE JUNHO DE 2019
- RESOLUÇÃO 231 DE 15 DE MARÇO DE 2007
- Resolução CONTRAN nº 969, de 20 de junho de 2022
See also RENAVAM
Last updated on
