Gravity ships with localization support and two languages (English and Spanish) out of the box. This section will example how to use localization on the server side.

Refer to the client-side localisation section to learn how to translate the UI.

Localization is managed with the i18n package, which can be used to translate individual requests depending on the language provided by the client using the Accept-Language header.

Locale Files

Locale files are stored inside the /locales folder, and each language has its own folder of .json files. The locales are split up based on entities in the application to match the same structure as the models, views and controllers.

All .json files inside each locale folder are automatically imported and combined using the i18n helper.config() method, so you can add more JSON files without explicitly importing them anywhere.


The locale files are simple JSON files containing the strings. The keys are always the same (in English) as these are referenced in the code. The string changes depending on the language.

// en_account.json
  "create": {
    "denied": "Registration denied",
    "duplicate": "You have already registered an account",
    "duplicate_child": "You already have an account registered with this email address. Please enter your original password to continue"

// es_account.json
"create": {
  "denied": "Registro denegado",
  "duplicate": "Ya has registrado una cuenta",
  "duplicate_child": "Ya tienes una cuenta registrada con esta dirección de correo electrónico. Por favor, introduce tu contraseña original para continuar"

Adding More Locales

Pro tip – ask ChatGPT to translate the JSON files to new languages.

  1. Create a new folder inside /locales eg. /fr

  2. Add the new locale to the locales array in /helper/i18n


  defaultLocale: 'en',
  locales: ['en', 'es', 'fr'], // add new locale here
  updateFiles: false,
  objectNotation: true,
  staticCatalog: translations


Performing Translations

Translations can be performed in two ways:

  1. Using the i18n package and setting the locale

  2. Using the res object.

// translate
const i18n = require('i18n'); 

function translateWithi18n(req, res){

 // translate using the i18n package
  return res.status(200).send({ message: i18n.__('account.plan.updated'));


function translateWithRes(req, res){
  // translate using res
  return res.status(200).send({ message: res.__('account.plan.updated'));


You can use the locale column on the user table when neither option is available, for example, in a background job worker.

Translating Controllers

To translate inside a controller method with the res object available, use the res.__ method.

Translating Models & Helpers

If the res object is unavailable - i.e. you're translating a model or helper file, you can pass the req.locale var to the method.

function controller(req, res){

  convertToMonthName(2, req.locale);

function convertToMonthName(month, locale){

  locale && i18n.setLocale(locale);
  const monthNames = i18n.__('global.months').split(',')
  return monthNames[month-1];


Handling Plurals

The i18nHelper has a method for handling plurals for you. You just need to structure your JSON as follows, and pass the translation key and number to the helper.

 "sent": {
   "one": "Invite sent",
   "other": "Invites sent"

i18nHelper.plural('invite.sent', emails.length);


Emails are handled slightly differently because the content is stored in the database, not the locale files. Each email in the email table has a locale column so the content can be localised. You pass the locale as part of the data prop to the mail method.

await mail.send({

 to: userData.email,
 locale: req.locale,
 template: 'new_account'

Last updated