TypeScript-first · React 18+

Localization that just works at runtime.

A lightweight i18n library for React. Switch languages instantly, fall back gracefully when a translation is missing, detect browser locales, and persist language preferences with configurable storage — all while keeping components in sync with useSyncExternalStore, with no external localization dependencies required.

$ npm install atum-localization
Read the docs
License MIT Peer deps react ≥18, react-dom ≥18 Formats ESM & CJS
Home.tsx
live demo useTranslation() + useLanguage()

Atum Localization

Login Save Cancel
LANGUAGE
setLanguage() re-renders instantly, no reload

Why atum-localization

Small API. Real-time updates. Zero bloat.

Everything the library does, and nothing it makes you carry.

Runtime language switching

Call setLanguage() anywhere in the tree and every connected component updates immediately.

Fallback language support

Missing a key in the active locale? It resolves from the configured fallback before giving up.

React Context integration

One provider at the root. Every hook downstream reads from the same localization instance.

TypeScript support

Ships with full type declarations, so resources and options are checked as you write them.

No external i18n deps

No gettext, no ICU parser, no locale-data bundle. Just your own resource objects.

Built on useSyncExternalStore

Subscriptions are handled the way React itself recommends for external state — efficient by default.

Browser language detection

Detect the user's browser locale automatically and match regional locales before falling back to the base language.

Language persistence

Persist the selected language with LocalStorage, SessionStorage, or disable persistence when you do not need it.

Configurable storage key

Use the default storage key or provide your own key for language persistence.


Getting started

Six steps to a localized app

The order matters here — each step builds on the last.

01

Create translation resources

Plain objects, one per language. Keys are whatever you want — SCREAMING_CASE keeps them easy to spot in JSX.

locales/en.tsexport default {
  TITLE: "Atum Localization",
  LOGIN: "Login",
  SAVE: "Save",
  CANCEL: "Cancel",
  LANGUAGE: "Language",
};
02

Create the localization instance

Wire your resources together with a default and a fallback language.

localization.tsimport { createLocalization } from "atum-localization";
import en from "./locales/en";
import ta from "./locales/ta";

export const localization = createLocalization({
  defaultLanguage: "en",
  fallbackLanguage: "en",
  resources: { en, ta },
});
03

Enable language detection

Automatically detect the browser locale and persist an explicitly selected language using LocalStorage, SessionStorage, or no persistence.

localization.tsexport const localization = createLocalization({
  defaultLanguage: "en",
  fallbackLanguage: "en",
  resources: { en, ta },
  detection: {
    enabled: true,
    storage: "local",
    storageKey: "atum-localization-language",
  },
});

Detection priority: stored language → browser language → defaultLanguage. Regional locales try an exact match first, then the base language.

04

Wrap your application

Mount LocalizationProvider once, at the root.

main.tsximport { LocalizationProvider } from "atum-localization";
import { localization } from "./localization";

<LocalizationProvider localization={localization}>
  <App />
</LocalizationProvider>
05

Translate content

useTranslation() returns a t() function scoped to the current language.

Home.tsximport { useTranslation } from "atum-localization";

export default function Home() {
  const { t } = useTranslation();
  return <h1>{t("TITLE")}</h1>;
}
06

Change language

useLanguage() gives you the active language and a setter — this is what powers the demo above.

LanguageSelector.tsximport { useLanguage } from "atum-localization";

export default function LanguageSelector() {
  const { language, setLanguage } = useLanguage();
  return (
    <select value={language} onChange={e => setLanguage(e.target.value)}>
      <option value="en">English</option>
      <option value="ta">Tamil</option>
    </select>
  );
}

API reference

Everything the package exports

One factory function, one provider, two hooks.

createLocalization(options)

Creates a localization instance from your resources.

OptionTypeDescription
defaultLanguagestringInitial app language
fallbackLanguagestringUsed when a key is missing
resourcesLocalizationResourcesYour translation objects
detectionLanguageDetectionOptionsBrowser language detection and persistence configuration

detection

Configures browser language detection and language persistence.

detection: {
  enabled: true,
  storage: "local",
  storageKey: "atum-localization-language",
}
OptionTypeDefaultDescription
enabledbooleanfalseEnables browser language detection and persistence
storage"local" | "session" | "none""local"Controls where the selected language is persisted
storageKeystring"atum-localization-language"Storage key used to persist the language

LocalizationProvider

Provides the instance to the component tree via React Context.

<LocalizationProvider localization={localization}>
  <App />
</LocalizationProvider>

useTranslation()

Returns the translation function bound to the active language.

const { t } = useTranslation();
t("LOGIN"); // → "Login"

useLanguage()

Returns the current language and a function to change it.

const { language, setLanguage } = useLanguage();
setLanguage("ta");

Fallback behavior

Missing a translation? Nothing breaks.

If a key isn't found in the active language, atum-localization looks in the fallback language. Still nothing? It returns the key itself, so your UI stays legible instead of blank.

Try a key

Resolution

t("LOGIN")
active (ta)சேமி
fallback (en)—
output"சேமி"