mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmWeb Frontendupdated 22 Sept 2026

icu-minify review

icu-minify 4.13.7 compiles ICU MessageFormat strings into compact JSON arrays, then formats those arrays with native Intl objects at runtime. Its two public subpaths split the expensive parser from the small formatter: use icu-minify/compile during a build and ship icu-minify/format with the catalog. Arguments, select branches, cardinal and ordinal plurals, number and date styles, time values, and rich-text tags are supported. Our root-entry checks failed for both require() and ESM import because version 4.13.7 exports subpaths only; a bare icu-minify import is not part of its API.

Verdict

icu-minify 4.13.7 installed in 1.1 seconds and left 3 packages, but bare require() and ESM import both failed in our sandbox because only subpath exports exist. Choose it for build-time ICU compilation into JSON; choose a full i18n package when you also need catalog loading, framework hooks, or remote-message handling.

We installed it

Lab card: what happened when we installed icu-minifyScreenshot of icu-minify documentation
Install✓ · 1.1s3 packages on disk · 1 MB
ImportESM import fails · require() fails · ESM package with exports map
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does icu-minify install cleanly?

Yes. In a fresh container with an empty cache, npm install icu-minify finished in 1 seconds, leaving 3 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

Can icu-minify run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does icu-minify work with both ESM and CommonJS?

Neither plain import nor require succeeded in our sandbox, so it needs a bundler or extra setup.

Does icu-minify include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

icu-minify or intl-messageformat: which should you use?

intl-messageformat: Use it when runtime parsing and a mature direct formatter matter more than a precompiled JSON catalog. icu-minify 4.13.7 installed in 1.1 seconds and left 3 packages, but bare require() and ESM import both failed in our sandbox because only subpath exports exist.

When should you not use icu-minify?

Messages arrive from a CMS at request time and no server-side compilation cache is available. Shipping the compile subpath to browsers defeats the package's design.

API stability2/5Version 4.13.7 has only two function entry points, icu-minify/compile and icu-minify/format, so direct usage can be wrapped easily. The package first appeared with next-intl's 4.8 precompilation work and still versions in lockstep with that monorepo. A missing root export caused both module probes to fail, and the README call omits the formatter options required by the current implementation, which makes the public contract feel young.
Docs3/5The package README names every supported ICU construct, shows the array output, and links a detailed RFC covering motivation, encoding, remote messages, raw catalog values, and plural offsets. The quick example does not show the required Intl formatter factories or explain named formats, Date values, production-only validation differences, and result unions for rich text. Those details currently require reading source and tests.
Maintenance5/5Version 4.13.7 was published on August 17, 2026, and the next-intl repository was pushed on August 21. GitHub shows 52 open issues and pull requests across the full monorepo, which is not a package-specific backlog. The recent changelog includes fixes for plural branches and ICU escaping, although the 4.13.7 entry itself concerns an SWC range used by the surrounding extractor plugin.
Ecosystem3/5npm counted 4,329,151 downloads in the week ending August 24, 2026, and the next-intl repository has 4,354 stars. Much of that reach comes from next-intl rather than engineers selecting icu-minify directly. Standard ICU syntax, native Intl APIs, and JSON output make the pieces portable, but the package supplies no adapters for routing, React state, extraction, or translation services.

Use it if

  • Your ICU catalogs can be compiled before deployment, allowing the browser to receive JSON arrays instead of a message parser.
  • Compiled messages must cross a server-to-client data boundary as serializable values rather than JavaScript functions.
  • You already own locale loading and message lookup and need only the compiler and formatter layers.
  • Client runtime weight matters enough to add a catalog compilation step and an Intl formatter cache.
Skip it if

Setup reality

We installed icu-minify 4.13.7 in a fresh Node 22 Bookworm sandbox in 1.1 seconds. It left 3 packages and 1 MB on disk, with 0 known vulnerabilities from npm audit. The package has 1 direct dependency, 0 peer dependencies, 88 KB unpacked, and an MIT license. Our scanner found no TypeScript types. Node 22.23.2 failed both root require() and root ESM import because the exports map contains only ./compile and ./format.

Import icu-minify/compile in a build script and icu-minify/format in runtime code. There are no credentials, native builds, or required config files. You must create the catalog transform yourself when you use this package directly. Store the compiler output as JSON, version it with the source catalog if reproducibility matters, and fail CI on malformed strings.

The formatter requires factories for Intl.DateTimeFormat, Intl.NumberFormat, and Intl.PluralRules. Cache those objects by locale and options in a busy service; constructing them for every message wastes the size win. Named number or date styles need matching entries in the options object, and formatted date values must be Date instances.

