0.1.0 • Published 5 months ago

fastify-i18next-plugin v0.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

fastify-i18next-plugin

Coverage Status neostandard javascript style

A simple plugin for using i18next with fastify.

Install

npm install fastify-i18next-plugin

Example

import fastify from 'fastify'
import i18nPlugin from 'fastify-i18next-plugin'
const app = fastify()

app.register(i18nPlugin, {
  // Languages to support (required)
  languages: ['en', 'it'],
  resources: {
    // Lang
    en: {
      // i18next namespace
      messages: {
        // Key and message
        hello: 'Hello {{name}}'
      }
    },
    it: {
      messages: {
        hello: 'Ciao {{name}}'
      }
    }
  }
})

app.get('/en', (request, reply) => {
  // Set language of the messages returned from request.t()
  request.setLanguage('en')
  return request.t('messages:hello', { name: 'Daria' })
})

app.get('/it', (request, reply) => {
  // Set language of the messages returned from request.t()
  request.setLanguage('it')
  return request.t('messages:hello', { name: 'Daria' })
})

// Hello Daria
app.inject('/en')
  .then(res => console.log(res.payload))

// Ciao Daria
app.inject('/it')
  .then(res => console.log(res.payload))

Loading JSON resources from disk

Providing a path pattern to opts.loadPath will allow you load JSON locale files from disk. The path must include {{ns}} and {{lng}} patterns so that the namespace and language of the resource can be determined.

For example, given the following structure:

└ locales/
    ├ en/
    │ ├ messages.json
    │ └ greetings.json
    └ it/
      ├ messages.json
      └ greetings.json

The path ./locales/{{lng}}/{{ns}}.json will load the messages.json and greetings.json files in the en and it directories, and add them to the messages and greetings namespaces for English and Italian.

Eg:

// ./locales/it/greetings.json
{
  "nested": {
    "hello": "Ciao!"
  }
}
import { join } from 'node:path'
import fastify from 'fastify'
import i18nPlugin from 'fastify-i18next-plugin'
const app = fastify()

await app.register(i18nPlugin, {
  languages: ['en', 'it'],
  // Will load: ./locales/en/greetings.json, ./locales/it/greetings.json
  // setting the langues from the path and namespace from the filename
  loadPath: join(import.meta.dirname, './locales/{{lng}}/{{ns}}.json')
})

// Ciao!
console.log(app.t('greetings:nested.hello', 'it'))

Loading resources within another plugin

You can still load resources after the plugin is registered using the fastify.addI18nResource() and fastify.loadI18nResources() functions.

import fastify from 'fastify'
import i18nPlugin from 'fastify-i18next-plugin'
const app = fastify()

app.register(i18nPlugin, {
  languages: ['en', 'it']
})

app.register((instance) => {
  instance.addI18nResource('en', 'greeting', { hello: 'Hello' })
  instance.addI18nResource('it', 'greeting', { hello: 'Ciao' })
  instance.loadI18nResources('./locales/{{lng}}.{{ns}}.json')
  instance.log.info(instance.t('greeting:hello', 'en'))
})

Adding i18next formatters

Use the fastify.addI18nFormatter() and fastify.addI18nCachedFormatter() functions to add i18next formatters. See the i18next formatting documention for more info about their usage.

import fastify from 'fastify'
import i18nPlugin from 'fastify-i18next-plugin'
const app = fastify()

await app.register(i18nPlugin, {
  languages: ['en', 'it'],
  resources: {
    en: {
      messages: {
        hello: 'Hello {{name, upper}}',
        helloAll: 'Hello {{names, list}}'
      }
    }
  }
})

app.addI18nFormatter('upper', (val) => val.toUpperCase())
app.addI18nCachedFormatter('list', (lng) => {
  const formatter = new Intl.ListFormat(lng, {
    style: 'long',
    type: 'conjunction',
  })
  return (val) => formatter.format(val)
})

// Hello DARIA
app.t('messages:hello', 'en', { name: 'Daria' })

// Hello Niall, James, and Tim
app.t('messages:helloAll', 'en', { names: ['Niall', 'James', 'Tim'] })

Detecting the language for the request

