@cutticat/i18n
Locale, region, and language utilities for CuttiCat projects — built on the built-in Intl API.
Not a translation framework. This package provides constants, normalization, locale parsing/resolution, conversions, display names, and formatting. Message loading, routing, and framework integration stay in your app.
Table of Contents
Overview
@cutticat/i18n centralizes low-level i18n helpers used across CuttiCat apps:
- ISO code constants (
countryCodes,languageCodes,currencyCodes) - Normalization with bundled whitelists (
normalizeCountryCode,normalizeLanguageCode,normalizeCurrencyCode,normalizeLocaleTag) - Locale parsing and closest-match resolution (
parseLocaleTag,getClosestLocale) - Language ↔ region conversions and flag emoji (
Intl.Locale#maximize,@cutticat/country-code-emoji) - Localized display names (
Intl.DisplayNames) - Currency formatting (
formatCurrencyWithSymbol)
API contract
| Role | Functions | On failure |
|---|---|---|
| Normalization | normalizeCountryCode, normalizeLanguageCode, normalizeCurrencyCode, normalizeLocaleTag |
undefined |
| Locale parsing / resolution | parseLocaleTag, getClosestLocale |
undefined |
| Conversion / flags | languageToLikelyRegionCode, countryCodeToMainLanguageCode, *FlagEmoji |
undefined |
| Display / formatting | get*DisplayName, formatCurrencyWithSymbol |
undefined |
| Predicate | isLocaleInList |
false |
Callers provide UI fallbacks explicitly, for example:
getRegionDisplayName("DE", { locale: "en" }) ?? "DE"
languageToFlagEmoji("en") ?? "🌐"
getClosestLocale(header, ["en", "ru"]) ?? "en"
normalizeCountryCode(input) ?? input
Installation
pnpm i @cutticat/i18n
Quick Start
import {
countryCodeToMainLanguageCode,
formatCurrencyWithSymbol,
getClosestLocale,
getLanguageDisplayName,
getRegionDisplayName,
languageToFlagEmoji,
languageToLikelyRegionCode,
normalizeCountryCode,
normalizeLocaleTag,
parseLocaleTag,
} from "@cutticat/i18n"
normalizeCountryCode("us") // "US"
normalizeLocaleTag("EN-us") // "en-US"
parseLocaleTag("en-US") // { language: "en", region: "US" }
languageToLikelyRegionCode("en-US") // "US"
countryCodeToMainLanguageCode("RU") // "ru"
getLanguageDisplayName("ru", { locale: "en" }) // "Russian"
getRegionDisplayName("DE", { locale: "en" }) // "Germany"
formatCurrencyWithSymbol("USD", { locale: "en" }) // "$ (US Dollar)"
languageToFlagEmoji("en-US") // "🇺🇸"
getClosestLocale("EN", ["en", "ru"]) // "en"
API
Constants
countryCodes,countryCodeSet— ISO 3166-1 alpha-2languageCodes,languageCodeSet— ISO 639-1currencyCodes,currencyCodeSet— ISO 4217 (ICUsupportedValuesOf('currency'))
Normalization
Validates format and membership in bundled lists. Pass a custom codes array/set for app whitelists, or false to skip membership and normalize by format only.
normalizeCountryCode(code, codes?)— uppercase alpha-2normalizeLanguageCode(code, codes?)— lowercase alpha-2normalizeCurrencyCode(code, codes?)— uppercase alpha-3normalizeLocaleTag(tag)— canonical BCP 47 viaIntl.Locale
Locales
parseLocaleTag(tag)—{ language, region?, script? }orundefinedParsedLocaleTag— parsed fields typeisLocaleInList(value, supportedLocales)— exact-match type guardgetClosestLocale(locale, supportedLocales)— canonical tag, then language subtag, then region (exact match)
Conversions and flags
Thin Intl.Locale#maximize wrappers and @cutticat/country-code-emoji re-exports:
languageToLikelyRegionCode(language)— e.g.en→US,pt-BR→BRcountryCodeToMainLanguageCode(code)— e.g.DE→deregionCodeToFlagEmoji(code)— ISO alpha-2 → flag emojiflagEmojiToCountryCode(emoji)— reverse lookup (re-export)languageToFlagEmoji(language)— flag for the likely region of a language tag
ICU caveat: inferred regions/flags for language-only tags (
en,ru) reflect ICU defaults. Use explicit regional tags (en-GB,pt-BR) when the UI must target a specific country.
Display names
Thin Intl.DisplayNames wrappers — no bundled-list validation; behavior follows ICU.
defaultGetDisplayNameOptions—{ locale: "en", style: "long" }GetDisplayNameOptions—{ locale?, style? }getLanguageDisplayName(language, options?)getRegionDisplayName(code, options?)getScriptDisplayName(script, options?)getCurrencyDisplayName(code, options?)
Formatting
formatCurrencyWithSymbol(code, options?)— e.g.$ (US Dollar)(normalizeCurrencyCode+getCurrencyDisplayName+ symbol map)
Documentation
- STRUCTURE.md — project layout
- SCRIPTS.md — npm scripts
- STYLE_GUIDE.md — code style
- CONTRIBUTING.md — how to contribute
Requirements
- Node.js >= 22
- Modern ICU/
Intlsupport (language/region/currency/script display names)