Our esbuild browser build failed, so we have no measured browser size for version 4.13.7. Development code throws for missing arguments, wrong plural values, absent fallback branches, and tag handlers. Some checks are conditional on NODE_ENV, which makes preproduction catalog tests important. Plural offsets remain unsupported, and remote catalogs need a trusted compilation step before formatting.

Patterns

Compile one message during the build compile-message

import compile from 'icu-minify/compile';

const message = compile('Hello {name}!');
console.log(JSON.stringify(message));

Version 4.13.7 exposes compile only through the /compile subpath; a bare package import failed in our Node 22 test.

Provide cached Intl factories create-formatters

const cache = new Map();
const cached = (Ctor, args) => {
  const key = `${Ctor.name}:${JSON.stringify(args)}`;
  if (!cache.has(key)) cache.set(key, new Ctor(...args));
  return cache.get(key);
};

const formatters = {
  getDateTimeFormat: (...args) => cached(Intl.DateTimeFormat, args),
  getNumberFormat: (...args) => cached(Intl.NumberFormat, args),
  getPluralRules: (...args) => cached(Intl.PluralRules, args),
};

The current formatter expects all 3 factory functions; caching avoids rebuilding Intl instances for each message.

Format a compiled argument format-message

import format from 'icu-minify/format';

const output = format(
  ['Hello ', ['name'], '!'],
  'en',
  {name: 'Mina'},
  {formatters}
);

The /format subpath consumes compiled data, a locale, values, and formatter options in version 4.13.7.

Transform a flat JSON catalog compile-catalog

const compiled = Object.fromEntries(
  Object.entries(source).map(([key, value]) => {
    if (typeof value !== 'string') throw new TypeError(`${key} is not a string`);
    return [key, compile(value)];
  })
);
await writeFile(outputPath, JSON.stringify(compiled));

Compilation can throw on invalid ICU syntax, so fail the build instead of silently copying the source value.

Handle exact and cardinal plural branches format-plural

const files = compile(
  '{count, plural, =0 {No files} one {# file} other {# files}}'
);
format(files, 'en', {count: 2}, {formatters});

Every plural needs an other branch, and ICU plural offsets are unsupported by this compiler.

Select locale-aware ordinal rules format-ordinal

const rank = compile(
  '{n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
);
format(rank, 'en', {n: 21}, {formatters});

Ordinal categories depend on the locale; these 4 suffix branches describe English only.

Choose a select fallback format-select

const role = compile(
  '{role, select, admin {Administrator} editor {Editor} other {Member}}'
);
format(role, 'en', {role: 'guest'}, {formatters});

Version 4.13.7 falls back to other for an unmatched string, and development mode reports a missing fallback.

Supply a named number style format-currency

const total = compile('Total: {amount, number, currency}');
const options = {
  formatters,
  formats: {number: {currency: {style: 'currency', currency: 'EUR'}}}
};
format(total, 'de-DE', {amount: 1234.5}, options);

The style name currency must appear under formats.number or development formatting throws.

Format a Date in a fixed zone format-date

const published = compile('Published {at, date, short}');
const options = {
  formatters,
  timeZone: 'UTC',
  formats: {dateTime: {short: {year: 'numeric', month: 'short', day: 'numeric'}}}
};
format(published, 'en-GB', {at: new Date('2026-08-26T12:00:00Z')}, options);

Pass a Date instance for date and time nodes; an ISO string is the wrong runtime value type.

Map an ICU tag to an application value format-rich-text

const linked = compile('Read <link>{title}</link>');
const output = format(linked, 'en', {
  title: 'the guide',
  link: (chunks) => ({type: 'a', href: '/guide', children: chunks})
}, {formatters});

A tag callback may return an object, so the formatter result can be a mixed array instead of one string.

Cache a compiled remote catalog compile-remote-catalog

const response = await fetch(catalogUrl);
if (!response.ok) throw new Error(`catalog HTTP ${response.status}`);
const source = await response.json();
const compiled = compileCatalog(source);
await cache.set(cacheKey, compiled);

Remote source strings cannot benefit from build-time compilation unless your server fetches, compiles, and caches them before clients request the catalog.

Reject one bad locale in CI validate-all-locales

for (const [locale, catalog] of Object.entries(catalogs)) {
  for (const [key, message] of Object.entries(catalog)) {
    try { compile(message); }
    catch (cause) { throw new Error(`${locale}.${key} is invalid`, {cause}); }
  }
}

Run this under a nonproduction NODE_ENV because several formatter checks are omitted from the production build.

Alternatives

PackageRegistryPick it when
intl-messageformatnpmUse it when runtime parsing and a mature direct formatter matter more than a precompiled JSON catalog.
@messageformat/corenpmUse it when compiling messages into JavaScript functions fits your build and deployment model.
@lingui/corenpmUse it when catalog extraction and application integrations should come from the same i18n toolkit.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.