codinglombok / lomboklocale
BCP 47 negotiation, CLDR 47 plural rules, number/currency/date formatting and an ICU MessageFormat subset with identical results in PHP, Rust, TypeScript, Python and Go. No dependencies.
Package info
github.com/codinglombok/LombokLocale
Language:Rust
pkg:composer/codinglombok/lomboklocale
Requires
- php: >=8.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
BCP 47 tags and negotiation, CLDR 47 plural rules, number/percent/currency and date formatting, an ICU MessageFormat subset, and JSON message catalogs. The same input gives the same text in Rust, TypeScript, Python, Go and PHP. No runtime dependencies.
Part of the Lombok Ecosystem.
Mengapa library ini? (Why this library?)
- The same text everywhere.
Intlin browsers, ICU in PHP, Babel in Python and hand-written code in Go each ship a different CLDR version and different defaults, so an invoice total or a "3 files" message can differ between your frontend and backend. LombokLocale pins CLDR 47 and runs 342 shared cases (SPEC) in CI for every port. - Checked against ICU. Number, currency, percent, date and plural results are cross-checked against ICU 77.1; the four known differences are listed in the SPEC.
- Exact decimals. Numbers are taken as decimal text, so
0.1 + 0.2,1e21or a 30-digit amount are rounded exactly (half away from zero), never through binary floating point. - Small and embeddable. No dependencies; the Rust crate is
no_std+alloc; data for 28 locales and plural rules for all CLDR locales are embedded.
Installation
| Language | Package | Status |
|---|---|---|
| Rust | lomboklocale (crates.io) |
not yet published |
| TypeScript / JavaScript | lomboklocale (npm) |
not yet published |
| Python | lomboklocale (PyPI) |
not yet published |
| Go | github.com/codinglombok/lomboklocale/go |
tag go/v0.2.0 on release |
| PHP | codinglombok/lomboklocale (Packagist) |
root composer.json, no version field: versioned by the v* tags |
The npm package ships dual ESM and CJS builds: import ... from 'lomboklocale' and require('lomboklocale') both work.
Quick start
import { formatNumber, formatDate, formatMessage, negotiate, pluralCategory } from 'lomboklocale'; negotiate(['id-ID', 'en'], ['en', 'id'], 'en'); // "id" formatNumber('id', 1234567.891); // "1.234.567,891" formatNumber('de', '15000', { style: 'currency', currency: 'EUR' }); // "15.000,00 €" formatNumber('hi', '1234567.5'); // "12,34,567.5" formatDate('id', '2026-10-02', 'full'); // "Jumat, 02 Oktober 2026" pluralCategory('pl', 21); // "many" formatMessage('en', '{n, plural, one{# file} other{# files}}', { n: 1234 }); // "1,234 files" formatMessage('en', '{n, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}', { n: 23 }); // "23rd"
use lomboklocale::{format_message, format_number, Arg, NumberOptions}; format_number("id", "15000", &NumberOptions::currency("IDR"))?; // "Rp 15.000,00" format_message("id", "Total {n}", &[("n", Arg::from(1234567.5))])?; // "Total 1.234.567,5"
from lomboklocale import format_date, format_number, Num format_number("fr", 1234.5, style="currency", currency="EUR") # "1 234,50 €" format_date("ja", "2026-10-02", "full") # "2026年10月2日金曜日"
s, err := lomboklocale.FormatNumber("en", "0.256", lomboklocale.NumberOptions{Style: "percent"}) // "26%"
use LombokLocale\Locale; Locale::formatNumber('ko', 15000, 'currency', 'KRW'); // "₩15,000"
Spaces shown above inside amounts are U+00A0 or U+202F, as CLDR specifies.
Message catalogs
locales/<tag>/lomboklocale.json holds the messages of this library itself; en and id are reviewed, the others are machine-translated drafts awaiting native review (see locales/STATUS.md). Any application can use the same format: parse_catalog reads a strict JSON object of strings, and resolve formats one message or returns !!id!! so a missing translation is visible.
Known limitations
Latin digits and the Gregorian calendar only; number/date data for 28 locales (other locales fall back to en for formatting but use their own plural rules); no time-of-day, time zones, relative time, lists, units, compact or scientific notation; MessageFormat is a subset (no choice, spellout, ordinal styles, no MessageFormat 2 syntax). See docs/full_summary_project_LombokLocale_v0.2.0.md.
Development
python3 scripts/gen_ports.py --check && python3 vectors/build_vectors.py && node vectors/check_icu.mjs cd rust && cargo test && cargo clippy --all-targets -- -D warnings cd typescript && npm ci && npm run coverage cd python && python -m pytest && python -m ruff check --select E4,E7,E9,F . && python -m mypy lomboklocale cd go && go test ./... cd php && php tests/run.php bash scripts/lombok-doctor.sh LombokLocale docker build -t lomboklocale:local . && docker run --rm lomboklocale:local
License
Code: Apache-2.0 OR MIT, at your option (LICENSE-APACHE, LICENSE-MIT). Embedded CLDR data: Unicode License v3 (LICENSE-UNICODE); the data subset, its source and its license are recorded in NOTICE.