i18n solution in thinkjs 3.0

Usage no npm install needed!

<script type="module">
  import thinkI18n from '';



Build Status Coverage Status npm


i18n solution in thinkjs 3.0, implement base on Jed, Moment and Numeral.


  • I18N solution cover translation, datetime and number display.
  • Support per locale custom format for datetime and number.
  • Easy to use, just defined your expect format and translation in each locale file and the library will help to apply locale settings on time, no more annoying locale switching!
  • Easy to debug
  • Locale switch process is Customizable to suit your case.
  • moment and numeral are isolated, so you can use them for free.

How to Use

npm install think-i18n --save

Configure extends.js

// thinkjs config/extend.js

const createI18n = require('think-i18n');
const path = require('path');

module.exports = [
    i18nFolder: path.resolve(__dirname, '../i18n'),
    localesMapping(locales) {return 'en';}


complete options

Locale Settings

Each locale settings is a javascript file. see example

module.exports = {
    app:, // if not passed in, __ will not be auto `assign` into `think-view` instance
    dateFormat, // optional
    numeralFormat, // optional
  • localeId: The unique name of your locale.

  • dateFormat: Will apply to moment.local(localeId, dateFormat); if empty you will get a moment instance with 'en' locale.

  • numeralFormat: Will apply numeral.locales[localeId] = numeralFormat; if empty you will get numeral instance with 'en' locale.

  • translation: Equivalent to Jed locale_data, if you use .po file, jed suggest use po2json which support jed format transform.

Controller and View (nunjucks)


You can get i18n instance or current locale in controller.

    async indexAction(){


If you used think-view , think-i18n will auto-inject an i18n instance into __ field, like this.assign('__', this.getI18n()).

{{ __('some key') }} translation
{{ __.jed.dgettext('domain', 'some key') }} translation in specify domain
{{ __.moment().format('llll') }} datetime format
{{ __.numeral(1000).format('currency') }} number format (see numberFormat.formats)

Complete Options

  • i18nFolder:string Directory path where you put all locale settings files

  • localesMapping:function(locales){return localeId;} Given a list of possible locales, return a localeId

  • getLocale default logic is to extract header['accept-language'] if set to falsy value. To get from url query locale, set to {by: 'query', name: 'locale'}. To get from cookie of locale, set to {by: 'cookie', name: 'locale'} To implement your own logic, function(ctx) {return locale;}

  • debugLocale set to value of localeId

  • jedOptions You can set domain and missing_key_callback, for details refer to jed doc.

    default value is {}, the final jed options will be

   Object.assign(jedOptions, {locale_data: <your locale translation>})

Some Thoughs Behine

Why combine all there libraries' i18n config into one? the answer is for transparent but most importantly, flexibility to compose you date, number and message's behavior per locale, for example, you have a website in China and want to provide English translation, but keep the chinese currency symbol, so you can compose english translation and chinese date and currency in your en.js locale setting.

// locale setting of en.js
module.exports = {
  localeId: 'en_ch',
  translation: require('../english.po.json'),
  dateFormat: require('../moment/en.json'),
  numeralFormat: require('../numeral/en.json')

We should always use customize format.

  • use __.moment().format('llll') instead of moment().format('YYYY-MM-dd HH:mm').
  • use __.numeral(value).format('customFormat') instead of numeral(value).format('00.00 ),


  • locale id must be lower case.
  • If you defined en locale, witch will override the default en locale in Numeral.
  • In case you also want to use moment and numeral in your system, we isolated the Moment and Numeral by use bundledDependencies in package.json.
  • Numeral doesn't not support per locale custom format, this is done using some tricks and you can config each locale's customFormat in config.numeralFormat.formats.