Fastify Hook
Fastify integrates with Polingo through the same polingoMiddleware helper used by Express. Register it as an onRequest hook to provide localized helpers on each request.
Project Setup
bash
mkdir polingo-fastify-example
cd polingo-fastify-example
pnpm init
git initInstall dependencies:
bash
pnpm add fastify @polingo/node
pnpm add -D typescript tsx @types/node @polingo/cliTypeScript configuration (tsconfig.json):
json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}Add scripts to package.json:
json
{
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"extract": "polingo extract src --locales locales --languages en,es,fr --default-locale en",
"validate": "polingo validate locales"
}
}Register the Hook
Create src/server.ts:
ts
import fastify from 'fastify';
import { polingoMiddleware } from '@polingo/node';
const app = fastify({ logger: true });
app.addHook(
'onRequest',
polingoMiddleware({
directory: './locales',
locales: ['en', 'es', 'fr'],
fallback: 'en',
perLocale: true,
localeExtractor: (req) =>
typeof req.query?.lang === 'string'
? req.query.lang
: ((req.headers['accept-language'] as string | undefined)?.split(',')[0]?.split('-')[0] ??
'en'),
})
);
app.get('/', async (request) => request.polingo.t('Hello from Fastify!'));
app.get('/items', async (request) => {
const count = Number.parseInt((request.query as Record<string, string>).count ?? '0', 10);
return request.polingo.tn('{count} item in cart', '{count} items in cart', count);
});
app.get('/api/status', async (request) => ({
message: request.polingo.t('Server is running'),
status: request.polingo.tp('status', 'OK'),
locale: request.polingo.getLocale(),
}));
const PORT = Number.parseInt(process.env.PORT ?? '3000', 10);
await app.listen({ port: PORT });
console.log(`Fastify server listening on http://localhost:${PORT}`);Using perLocale: true ensures each request receives an isolated translator, matching Fastify's concurrency model.
Translate Catalogs
Extract strings and create the catalogs:
bash
pnpm extractTranslate locales/es/messages.po:
po
msgid "Hello from Fastify!"
msgstr "¡Hola desde Fastify!"
msgid "{count} item in cart"
msgid_plural "{count} items in cart"
msgstr[0] "{count} artículo en el carrito"
msgstr[1] "{count} artículos en el carrito"
msgid "Server is running"
msgstr "El servidor está funcionando"
msgctxt "status"
msgid "OK"
msgstr "Bien"Translate locales/fr/messages.po:
po
msgid "Hello from Fastify!"
msgstr "Bonjour depuis Fastify!"
msgid "{count} item in cart"
msgid_plural "{count} items in cart"
msgstr[0] "{count} article dans le panier"
msgstr[1] "{count} articles dans le panier"
msgid "Server is running"
msgstr "Le serveur fonctionne"
msgctxt "status"
msgid "OK"
msgstr "D'accord"Validate & Run
bash
pnpm validate
pnpm devHit the endpoints:
bash
curl http://localhost:3000/
curl http://localhost:3000/?lang=es
curl "http://localhost:3000/items?count=3&lang=fr"
curl http://localhost:3000/api/status?lang=es- The middleware is framework-agnostic; swap Fastify for Express without changing translation logic.
- Prefer environment variables or cookies in
localeExtractorfor production deployments. - Shut down gracefully by awaiting
app.close()and callingrequest.polingo.stopWatching?.()whenwatchis enabled.