Setting the languageDetectors option will add a hook to detect the language from any or all of the following: a URL path parameter, query string, cookie, session or accept header. The detectors will run in the order they appear in the languageDetectors array and stop as soon as a supported language is found.

When using either the session or cookie detectors, the detected language will be persisted to the session or cookie, even if it was detected by a different detector, eg: the query string detector.

import fastify from 'fastify'
import cookiePlugin from '@fastify/cookie'
import i18nPlugin from 'fastify-i18next-plugin'
const app = fastify()

app.register(cookiePlugin)
app.register(i18nPlugin, {
  fallbackLng: 'en',
  languages: ['en', 'it', 'cy'],
  languageDetectors: ['query', 'cookie']
})

app.get('/', (request, reply) => {
  return request.lng
})

// 'en', fallback language
app.inject('/').then(res => console.log(res.payload))

// 'it', detected from lng cookie
app.inject({
  path: '/',
  cookies: { lng: 'it' }
})
  .then(res => console.log(res.payload))

// 'cy', detected from lng query, the cookie won't be checked as it is lower
// priority
app.inject({
  path: '/',
  query: { lng: 'cy' },
  cookies: { lng: 'it' }
})
  .then(res => console.log(res.payload))

Plugin options

  • fallbackLng (String): The default language code to use. Default: the first language in the languages array.
  • languages (Array): List of language codes to support, trying to change to a language not in the list will instead set the fallback language.
  • resources (Object): Object map of locale messages, in the format { lang: { namespace: { key: message } } }.
  • loadPath (String): Path to load JSON file resources from, namespace and language are determined from path parameters, eg: ./locales/{{lng}}/{{ns}}.json.
  • languageDetectors (Array): A list of language detectors to use, will be run in the supplied order until a supported language is detected. Can be one to many of the following:
    • "query": Detect language from query string.
    • "param": Detect language from parsed URL path parameter (eg: request.params.lng).
    • "accept": Detect language from the language accept header. Note: the @fastify/accepts plugin must be loaded to use this detector.
    • "cookie": Detect language from cookie value (will also persist any detected language to this cookie). Note: the @fastify/cookie plugin must be loaded to use this detector.
    • "session": Detect language from session value (will also persist any detected language to this sesion property). Note: the @fastify/session plugin must be loaded to use this detector.
  • queryKey (String): The query value to use for the query language detector. Default: lng.
  • param (String): The path paramter to use for the param language detector. Default: lng.
  • sessionKey (String): The session property to use for the session language detector. Default: lng.
  • cookieName (String): The cookie to use for the cookie language detector. Default: lng.
  • cookieOpts (Object): The cookie options to use when setting the language cookie. See the @fastify/cookie options for more details. Default: { path: '/', sameSite: true, httpOnly: true }
  • decorateLocals (Boolean): Decorate reply.locals with language properties and t() for use in templates, for example when using @fastify/view.

Server decorators

app.t('namespace:key') // Get a message for the fallback language
app.t('namespace:key', opts) // Get a message with options object, eg data to interpolate
app.addI18nFormatter('name', func) // Add an i18next formatter
app.addI18nCachedFormatter('name', func) // Add an i18next cached formatter
app.addI18nResource('lang', 'namespace', { key: 'Message' }) // Add new locales messages
app.loadI18nResources('./locales/{{lng}}.{{ns}}.json') // Load JSON locales files from a path pattern

Request decorators

app.get('/', (request, reply) => {
  request.setLanguage('en') // Sets language to English
  request.lng // The currently set language code
  request.lngDir // Direction of current language, eg: 'ltr', 'rtl'
  request.lngs // The currently support languages, eg: ['en', 'it']
  request.t('messages:hello') // Get a message for the current language
  request.i18nKeyExists('messages:hello') // Returns true if given key has a value
})

reply.locals properties (if decorateLocals is true)

app.get('/', (request, reply) => {
  reply.locals.t('messages:hello') // same as request.t
  reply.locals.exists('messages:hello') // same as request.i18nKeyExists
  reply.locals.lngs // same as request.lngs
  reply.locals.lng // same as request.lng
  reply.locals.lngDir // same as request.lngDir
  reply.locals.language // same as request.lng
  reply.locals.languageDir // same as request.lngDir
})
0.1.0

5 months ago