ISO 639-1 / BCP-47 Language Code Regex
Validates an ISO 639-1 two-letter language code, optionally followed by a BCP-47 style region subtag such as en-US or zh-CN.
Regex Pattern
^[a-z]{2}(-[A-Z]{2})?$Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| ^ | Anchors the match to the start of the string |
| [a-z]{2} | Matches exactly two lowercase letters, the ISO 639-1 language subtag |
| (-[A-Z]{2})? | Optionally matches a hyphen followed by a two-letter uppercase region subtag |
| - | Literal hyphen separating the language and region subtags |
| [A-Z]{2} | Matches exactly two uppercase letters, the ISO 3166-1 region subtag |
| $ | Anchors the match to the end of the string |
Detailed Explanation
What it does
This pattern validates a bare ISO 639-1 language code such as en or fr, or an extended BCP-47 locale tag that adds a region, such as en-US or pt-BR. The language subtag must be lowercase and the region subtag, when present, must be uppercase, matching common convention.
Why it works
The mandatory [a-z]{2} at the start enforces the two-letter lowercase language code required by every valid tag. The entire region portion is wrapped in an optional group (-[A-Z]{2})?, so the pattern matches whether or not a region is supplied, while still requiring that when a hyphen is present it is immediately followed by exactly two uppercase letters.
Common use cases
- Validating an Accept-Language or locale field in application settings
- Parsing i18n resource file names that encode a locale, like messages.en-US.json
- Filtering configuration values before passing them to an Intl or i18n library
- Normalizing user-selected language preferences in a form
Edge cases
- A bare language code like en or fr matches without needing a region subtag
- A full locale tag like en-US or zh-CN matches with the optional region group present
- Lowercase region codes like en-us are rejected since the region subtag must be uppercase per this pattern
- Three-letter language codes like eng (ISO 639-2) are rejected since only the two-letter ISO 639-1 form is accepted
Limitations
- Does not validate against the real list of assigned ISO 639-1 codes or ISO 3166-1 region codes, only the structural shape
- Does not support full BCP-47 complexity such as script subtags (zh-Hans-CN) or variant subtags
- Assumes a specific casing convention; some systems use lowercase region codes like en-us instead
Interactive Tester
Edit the pattern or text below — matching runs live in your browser.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const langCodeRegex = /^[a-z]{2}(-[A-Z]{2})?$/;
console.log(langCodeRegex.test('en-US')); // trueCommon Mistakes
Making the region subtag case-insensitive by adding the i flag, which then also accepts an incorrectly lowercased language subtag or an uppercase one
Fix: Keep the pattern case-sensitive and normalize casing explicitly with toLowerCase/toUpperCase before validating
Using an underscore separator like en_US instead of a hyphen, which is the POSIX locale convention rather than BCP-47
Fix: Match on a literal hyphen - as required by BCP-47, and convert POSIX-style locale strings before validating
Assuming this pattern supports full BCP-47 tags with script or variant subtags like zh-Hans-CN
Fix: Extend the pattern with additional optional groups, or use a dedicated BCP-47 parsing library for full compliance
Performance Notes
- The pattern is short, fixed-length, and anchored, so matching is effectively constant time
- The optional group (-[A-Z]{2})? does not introduce backtracking risk since its contents are fixed-length
- Precompiling the regex once and reusing it avoids repeated compilation overhead in hot validation paths
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |
Work with this pattern
Take this expression further — test it against your own data, adapt it in the builder, or read up on the syntax behind it.
Regex Tester
Test a regular expression against sample text and see every match highlighted live.
Regex Generator
Build a pattern by clicking blocks — no syntax memorisation required.
Regular Expressions Guide
What regex is, how the syntax works, and the mistakes that trip people up.
Regex Cheat Sheet
Every token, quantifier, and assertion on one printable page.
Python Regex Guide
The re module end to end — search, match, findall, groups, and substitution.
Regex Pattern Library
100+ ready-to-use patterns for email, URLs, dates, IPs, and more.