@polingo/node
v0.0.3
Published
Node.js loader for Polingo translation system
Downloads
8
Maintainers
Readme
@polingo/node
Node.js adapter that loads gettext catalogs from disk, watches for changes, and plugs into web frameworks.
@polingo/node combines a filesystem loader, optional hot-reload watcher, and helper utilities so you can bootstrap Polingo in backend services, CLIs, Electron apps, or server-rendered frameworks.
Contents
- What it does
- Installation
- Quick start
- Directory layout
- API overview
- Framework recipes
- Hot reload & deployment tips
- Troubleshooting
- Related packages
- License
What it does
- Parses
.poand.mocatalogs withgettext-parser. - Provides
createPolingofor one-call setup (loader, cache, preload, optional watcher). - Exposes
NodeLoaderif you need to wire things manually. - Ships an Express/Fastify-compatible
polingoMiddlewarewith locale detection. - Includes
TranslationWatcherfor chokidar-based hot reloading during development. - Designed for TypeScript with rich types carried through from
@polingo/core.
Installation
npm install @polingo/core @polingo/node
# or
pnpm add @polingo/core @polingo/node
# or
yarn add @polingo/core @polingo/nodeQuick start
import { createPolingo } from '@polingo/node';
const polingo = await createPolingo({
locale: 'en',
locales: ['en', 'es'],
directory: './locales',
fallback: 'en',
cache: true,
watch: process.env.NODE_ENV === 'development',
});
polingo.t('Hello, world!');
polingo.tn('{n} file', '{n} files', 3, { n: 3 });Call polingo.stopWatching?.() on shutdown if you enabled watch: true.
Directory layout
The loader accepts either of the common gettext layouts:
locales/
├── en/
│ ├── messages.po
│ └── errors.po
└── es/
└── LC_MESSAGES/
├── messages.mo
└── errors.moInternally the loader first tries <locale>/<domain>.po and falls back to <locale>/<domain>.mo.
API overview
createPolingo(options: CreatePolingoOptions)
Convenience helper that configures the loader, cache, domain, preload list, and optional watcher in one await.
interface CreatePolingoOptions {
locale: string; // current locale
locales: string[]; // locales to preload during init
directory: string; // root folder that holds locale subfolders
fallback?: string; // defaults to 'en'
domain?: string; // defaults to 'messages'
cache?: boolean; // MemoryCache when true, NoCache when false
watch?: boolean; // enables chokidar watcher
debug?: boolean; // console logging for missing catalogs, watcher events
}Returns a Translator from @polingo/core extended with an optional stopWatching() method.
NodeLoader
Low-level loader if you prefer constructing translators yourself:
import { NodeLoader } from '@polingo/node';
import { Translator, MemoryCache } from '@polingo/core';
const loader = new NodeLoader('./locales');
const translator = new Translator(loader, new MemoryCache(), {
locale: 'en',
fallback: 'en',
});
await translator.load(['en', 'es']);polingoMiddleware(options: MiddlewareOptions)
Drop-in Express/Fastify middleware that attaches a translator to req.polingo.
- Detects the locale from
req.query.localeor theAccept-Languageheader by default. - Supports
perLocale: trueto keep individual translator instances per locale. - Accepts the same preload, directory, cache, and watch options as
createPolingo.
app.use(
polingoMiddleware({
locales: ['en', 'es'],
directory: './locales',
fallback: 'en',
debug: process.env.NODE_ENV !== 'production',
})
);TranslationWatcher
Wires chokidar to re-load catalogs whenever matching .po or .mo files change. Normally you do not need to interact with it directly—createPolingo({ watch: true }) handles the setup.
Framework recipes
Express
import express from 'express';
import { polingoMiddleware } from '@polingo/node';
const app = express();
app.use(
polingoMiddleware({
locales: ['en', 'es'],
directory: './locales',
fallback: 'en',
})
);
app.get('/', (req, res) => {
res.send(req.polingo.t('Welcome to Polingo!'));
});Fastify
import Fastify from 'fastify';
import { polingoMiddleware } from '@polingo/node';
const fastify = Fastify();
fastify.addHook('onRequest', polingoMiddleware({ locales: ['en', 'es'], directory: './locales' }));
fastify.get('/', async (req) => {
return { message: req.polingo.t('Hello from Fastify') };
});Next.js route handlers / API routes
Create once and reuse between requests to avoid repeated disk IO:
import { createPolingo } from '@polingo/node';
let nodePolingo: Awaited<ReturnType<typeof createPolingo>> | null = null;
async function getPolingo() {
if (!nodePolingo) {
nodePolingo = await createPolingo({
locale: 'en',
locales: ['en', 'es'],
directory: './locales',
});
}
return nodePolingo;
}Hot reload & deployment tips
- Enable
watch: trueonly in development. File-system watchers keep extra descriptors open and may not be supported on serverless providers. - On change events the watcher clears the cache and re-loads the affected locale automatically. If you persist catalogs elsewhere, disable caching with
cache: falseand manage reloads manually. - Compile
.pofiles to.mofor production to reduce parsing cost (usemsgfmtor@polingo/clionce it lands). - Call
await polingo.stopWatching?.()during graceful shutdown to release chokidar resources.
Troubleshooting
Translation file not found– verify yourdirectoryand ensure both<locale>/<domain>.poand<locale>/LC_MESSAGES/<domain>.povariants are present if you rely on gettext structure.Invalid .po file– runmsgfmt -cor a lint tool before deployment; parsing errors bubble up fromgettext-parser.- High memory usage with many locales – disable caching (
cache: false) or supply aMemoryCachewith amaxSize. - Locale fallback not respected – make sure the
fallbacklocale is included in thelocalespreload array so it is available immediately.
Related packages
@polingo/core– translation runtime (Translator, caches, helpers).@polingo/web– fetch-based loader with localStorage cache for browsers.@polingo/react– React hooks, context provider, and Trans component.@polingo/cli– command line tooling for extraction, compilation, and validation.
License
MIT © Reinier Hernández Avila
