# Creating own Plugins

i18next comes with a lot of modules to enhance the features available. There are modules to:

* load resources, eg. via xhr or from filesystem (node.js)
* cache resources on client, eg. localStorage
* detect user language by querystring, navigator, cookie, ...
* post processors to further manipulate values, eg. to add sprintf support

The plugins need to support following APIs:

**HINT:** You can provide a singleton or a prototype constructor (prefered for supporting multiple instances of i18next).

## backend

Backend plugins are used to load data for i18next.

```javascript
{
  type: 'backend',
  init: function(services, backendOptions, i18nextOptions) {
    /* use services and options */
  },
  read: function(language, namespace, callback) {
    /* return resources */
    callback(null, {
      key: 'value'
    });

    /* if method fails/returns an error, call this: */
    /* callback(truthyValue, null); */
  },
  // or new since i18next v22.1.0
  // read: function(language, namespace) {
  //   return new Promise((resolve) => {
  //     resolve({
  //       key: 'value'
  //     })
  //   })
  // },

  // optional
  readMulti: function(languages, namespaces, callback) {
    /* return multiple resources - useful eg. for bundling loading in one xhr request */
    callback(null, {
      en: {
        translations: {
          key: 'value'
        }
      },
      de: {
        translations: {
          key: 'value'
        }
      }
    });

    /* if method fails/returns an error, call this: */
    /* callback(truthyValue, null); */
  },
  // or new since i18next-multiload-backend-adapter v2.1.0
  // readMulti: function(languages, namespaces) {
  //   return new Promise((resolve) => {
  //     resolve({
  //       en: {
  //         translations: {
  //           key: 'value'
  //         }
  //       },
  //       de: {
  //         translations: {
  //           key: 'value'
  //         }
  //       }
  //     })
  //   })
  // },

  // only used in backends acting as cache layer
  save: function(language, namespace, data) {
    // store the translations
  },

  create: function(languages, namespace, key, fallbackValue) { 
    /* save the missing translation */
  }
}
```

{% hint style="info" %}
Using `readMulti` is only supported when using the <https://github.com/i18next/i18next-multiload-backend-adapter>
{% endhint %}

## languageDetector

Language Detector plugins are used to detect language in user land.

```javascript
{
  type: 'languageDetector',
  async: true, // If this is set to true, your detect function receives a callback function that you should call with your language, useful to retrieve your language stored in AsyncStorage for example
  init: function(services, detectorOptions, i18nextOptions) { // optional since v22.3.0
    /* use services and options */
  },
  detect: function(callback) { // You'll receive a callback if you passed async true
    /* return detected language */
    // callback('de'); if you used the async flag
    return 'de';
  },
  // or new since v22.3.0
  // detect: async function () { // you can also return a normal Promise
  //   /* return detected language */
  //   return 'de';
  //   // or
  //   // return Promise.resolve('de')
  // },
  cacheUserLanguage: function(lng) { // optional since v22.3.0
    /* cache language */
  }
  // or new since v22.3.0, but i18next will not await for it... so it's basically a fire and forget
  // cacheUserLanguage: async function(lng) {
  //   /* await cache language */
  // }
}
```

## post processor

Post Processors are used to extend or manipulate the translated values before returning them in `t` function.

(Post Processors do not need to be prototype functions)

```javascript
{
  type: 'postProcessor',
  name: 'nameOfProcessor',
  process: function(value, key, options, translator) {
    /* return manipulated value */
    return value;
  }
}
```

## logger

Override the built in console logger.

(loggers do not need to be prototype functions)

```javascript
{
  type: 'logger',

  log: function(args) {},
  warn: function(args) {},
  error: function(args) {}
}
```

## formatter

Override the built in [Formatter](/translation-function/formatting.md).

```javascript
{
  type: 'formatter',
  init: function(services, detectorOptions, i18nextOptions) {},
  add: function(name, fc) {},
  addCached: function(name, fc) {},
  format: function(value, format, lng, options) {
    if (!format && value instanceof Date) {
      return value.toUTCString()
    }
  }
}
```

## Helpful tips

### Make sure to set the plugin type

If you do not set the plugin type, you may get an error like this.

`... No [plugin type] was added via i18next.use. Will not load resources.`

If you are creating a class for your plugin, you may set the type like in the following example (the following is an example if you are making a backend plugin):

```javascript
class Backend {
  constructor(services, backendOptions, i18nextOptions){

  }

  // other required methods;
  // ie. read, create, etc.
}
Backend.type = "backend";

export default Backend;
```

### Create a private method to initialize your plugin

The constructor of your plugin (if the plugin is of type `backend` or `languageDetector`) will be [called without arguments](https://github.com/i18next/i18next/issues/1379#issuecomment-571913660) if you use the plugin as a class. Using the plugin as a class looks like this:

```javascript
import i18n from "i18next";
import {
  initReactI18next
} from "react-i18next";
import i18nBackend from "my-custom-backend";

i18n
  .use(i18nBackend)
  .use(initReactI18next)
  .init({
    backend: {
      // custom options
    },
    // other options
  });
```

While using your plugin in this way, you may want to validate the `options` passed into the **backend** property of the `.init` method. A good way to validate them is to have a private method where you initialize your plugin.

```javascript
class Backend {
  constructor(services, backendOptions = {}, i18nextOptions = {}){
    this.init(services, backendOptions, i18nextOptions);
  }

  init(services, backendOptions, i18nextOptions){
    // Validate backendOptions
  }

  // other required methods;
  // ie. read, create, etc.
}
Backend.type = "backend";

export default Backend;
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.i18next.com/misc/creating-own-plugins.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
