Changelog
Unreleased
Added
countRanges.ranges— declare custom range thresholds as an array of[min, max]tuples.Infinityas the max renders the suffix asplus. If unset, the defaults ([[0,5], [6,20], [21,Infinity]]) are used, so existing consumers see no behavior change.translationLocaleCode— correctly-spelled configuration key for the runtime translation locale override. The misspelledtranslationLocalCodestill works (read with lower priority) so existing configs keep functioning.- README rewrite in the same marketing-then-reference shape as
@mongez/atom. Real code, every public export documented, JSX/React story linked out to@mongez/react-localizationrather than mixed in. - AI kit.
llms.txt,llms-full.txt, andskills/(README,overview,translations,interpolation,count-translations,events,recipes) for tool-assisted development. - Vitest test suite at
src/__tests__/. 84 passing tests across the translator core, configuration plumbing, events bus, placeholder interpolation, grouped translations, thetransObjectproxy, count-based translations, and range-based count keys. - CI. GitHub Actions workflow at
.github/workflows/test.yml: Node 18/20/22 on Ubuntu, plus Node 20 on Windows. vitest.config.tswith a self-detecting sibling-alias resolver: it picks up@mongez/eventsand@mongez/reinforcementsfrom the local monorepo when present, and lets the published packages resolve fromnode_modulesotherwise. CI runs identically to a fresh consumer install.
Changed
package.json:descriptionrewritten to lead with the framework-agnostic, count-rule, and placeholder-interpolation story instead of the generic “i18n handler” line.sideEffects: falseset so bundlers can tree-shake.keywordsexpanded to coverinternationalization,pluralization,interpolation,framework-agnostic, and the major framework names users actually search for.scripts.testswapped fromjest ./teststovitest run;test:watchswapped tovitest. The oldtest:coverage,test:file,fix:test,format:testscripts and their jest/eslint/prettier devDeps were removed.devDependenciescollapsed totypescript ^5.4.0andvitest ^2.1.0. The previous jest/ts-jest/eslint stack no longer worked under modern Node — the existingtests/folder ran on jest 29 + ts-jest 29, which stopped resolving cleanly under Node 22 in this workspace.
Fixed
- Typo in config key.
LocalizationConfigurations.translationLocalCode(src/types.ts:110) was missing theein “locale”. The documentedtranslationLocaleCodeis now the preferred name and is checked first insrc/translator.ts. The misspelledtranslationLocalCodecontinues to work as a fallback for backward compatibility (marked@deprecatedintypes.ts). countRanges.separatoris now respected. It was read insrc/count-rules.ts:80but never used to construct the lookup key. The selector now builds the suffix as_range{separator}{min}{separator}{max}, defaulting to_to preserve current behavior.- Range thresholds are now configurable. The hardcoded buckets in
src/count-rules.ts:83-87are replaced bycountRanges.ranges— an array of[min, max]tuples. When unset, the previous defaults ([0,5], [6,20], [21,Infinity]) are used, so existing consumers see no change. _range[0-5]doc references removed. Only the underscore form_range_0_5is implemented. Docs match the implementation.
Other observations (not introduced here, surfaced during the docs/test pass)
- Falsy translations bypass.
transFromuses||chains on the result ofget(translationsList, …), so an intentionally-empty translation ("") falls through to the fallback locale and then to the bare keyword. Translations that should legitimately be empty in some locales aren’t supported. (src/translator.ts:235-238and:247-251.) localizationEvents.onChangefires even when the value doesn’t change. CallingsetCurrentLocaleCode("en")while the current locale is already"en"still triggers thelocaleCodeevent. The tests assert the current behavior; whether to dedupe is a design call.- Arabic
manyrule cuts off at 99. Persrc/count-rules.ts:30-33,manymatchesmod100between 11 and 99. The README example (“count > 10”) and the explicit “many: count > 10” wording inREADME.md:846-849don’t match. Counts of 100, 200, 1000 land on_other, not_many.
Tests
84 passing + 0 skipped = 84 total