JSPM

fastify-multilingual

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 15
  • Score
    100M100P100Q23976F
  • License MIT

Fastify plugin to decorate the request with different translations, based on available Polyglot.js phrases.

Package Exports

  • fastify-multilingual
  • fastify-multilingual/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (fastify-multilingual) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

fastify-multilingual

neostandard javascript style CI npm version

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-Language headers
  • 📦 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
^0.1.0 ^5.0.0 >=20.0.0

Installation

npm install fastify-multilingual

Usage

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,
    defaultLocale: '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' }),
    availableLocales: request.availableLocales
  };
});

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) phrases

Individual 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,
    defaultLocale: '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,
    defaultLocale: 'en'
  }
});

// TypeScript route with proper typing
app.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
  const polyglot = request.polyglot();
  
  return {
    message: polyglot.t('greeting.hi'),
    welcome: polyglot.t('greeting.welcome', { name: 'TypeScript' }),
    availableLocales: request.availableLocales
  };
});

Configuration Options

The plugin accepts the following options:

multilingual

Property Type Required Description
phrases NestedPhrases Yes Object containing locale-keyed phrase objects
defaultLocale 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.

const polyglot = request.polyglot();
const message = polyglot.t('greeting.hi');

request.availableLocales

String containing comma-separated list of available locales.

console.log(request.availableLocales); // "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:

  1. Parse Accept-Language header
  2. Match against available locales (exact match first, then language family)
  3. Fall back to defaultLocale
  4. If no default locale, return key-based responses

Example Accept-Language header processing:

  • en-US,en;q=0.9,it;q=0.8 → Prefers en-US, falls back to en, then it
  • pt-BR,pt;q=0.9 → Prefers pt-BR, falls back to pt

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:get

The 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

Building

npm run build          # Compile TypeScript
npm run build:clean    # Clean build and rebuild

Testing

The project uses Node.js built-in test runner with comprehensive test coverage:

npm test               # Build and run all tests

Test coverage includes:

  • Locale detection and matching algorithms
  • Fallback behavior for unsupported locales
  • Nested phrase structure support
  • Malformed Accept-Language header 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 issues

Project Structure

├── src/
│   ├── plugin.ts      # Main plugin implementation
│   └── util.ts        # Locale matching utilities
├── test/              # Test files
├── example/           # Working example application
└── dist/              # Compiled output

Continuous 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

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for your changes
  4. Ensure all tests pass and code follows style guidelines
  5. Submit a pull request

License

Licensed under MIT.