Skip to content

yankouskia/localize-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

62 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

localize-react

localize-react

React i18n, without the weight.

Tiny, type-safe React i18n built on Context and hooks.
< 1 kB brotli Β· 0 runtime deps Β· dual ESM + CJS Β· React 19 ready.

Docs Β· Quickstart Β· API Β· Recipes Β· Migration v1 β†’ v2

npm downloads CI coverage bundle types license


import { LocalizationProvider, useLocalize } from 'localize-react';

const translations = {
  en: { hello: 'Hi {{name}}!' },
  es: { hello: 'Β‘Hola {{name}}!' },
  ja: { hello: '{{name}}さん、こんにけは!' },
};

function Greeting() {
  const { translate } = useLocalize();
  return <h1>{translate('hello', { name: 'Alex' })}</h1>;
}

export default function App() {
  return (
    <LocalizationProvider locale="en" translations={translations}>
      <Greeting />
    </LocalizationProvider>
  );
}

That's the whole API. Three exports, no plugins, no extraction toolchain. Ship it.


✨ Why?

Most React i18n libraries are 30–80 kB and bring opinions about plural rules, ICU MessageFormat, async loading, and TMS workflows. localize-react is the smallest thing that works.

It does exactly what a frontend most often needs:

  • A nested translations tree, keyed by locale.
  • Dot-path lookups ('cart.summary').
  • {{name}}-style interpolation.
  • A graceful fallback when keys are missing.

For everything else (plurals, currency, dates) β€” reach for the platform: Intl.PluralRules, Intl.NumberFormat, Intl.DateTimeFormat. Free, fast, already in the browser. See the Intl formatters recipe.

πŸ“¦ Specs

Property Value
Bundle (brotli) 916 B ESM Β· 989 B CJS
Runtime dependencies 0
Source Strict TypeScript 6
Module formats ESM + CJS with proper exports/types conditions
Tree-shaking sideEffects: false
Peer range React >= 16.8 < 20 (tested in CI through React 19)
Node >= 20.19 (CI on 20, 22, 24 Γ— Linux/macOS/Windows)
Test coverage 100 % statements Β· 100 % functions Β· 98 % branches
Type-checked exports Validated by publint + @arethetypeswrong/cli in CI

πŸš€ Install

npm install localize-react
# or: pnpm add localize-react Β· yarn add localize-react Β· bun add localize-react

⚑️ Quickstart

1. Define translations

export const translations = {
  en: {
    greeting: { hello: 'Hi {{name}}!' },
    cart: { summary: '{{count}} items, {{total}} total' },
  },
  es: {
    greeting: { hello: 'Β‘Hola {{name}}!' },
    cart: { summary: '{{count}} artΓ­culos, {{total}} total' },
  },
} as const;

2. Mount the provider

import { LocalizationProvider } from 'localize-react';
import { translations } from './i18n/translations';

export function App() {
  return (
    <LocalizationProvider locale="en" translations={translations}>
      <Shell />
    </LocalizationProvider>
  );
}

3. Translate, two ways

import { Message, useLocalize } from 'localize-react';

// Hook
function Cart() {
  const { translate } = useLocalize();
  return <p>{translate('cart.summary', { count: 3, total: '$42.00' })}</p>;
}

// Component
function CartHeader() {
  return (
    <h1>
      <Message descriptor="greeting.hello" values={{ name: 'Alex' }} />
    </h1>
  );
}

That's the whole story. Full docs at yankouskia.github.io/localize-react.

🧠 Concept in one screen

Operation API
Mount translations <LocalizationProvider locale translations>
Translate (hook) useLocalize().translate(descriptor, values?, default?)
Translate (component) <Message descriptor values? defaultMessage? />
Switch locale at runtime Re-render with a new locale prop
Missing key Renders defaultMessage ?? descriptor (never throws)
Nested lookup translate('a.b.c') walks the tree
Interpolation {{token}} β€” literal replacement, safe with regex chars
Locale normalization En-US β†’ en_us β†’ en

πŸ“š Recipes

Real-world patterns, fully documented on the site:

πŸ₯Š How it compares

localize-react react-i18next react-intl lingui
Bundle (brotli) < 1 kB ~17 kB ~38 kB ~9 kB
Runtime deps 0 several several one macro
Pluralization (CLDR) Use Intl βœ… βœ… (ICU) βœ… (ICU)
Number / date format Use Intl Optional βœ… βœ…
ICU MessageFormat ❌ βœ… βœ… βœ…
Lazy locale loading DIY βœ… βœ… βœ…
Auto extraction ❌ βœ… CLI CLI
TypeScript-first βœ… βœ… βœ… βœ…
Learning curve Tiny Medium Medium Medium

Use localize-react when you want a hook + a tag. Reach for the others when CLDR plurals, ICU MessageFormat, or a TMS workflow matter β€” they're all great at what they do.

πŸ›‘ Production-ready

  • Types ship inside the package β€” no @types/localize-react to chase.
  • Provenance attestation on every published version (npm OIDC trusted publishing).
  • CodeQL runs on every PR; CI matrix exercises Node 20/22/24 Γ— Linux/macOS/Windows.
  • Size budget enforced β€” < 2 kB ESM, < 2.5 kB CJS, checked on every PR with size-limit.
  • No dynamic require, no eval, no regex from user input β€” interpolation is literal replaceAll.

πŸ“– v1 β†’ v2

The runtime API is unchanged. v2 modernizes the toolchain (strict TS 6, dual ESM+CJS, React 19 peer, GitHub Actions + Changesets). One soft TypeScript regression in exactOptionalPropertyTypes mode β€” see the migration guide.

🀝 Contributing

PRs welcome. See CONTRIBUTING.md for the setup + release flow. Security reports: please open a private security advisory rather than a public issue.

If you'd like to support the project, sponsoring helps a lot.

πŸ“„ License

MIT Β© Aliaksandr Yankouski

About

✈️ Lightweight React Localization Library πŸ‡ΊπŸ‡Έ

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors