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 | — |