fastify-multilingual
v1.1.0
Published
Fastify plugin to decorate the request with different translations, based on available Polyglot.js phrases.
Downloads
22
Maintainers
Readme
fastify-multilingual
A Fastify plugin that decorates requests with internationalization (i18n) capabilities using Polyglot.js. The plugin automatically detects user language preferences from Accept-Language headers and provides localized phrases with support for pluralization and nested phrase structures.
Features
- 🌍 Automatic locale detection from
Accept-Languageheaders - 📦 Polyglot.js integration for powerful i18n features
- 🔄 Graceful fallback to default locale
- 🏗️ Support for nested phrase structures
- ⚡ Per-request polyglot instances for optimal performance
- 🛡️ TypeScript support with full type safety
Compatibility
| Plugin Version | Fastify Version | Node.js Version |
| -------------- | --------------- | --------------- |
| 1.x.x | ^5.0.0 | >=20.0.0 |
Installation
npm install fastify-multilingualUsage
Basic Setup
const fastify = require('fastify')({ logger: true });
// Define your phrases
const phrases = {
en: {
greeting: {
hi: 'Hello',
welcome: 'Welcome %{name}'
},
404: {
not_found: 'Page not found'
}
},
it: {
greeting: {
hi: 'Ciao',
welcome: 'Benvenuto %{name}'
},
404: {
not_found: 'Pagina non trovata'
}
}
};
// Register the plugin
await fastify.register(require('fastify-multilingual'), {
multilingual: {
phrases,
defaultTranslation: 'en'
}
});
// Use in routes
fastify.get('/', async (request, reply) => {
const polyglot = request.polyglot;
return {
message: polyglot.t('greeting.hi'),
welcome: polyglot.t('greeting.welcome', { name: 'World' }),
availableTranslations: request.availableTranslations
};
});
await fastify.listen({ port: 3000 });Organizing Phrases in Separate Files
For larger applications, it's recommended to organize phrases in separate files by locale. Here's how to structure your i18n files:
i18n/
├── index.js # Combines all locale files
├── en.js # English phrases
├── it.js # Italian phrases
└── pt-BR.js # Portuguese (Brazil) phrasesIndividual locale files:
// i18n/en.js
export const phrases = {
greeting: {
hi: 'Hello',
welcome: 'Welcome %{name}'
},
404: {
not_found: 'Page not found'
},
other: 'Not nested'
};// i18n/it.js
const phrases = {
greeting: {
hi: 'Ciao'
},
404: {
not_found: 'Pagina non trovata'
}
};
export default phrases;Index file to combine all locales:
// i18n/index.js
import { phrases as en } from './en.js';
import it from './it.js';
import { phrases as pt_BR } from './pt-BR.js';
export const phrases = {
en: { ...en },
it: { ...it },
pt_BR: { ...pt_BR }
};Using in your Fastify application:
import fastify from 'fastify';
import fastifyMultilingual from 'fastify-multilingual';
import { phrases } from './i18n/index.js';
const app = fastify({ logger: true });
await app.register(fastifyMultilingual, {
multilingual: {
phrases,
defaultTranslation: 'en'
}
});See a complete example in the repository
TypeScript Usage
import fastify, { FastifyRequest, FastifyReply } from 'fastify';
import fastifyMultilingual, { NestedPhrases } from 'fastify-multilingual';
const app = fastify({ logger: true });
const phrases: NestedPhrases = {
en: {
greeting: {
hi: 'Hello',
welcome: 'Welcome %{name}'
}
},
it: {
greeting: {
hi: 'Ciao',
welcome: 'Benvenuto %{name}'
}
}
};
await app.register(fastifyMultilingual, {
multilingual: {
phrases,
defaultTranslation: 'en'
}
});
// TypeScript route with proper typing
app.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
const { polyglot } = request;
return {
message: polyglot.t('greeting.hi'),
welcome: polyglot.t('greeting.welcome', { name: 'TypeScript' }),
availableTranslations: request.availableTranslations
};
});Configuration Options
The plugin accepts the following options:
multilingual
| Property | Type | Required | Description |
| --------------- | -------- | -------- | ----------- |
| phrases | NestedPhrases | Yes | Object containing locale-keyed phrase objects |
| defaultTranslation | string \| null | Yes | Fallback locale when user's preferred locale is unavailable |
Phrases Structure
The phrases object supports nested structures:
{
"en": {
"simple": "Simple message",
"nested": {
"deep": {
"message": "Deeply nested message"
}
},
"pluralization": "You have %{smart_count} message |||| You have %{smart_count} messages"
}
}API Reference
The plugin decorates the Fastify request object with the following properties:
request.polyglot
Returns a Polyglot instance configured for the user's detected locale, as set in the onRequest hook.
const polyglot = request.polyglot;
const message = polyglot.t('greeting.hi');request.availableTranslations
String containing comma-separated list of available locales.
console.log(request.availableTranslations); // "en,it,pt-BR"request['polyglot-{locale}']
Access polyglot instances for specific locales:
const englishPolyglot = request['polyglot-en'];
const italianPolyglot = request['polyglot-it'];Locale Detection
The plugin automatically detects the user's preferred locale using the following priority:
- Parse
Accept-Languageheader - Match against available locales (exact match first, then language family)
- Fall back to
defaultTranslation - If no default translation, return key-based responses
Example Accept-Language header processing:
en-US,en;q=0.9,it;q=0.8→ Prefersen-US, falls back toen, thenitpt-BR,pt;q=0.9→ Preferspt-BR, falls back topt
Examples
Running the Example
The repository includes a working example demonstrating the plugin:
# Start the example server
npm run example
# Test with different locales
npm run example:getThe example server runs on port 3000 and includes phrases in English, Italian, Portuguese (Brazil), and German (not present) fallback handling.
Testing Different Locales
# Test English
curl -H "Accept-Language: en" http://localhost:3000/
# Test Italian
curl -H "Accept-Language: it" http://localhost:3000/
# Test Portuguese (Brazil)
curl -H "Accept-Language: pt-BR" http://localhost:3000/
# Test fallback (German → English)
curl -H "Accept-Language: de" http://localhost:3000/Development
Using with Dev Containers
This project supports Development Containers for a consistent development environment. The devcontainer configuration includes Node.js 20, npm, and all necessary development tools.
Using with VS Code:
- Install the Dev Containers extension
- Open the project in VS Code
- When prompted, click "Reopen in Container" or use
Ctrl+Shift+P→ "Dev Containers: Reopen in Container" - VS Code will build the container and install dependencies automatically
Using with GitHub Codespaces:
- Navigate to the repository on GitHub
- Click the green "Code" button → "Codespaces" tab → "Create codespace on main"
- The environment will be ready with all dependencies installed
Manual setup in any devcontainer-compatible environment:
# The container will automatically run these commands:
npm install # Install dependencies
npm run build # Build the project
npm test # Verify everything worksOnce inside the devcontainer, you can use all the standard development commands like npm run example, npm test, and npm run build as documented below.
Building
npm run build # Compile TypeScript
npm run build:clean # Clean build and rebuildTesting
The project uses Node.js built-in test runner with comprehensive test coverage:
npm test # Build and run all testsTest coverage includes:
- Locale detection and matching algorithms
- Fallback behavior for unsupported locales
- Nested phrase structure support
- Malformed
Accept-Languageheader handling - Plugin registration and decorator behavior
Code Style
The project uses neostandard ESLint configuration:
npx eslint . # Check code style
npx eslint . --fix # Auto-fix style issuesProject Structure
├── src/
│ ├── plugin.ts # Main plugin implementation
│ └── util.ts # Locale matching utilities
├── test/ # Test files
├── example/ # Working example application
└── dist/ # Compiled outputContinuous Integration
The project includes automated CI/CD with:
- GitHub Actions: Automated testing on Node.js 20.x and 22.x
- Dependabot: Daily dependency updates
- Code Quality: Automated linting and type checking
Contributing
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Ensure all tests pass and code follows style guidelines
- Submit a pull request
License
Licensed under MIT.
