License plate
Brazilian vehicle license plates, in the Mercosul (LLLNLNN) and pre-Mercosul (LLLNNNN) patterns.
Validate
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.
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.
Shared test cases (48) and the result in each library licensePlate.isValid
Format
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.
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'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.
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'Shared test cases (11) and the result in each library licensePlate.parse
Generate
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.
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.
Shared test cases (6) and the result in each library licensePlate.generate
Convert to mercosul
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.
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.
Shared test cases (20) and the result in each library licensePlate.convertToMercosul
Get format
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.
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)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
