I18next-Scanner in Grafana Framework React App Not Generating Keys in JSON Translation Files for Internationalization

Hello everybody,

I am adding i18next-scanner to my Grafana plugin after implementing the i18next internationalization . However, when I run the command npm run node i18n.config.js, it does not generate the required keys automatically for me in the translation JSON files. Does anyone know what I might be missing?

Here is my i18n.ts

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import en from './locales/en/en.json';
import de from './locales/de/de.json';

    debug: true,
    fallbackLng: 'en',
    resources: {
      en: {
        translation: en,
      de: {
        translation: de,
    ns: ['translation'],

export default i18n;

My i18n.config.js

const scanner = require('i18next-scanner');

  input: ['./src/**/*.ts', './src/**/*.tsx'],
  output: './src/locales/{{lng}}/{{lng}}.json',
  options: {
    debug: true,
    removeUnusedKeys: true,
    sort: true,
    lngs: ['en', 'de'],
    nsSeparator: ':',
    keySeparator: '.',
  defaultValue: '',

My script at package.json to run it:

"scripts": {
    "i18n:scan": "node i18n.config.js",

The translations files are at:
./src/locales/en/en.json for english
./src/locales/de/de.json for german and so on…

At my component .tsx file, I am using the translation like:

import React, { useState } from 'react';
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from 'types';

import i18n from 'i18n';
import { useTranslation, Trans } from 'react-i18next';

const lngs: any = {
  en: { nativeName: 'English' },
  de: { nativeName: 'Deutsch' },

interface Props extends PanelProps<SimpleOptions> {}
export const SimplePanel: React.FC<Props> = ({ options, data, width, height }) => {
  const { t } = useTranslation();

  const [count, setCounter] = useState(0);
  return (
            width: ${width}px;
            height: ${height}px;
        <header className="App-header">
            <p>0- {t('header.part0')}</p>
              <p>1- {t('header.part1')}</p>
            {Object.keys(lngs).map((lng) => (
                  fontWeight: i18n.resolvedLanguage === lng ? 'bold' : 'normal',
                onClick={() => {
                  setCounter(count + 1);
            {/* the variable must be count */}
            <p>2- {t('header.part2')}</p>
            <i>{t('counter', { count })}</i>

When I run the script, it does not add any keys to my JSON translation files. Does anyone know why? I do not receive any error messages; it is as if it’s executed but does not find any tasks to perform, and I receive no warnings or errors. I would appreciate it if someone could point out what I am missing.


I found a solution. I could solve it changing the i18n.config.js file to: i18next-scanner.config.js and also the config inside like:

// i18next-scanner.config.js

module.exports = {
  input: [
    'src/**/*.{ts,tsx}', // Specify the file types to scan (e.g., .js and .jsx)
  output: './',
  options: {
    debug: true, // Set to true for debugging output
    removeUnusedKeys: true, // Remove unused keys from translations files
    sort: true, // Sort keys in translation files
    func: {
      // The function names used for translation keys
      list: ['t', 'i18next.t', 'i18n.t'],
      extensions: ['.ts', '.tsx'],
    lngs: ['en', 'de'], // List the languages you want to support
    defaultLng: 'en', // The default language
    resource: {
      // The path where translation files will be saved
      loadPath: '.src/locales/{{lng}}/{{ns}}.json',
      savePath: '.src/locales/{{lng}}/{{ns}}.json',
  ns: ['translation'], // Namespace for your translations

I am running the command in the cli like:

npm run i18next-scanner

I removed/replaced the old script in the package.json posted above.

The setup above worked. However, the default behavior of i18next-scanner when it runs, it replaces the entire translation file with the newly extracted keys and their values.
To avoid this default behavior you need to add a defaultValue at i18next-scanner.config.js like:

defaultValue: ''

then it will not replace an existing key preserving the old value.