pk-lang-codes
v0.2.0
Published
Unified language & locale code toolkit. ISO 639-1/2, BCP 47, scripts, regions. TypeScript-first, tree-shakeable, zero dependencies.
Maintainers
Readme
pk-lang-codes
Tiny, tree-shakeable language & locale toolkit for JavaScript/TypeScript.
ISO 639-1/2, BCP 47, scripts (with text direction), regions, and territory-language mapping for 257 countries — all in 68 KB on npm. Zero dependencies.
Install
npm install pk-lang-codesWhy?
Your server gets a locale code from a phone — zh-Hant-TW, ar-SA, hi-IN — and you need to:
- Pick the right translation —
zh-TW→ Traditional Chinese, not Simplified - Know the language, script, region, and text direction
- Build a language selector with native names (العربية, 中文, हिन्दी)
- Find what languages a country speaks
- Detect RTL for layout
One import. One function. Done.
import { resolveLocale } from 'pk-lang-codes';
const locale = resolveLocale('ar-SA');
// {
// language: { code: 'ar', name: 'Arabic', nativeName: 'العربية' },
// direction: 'rtl', ← auto-detected even without script tag
// region: { code: 'SA', name: 'Saudi Arabia' },
// displayName: 'Arabic (Saudi Arabia)',
// }Smart Translation Matching
The killer feature — match a device locale to the best translation from your supported list:
import { matchTranslation } from 'pk-lang-codes';
const supported = ['en', 'zh-Hans', 'zh-Hant', 'es', 'pt', 'pt-BR', 'ar', 'ja'];
matchTranslation('zh-Hant-TW', supported); // 'zh-Hant' → Traditional, not Simplified
matchTranslation('zh-Hans-CN', supported); // 'zh-Hans' → Simplified
matchTranslation('zh-TW', supported); // 'zh-Hant' → infers script from region!
matchTranslation('zh-CN', supported); // 'zh-Hans' → infers script from region!
matchTranslation('pt-BR', supported); // 'pt-BR' → exact regional match
matchTranslation('pt-PT', supported); // 'pt' → falls back to base Portuguese
matchTranslation('en-AU', supported); // 'en' → base English
matchTranslation('ar-EG', supported); // 'ar' → Arabic
matchTranslation('sw-KE', supported); // 'en' → Kenya's primary lang (English)No more generic zh when your user needs Traditional Chinese. No more guessing.
Features
Resolve any device locale — one call
import { resolveLocale } from 'pk-lang-codes';
resolveLocale('zh-Hant-TW');
// language: Chinese (中文) | script: Han Traditional | region: Taiwan | direction: ltr
resolveLocale('sr-Latn-RS');
// language: Serbian (српски) | script: Latin | region: Serbia | direction: ltrWhat languages does a country need?
257 countries, every spoken language, with population % and official status.
import { getLanguagesForRegion, getOfficialLanguages, getPrimaryLanguage } from 'pk-lang-codes';
// China — 28 languages
getLanguagesForRegion('CN');
// [
// { languageCode: 'zh', populationPercent: 90, officialStatus: 'official' },
// { languageCode: 'wuu', populationPercent: 6, officialStatus: 'none' }, // Wu
// { languageCode: 'yue', populationPercent: 5.2, officialStatus: 'none' }, // Cantonese
// { languageCode: 'ug', populationPercent: 0.55, officialStatus: 'official_regional' }, // Uyghur
// ...24 more
// ]
// India — 80+ languages, 21 official
getOfficialLanguages('IN');
// hi (official), en (official), bn (official_regional), te, mr, ta, ur, gu, kn, ml...
// Quick lookup
getPrimaryLanguage('JP'); // 'ja'
getPrimaryLanguage('BR'); // 'pt'
getPrimaryLanguage('SA'); // 'ar'RTL detection — works without script tag
Most real-world locale codes don't include the script (ar-SA not ar-Arab-SA). resolveLocale handles this automatically:
resolveLocale('ar-SA').direction; // 'rtl' ✓
resolveLocale('he-IL').direction; // 'rtl' ✓
resolveLocale('fa-IR').direction; // 'rtl' ✓
resolveLocale('ur-PK').direction; // 'rtl' ✓
resolveLocale('en-US').direction; // 'ltr' ✓
resolveLocale('hi-IN').direction; // 'ltr' ✓<html dir={resolveLocale(deviceLocale).direction}>Language selector with native names
import { getLanguage, getAllLanguages, searchLanguages } from 'pk-lang-codes';
getLanguage('fr'); // { name: 'French', nativeName: 'français', ... }
getLanguage('ja'); // { name: 'Japanese', nativeName: '日本語', ... }
getLanguage('ar'); // { name: 'Arabic', nativeName: 'العربية', ... }
// Search in any language
searchLanguages('Deutsch'); // → German
searchLanguages('中文'); // → Chinese
searchLanguages('french'); // → FrenchBCP 47 parsing & validation
import { parseBcp47, formatBcp47, isValidBcp47 } from 'pk-lang-codes/bcp47';
parseBcp47('zh-Hant-TW');
// { language: 'zh', script: 'Hant', region: 'TW', variants: [], privateUse: null }
formatBcp47({ language: 'sr', script: 'Latn', region: 'RS' }); // 'sr-Latn-RS'
isValidBcp47('en-US'); // true
isValidBcp47('not valid!'); // falseScript direction lookup
import { getScript } from 'pk-lang-codes/scripts';
getScript('Arab'); // { name: 'Arabic', direction: 'rtl' }
getScript('Hebr'); // { name: 'Hebrew', direction: 'rtl' }
getScript('Latn'); // { name: 'Latin', direction: 'ltr' }
getScript('Deva'); // { name: 'Devanagari', direction: 'ltr' }Type-safe — full autocomplete
import type { Iso639_1Code, ScriptCode, RegionCode } from 'pk-lang-codes';
const lang: Iso639_1Code = 'en'; // autocomplete for 184 codes
const script: ScriptCode = 'Latn'; // autocomplete for 226 codes
const region: RegionCode = 'US'; // autocomplete for 249 codes
// Type guards narrow string → literal union
import { isIso639_1Code, getLanguage } from 'pk-lang-codes';
const input: string = req.query.lang;
if (isIso639_1Code(input)) {
const lang = getLanguage(input); // fully typed, no cast
}Tree-Shaking
Import only the module you need — unused data stays out of your bundle:
import { resolveLocale } from 'pk-lang-codes/locale'; // Locale resolution
import { getLanguage } from 'pk-lang-codes/iso639-1'; // 184 languages
import { getLanguageByAlpha3 } from 'pk-lang-codes/iso639-2'; // 485 languages
import { parseBcp47 } from 'pk-lang-codes/bcp47'; // BCP 47 only
import { getScript } from 'pk-lang-codes/scripts'; // 226 scripts
import { getRegion } from 'pk-lang-codes/regions'; // 255 regions| Entry point | What you get | Size (min) |
|---|---|---|
| pk-lang-codes/bcp47 | Parse, format, validate BCP 47 tags | ~1 KB |
| pk-lang-codes/iso639-1 | 184 languages with native names | ~8 KB |
| pk-lang-codes/iso639-2 | 485 three-letter language codes | ~10 KB |
| pk-lang-codes/scripts | 226 scripts with LTR/RTL direction | ~8 KB |
| pk-lang-codes/regions | 255 countries with alpha-2/3/numeric | ~7 KB |
| pk-lang-codes/locale | Full resolver + territory mapping | ~25 KB |
| pk-lang-codes | Everything | ~60 KB |
Data Coverage
| Standard | Entries | Key Data | |---|---|---| | ISO 639-1 | 184 languages | English + native names (العربية, 中文, हिन्दी...) | | ISO 639-2 | 485 languages | Bibliographic + terminological codes | | ISO 15924 | 226 scripts | Text direction (LTR/RTL) | | ISO 3166-1 | 255 regions | Alpha-2, alpha-3, numeric | | CLDR | 257 territories | Spoken languages, population %, official status | | BCP 47 | — | Parse, format, validate |
Data Sources
All data is fetched from authoritative upstream sources:
- Languages: ISO 639 dataset
- Scripts: Unicode Consortium ISO 15924
- Regions: Unicode CLDR Code Mappings (Unicode License v3)
- Territory languages: Unicode CLDR
Data is regenerated weekly via CI. Regenerate manually with npm generate.
Package Size
| Metric | Size | |---|---| | npm tarball | 68 KB | | Unpacked (ESM) | 112 KB | | Dependencies | 0 |
Development
npm install # Install
npm generate # Fetch + regenerate data
npm build # Build ESM + CJS + DTS
npm test # Run 73 tests
npm typecheck # Type check