I'm having some trouble with React, i18next and Webpack. I've tried many solutions, but none of them worked. When I try to build my application, it builds successfully. But, when I try to open it, the console shows an error. My webpack.config and the error stacktrace are below.
const webpack = require('webpack');
const path = require('path');
const htmlWebpackPlugin = require("html-webpack-plugin")
const miniCSSExtractPlugin = require("mini-css-extract-plugin")
const terserPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const cleanWebpackPlugin = require("clean-webpack-plugin")
const i18nPlugin = require("i18n-webpack-plugin")
const options = require("../src/controllers/i18n").options
const locales = require("../src/controllers/i18n/locales")
options.backend.loadPath = "." + options.backend.loadPath
const config = {
mode: "production",
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: "./",
filename: 'bundle.js'
},
resolve: {
extensions: [" ", ".js", ".jsx"],
alias: {
"@components": path.resolve(__dirname, "../src/components"),
"@views": path.resolve(__dirname, "../src/views")
}
},
optimization: {
minimizer: [
new terserPlugin({
cache: true,
parallel: true,
include: /\.(js|jsx)$/
}),
new OptimizeCSSAssetsPlugin({})
]
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader"
}, {
loader: "i18next-loader"
}]
},
{
test: /\.css$/,
use: [
miniCSSExtractPlugin.loader,
{
loader: "css-loader",
}
]
}, {
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'file-loader?name=images/[hash].[ext]',
options: {
name: "assets/images/[hash].[ext]"
}
}]
}, {
test: /\.(ttf|woff(2)|eof|svg)$/,
use: [{
loader: "file-loader",
options: {
name: "assets/fonts/[hash].[ext]",
}
}]
}
],
},
plugins: [
new htmlWebpackPlugin({
template: path.join(__dirname, "..", "public", "index.html")
}),
new miniCSSExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new cleanWebpackPlugin("../dist/*", {
root: __dirname + "../",
allowExternal: true
}),
new i18nPlugin(locales,{
functionName: "t",
nested: true
},true)
]
};
module.exports = config;
The npm run build runs normally, no errors about i18next. Then, when I open the application, I got this error:
bundle.js:33 TypeError: r.getFixedT is not a function
at L (bundle.js:12)
at bundle.js:12
at Xo (bundle.js:33)
at Ia (bundle.js:33)
at qi (bundle.js:33)
at $i (bundle.js:33)
at jl (bundle.js:33)
at Cl (bundle.js:33)
at Pl (bundle.js:33)
at Ji (bundle.js:33)
Hope somebody can helps me.
I've found what was the problem. The i18next documentation says that I should run the init inside the webpack.config.js
.
My initial problem was loading the locales. The i18n couldn't find the files after the build, because the webpack doesn't recognize the i18n-xhr-backend
requiring the .json
files. After the build, there was no translation files. Then, I've tried to let webpack
deal with the i18n, and I got another problem, on next paragraph.
The React needs the i18n instance to be add to the i18nextProvider
. But, the way that I was doing it, there was no i18n instance to refence inside React. Then, it coudn't find the translation function or anything else. I've also found the i18nWebpackPlugin, but it also didn't solved my problem, because it don't give access to the i18n
instance inside react. At the end, I had two unsolved problems.
My solution was quite simple. I've created a new i18n
config to the development
env and let the webpack deal with the .json
, copying it to a new folder after build. I'll let my webpack and i18n config files below. The steps were:
i18n.init()
back to i18n/index.js
App.js
againNow, everything works like a charm.
OBS: To webpack recognize the
.json
files, you need to import it somewhere. I did inside theresources.js
file.
webpack.prod.config.js
const webpack = require("webpack");
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const miniCSSExtractPlugin = require("mini-css-extract-plugin");
const terserPlugin = require("terser-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const cleanWebpackPlugin = require("clean-webpack-plugin");
const config = {
mode: "production",
output: {
path: path.resolve(__dirname, "../dist"),
publicPath: "./",
filename: "bundle.js"
},
resolve: {
extensions: [" ", ".js", ".jsx"],
alias: {
"@components": path.resolve(__dirname, "../src/components"),
"@views": path.resolve(__dirname, "../src/views"),
"@static": path.resolve(__dirname, "../src/static")
}
},
optimization: {
minimizer: [
new terserPlugin({
cache: true,
parallel: true,
include: /\.(js|jsx)$/
}),
new OptimizeCSSAssetsPlugin({})
]
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader"
}
]
},
{
test: /\.css$/,
use: [
miniCSSExtractPlugin.loader,
{
loader: "css-loader"
}
]
},
{
test: /\.(png|jpg|gif|ico)$/,
use: [
{
loader: "file-loader?name=images/[hash].[ext]",
options: {
name: "assets/images/[hash].[ext]"
}
}
]
},
{
test: /\.(ttf|woff2?|eo(f|t)|svg)$/,
use: [
{
loader: "file-loader",
options: {
name: "assets/fonts/[hash].[ext]"
}
}
]
},
{
test: /\.(json)$/,
type: "javascript/auto",
use: [
{
loader: "file-loader",
options: {
name: "[folder]/[name].[ext]",
outputPath: "assets/locales/"
}
}
]
}
]
},
plugins: [
new htmlWebpackPlugin({
template: path.join(__dirname, "..", "public", "index.html")
}),
new miniCSSExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new cleanWebpackPlugin("../dist/*", {
root: __dirname + "../",
allowExternal: true
})
]
};
module.exports = config;
i18n/index.js
const i18n = require("i18next");
const initReactI18next = require("react-i18next").initReactI18next;
const langDetector = require("i18next-browser-languagedetector");
const backend = require("i18next-xhr-backend");
const moment = require("moment");
const resources = require("../../static/locales");
/*
Other codes...
*/
i18n.use(langDetector).use(initReactI18next);
var options;
switch (process.env.NODE_ENV) {
case "test":
options = {
whitelist: ["en", "pt"],
fallbackLng: "en",
resources,
ns: "translation",
defaultNS: "translation",
interpolation: {
format: function(value, format, lng) {
if (value instanceof Date) return moment(value).format(format);
return value.toString();
}
}
};
break;
case "production":
i18n.use(backend);
options = {
whitelist: ["en-US", "pt-BR"],
fallbackLng: {
pt: ["pt-BR"],
en: ["en-US"],
default: ["en"]
},
ns: ["button", "common", "lng", "info"],
defaultNS: "common",
backend: {
loadPath: "./assets/locales/{{lng}}/{{ns}}.json"
},
detection: {
order: ["querystring", "cookie", "navigator", "localStorage"]
},
lookupQuerystring: "lng",
caches: ["localStorage", "cookie"],
react: {
wait: true
},
interpolation: {
format: function(value, format, lng) {
if (format === "uppercase") return value.toUpperCase();
if (value instanceof Date) return moment(value).format(format);
return value;
}
}
};
break;
case "development":
i18n.use(backend);
options = {
whitelist: ["en-US", "pt-BR"],
fallbackLng: {
pt: ["pt-BR"],
en: ["en-US"],
default: ["en"]
},
ns: ["button", "common", "lng", "info"],
defaultNS: "common",
backend: {
loadPath: "./src/static/locales/{{lng}}/{{ns}}.json"
},
detection: {
order: ["querystring", "cookie", "navigator", "localStorage"]
},
lookupQuerystring: "lng",
caches: ["localStorage", "cookie"],
react: {
wait: true
},
interpolation: {
format: function(value, format, lng) {
if (format === "uppercase") return value.toUpperCase();
if (value instanceof Date) return moment(value).format(format);
return value;
}
}
};
break;
}
i18n.init(options);
module.exports = i18n;