Modern internationalization (i18n) for JavaScript and TypeScript using standard gettext catalogs. Polingo gives you one translation model across Node.js, browsers, React, Vue, and CLI-based catalog workflows.
Caution
Alpha Notice: Polingo is still alpha software. The public API is settling down, but breaking changes can still happen.
- Gettext-native: work with
.poand.mofiles instead of inventing a custom catalog format. - Shared core runtime: one
Translatormodel across server and client environments. - Synchronous translation APIs after preload: load catalogs once, then call
t(),tp(),tn(), ortnp()anywhere. - First-class pluralization, context-aware translations, and variable interpolation.
- Node.js helpers for filesystem loading, middleware, and hot reload during development.
- Browser helpers for fetch-based loading and localStorage-backed caching.
- CLI tooling for initialization, extraction, compilation, and validation.
- TypeScript-first packages with strict typing throughout.
| Package | Version | Use it for |
|---|---|---|
@polingo/core |
The environment-agnostic translation engine, cache types, interpolation, and plural logic | |
@polingo/node |
Filesystem loading, middleware, and translation watching for Node.js | |
@polingo/web |
Fetch-based loading and browser caching | |
@polingo/react |
React provider, hooks, and Trans component |
|
@polingo/vue |
Vue provider and composables | |
@polingo/cli |
init, extract, compile, and validate commands |
|
create-polingo-app |
Interactive starter scaffolding |
- A loader reads a catalog from disk or over the network.
- The catalog is normalized into the shared
TranslationCatalogstructure. - A cache stores parsed catalogs for reuse.
Translatorhandles locale selection, fallback lookup, interpolation, and plural rules.- Platform adapters such as
@polingo/node,@polingo/web,@polingo/react, and@polingo/vuewrap that core runtime for each environment.
Per-package Codecov coverage is published from CI:
| Package | Coverage |
|---|---|
| @polingo/cli | |
| @polingo/core | |
| @polingo/node | |
| @polingo/react | |
| @polingo/vue | |
| @polingo/web |
Choose the package set that matches your environment. Examples below use pnpm; swap for npm or yarn if preferred.
pnpm add @polingo/core @polingo/nodepnpm add @polingo/core @polingo/webpnpm add @polingo/core @polingo/web @polingo/reactpnpm add @polingo/core @polingo/web @polingo/vuepnpm add -D @polingo/cliScaffold a starter project from the maintained templates:
pnpm create polingo-appCurrent examples include React + Vite and Express.
import { createPolingo } from '@polingo/node';
const polingo = await createPolingo({
locale: 'es',
locales: ['es', 'en'],
directory: './locales',
fallback: 'en',
});
console.log(polingo.t('Hello'));
console.log(polingo.t('Hello, {name}!', { name: 'Juan' }));
console.log(polingo.tn('{n} item', '{n} items', 3, { n: 3 }));import { createPolingo } from '@polingo/web';
const polingo = await createPolingo({
locale: 'es',
locales: ['es', 'en'],
loader: { baseUrl: '/i18n' },
});
document.querySelector('#greeting')!.textContent = polingo.t('Hello');If you are actively editing translations in development, prefer cache: false or set cacheOptions.cacheKey so stale localStorage entries do not hide changes.
import { PolingoProvider, Trans, useTranslation } from '@polingo/react';
function AppContent() {
const { t, tn, locale, setLocale } = useTranslation();
return (
<div>
<h1>{t('Welcome to Polingo!')}</h1>
<p>{tn('You have {n} message', 'You have {n} messages', 3, { n: 3 })}</p>
<Trans
message="Read the <0>documentation</0> to learn more"
components={[<a href="/docs" />]}
/>
<button type="button" onClick={() => setLocale(locale === 'en' ? 'es' : 'en')}>
Switch locale
</button>
</div>
);
}
export function App() {
return (
<PolingoProvider
create={{
locale: 'en',
locales: ['en', 'es'],
loader: { baseUrl: '/i18n' },
}}
>
<AppContent />
</PolingoProvider>
);
}Vue support follows the same runtime model through @polingo/vue provider and composables. See the package README for framework-specific usage details.
Use the CLI to bootstrap a project and manage catalogs:
# Initialize scripts, dependencies, and locale folders
pnpm dlx @polingo/cli@latest init --env react --languages en,es
# Extract messages from source files and sync locale catalogs
pnpm polingo extract src --locales locales --languages en,es --default-locale en
# Compile catalogs for browser usage
pnpm polingo compile locales -o public/i18n --format json
# Or compile for Node.js usage
pnpm polingo compile locales -o dist/locales --format mo
# Validate catalogs in CI
pnpm polingo validate locales --strictThe extractor recognizes t(), tp(), tn(), tnp(), and React Trans usage. Fuzzy matching is enabled by default so near-renamed strings can be carried forward and marked for translator review.
Source catalogs usually live in a locale-first directory structure:
locales/
├── en/
│ └── messages.po
├── es/
│ └── messages.po
└── fr/
└── messages.po
For Node.js, @polingo/node also supports gettext-style LC_MESSAGES layouts and prefers .po files over .mo when both exist for the same locale and domain.
t(msgid, vars?)translates a basic message.tp(context, msgid, vars?)translates a message with context.tn(msgid, msgidPlural, count, vars?)selects the correct plural form.tnp(context, msgid, msgidPlural, count, vars?)combines context and plural handling.
For work inside this monorepo:
pnpm install --frozen-lockfile
pnpm build
pnpm typecheck
pnpm lint
pnpm format:check
make test
make coverageFor the maintainer-facing workflow around package tools, example/template sync, changesets, and publishing, see docs/guide/repository-workflow.md.
Requirements:
- Node.js
>= 22.0.0 pnpm >= 8.0.0
- @polingo/core
- @polingo/node
- @polingo/web
- @polingo/react
- @polingo/vue
- @polingo/cli
- create-polingo-app
Pull requests are welcome. For repository-specific workflow and development guidance, see AGENTS.md.
If you discover a security issue, follow the security policy.
MIT © Reinier Hernández Avila