Upload translations from CDN - next.js

Is it possible to get translations from cdn (s3 bucket)?
Config:
{
i18n: {
fallbackLng: 'en',
defaultLocale: 'empty',
locales: [
'empty',
'en',
'de',
'ee',
'es',
'fi',
'fr',
'gb',
'it',
'ko',
'lt',
'lv',
'nl',
'pl',
'pt',
'ru',
'sv',
'tr',
'zh',
],
localeDetection: false,
},
react: {
useSuspense: false,
},
serverLanguageDetection: true,
backend: {
loadPath: 'https://cdn-domain.com/{{ns}}/{{lng}}',
requestOptions: {
cache: 'default',
credentials: 'same-origin',
mode: 'no-cors',
},
},
reloadOnPrerender: true,
debug: true,
serializeConfig: false,
use: [I18NextHttpBackend],
};
Error
i18next::backendConnector: loading namespace translation for language en failed [Error: ENOENT: no such file or directory, open 'https://cdn-domain.com/translation/en'

Related

The next-i18next don't bring the data from an endpoint

In a Nextjs project using next-i18next, I'm trying to read language json file from a cms but my configuration is not working.
This is my 'next-i18next.config.js':
const I18NextHttpBackend = require('i18next-http-backend/cjs');
module.exports = {
debug: process.env.NODE_ENV === 'development',
reloadOnPrerender: process.env.NODE_ENV === 'development',
i18n: {
defaultLocale: 'es',
locales: ['es'],
localeDetection: false,
},
ns: ['translation'],
defaultNS: 'translation',
react: {
useSuspense: true,
},
backend: {
loadPath: 'http://localhost:3001/locales/{{lng}}/{{ns}}.json',
requestOptions: {
cache: 'default',
credentials: 'same-origin',
mode: 'no-cors',
},
},
serializeConfig: false,
use: isBrowser ? [I18NextHttpBackend] : [],
}
I'm following the examples that I found in the official repository of the next-i18next and in the i18next-http-backend next example.
The endpoint has an object like:
{"title": "My title"}
This is the debug:
{
"debug": true,
"initImmediate": true,
"ns": [
"translation"
],
"defaultNS": "translation",
"fallbackLng": [
"es"
],
"fallbackNS": false,
"supportedLngs": false,
"nonExplicitSupportedLngs": false,
"load": "currentOnly",
"preload": false,
"simplifyPluralSuffix": true,
"keySeparator": ".",
"nsSeparator": ":",
"pluralSeparator": "_",
"contextSeparator": "_",
"partialBundledLanguages": false,
"saveMissing": false,
"updateMissing": false,
"saveMissingTo": "fallback",
"saveMissingPlurals": true,
"missingKeyHandler": false,
"missingInterpolationHandler": false,
"postProcess": false,
"postProcessPassResolved": false,
"returnNull": true,
"returnEmptyString": true,
"returnObjects": false,
"joinArrays": false,
"returnedObjectHandler": false,
"parseMissingKeyHandler": false,
"appendNamespaceToMissingKey": false,
"appendNamespaceToCIMode": false,
"interpolation": {
"escapeValue": false,
"prefix": "{{",
"suffix": "}}",
"formatSeparator": ",",
"unescapePrefix": "-",
"nestingPrefix": "$t(",
"nestingSuffix": ")",
"nestingOptionsSeparator": ",",
"maxReplaces": 1000,
"skipOnVariables": true
},
"errorStackTraceLimit": 0,
"localeExtension": "json",
"localePath": "./public/locales",
"localeStructure": "{{lng}}/{{ns}}",
"react": {
"useSuspense": true
},
"reloadOnPrerender": true,
"serializeConfig": false,
"use": [
null
],
"backend": {
"loadPath": "http://localhost:3001/locales/{{lng}}/{{ns}}.json",
"requestOptions": {
"cache": "default",
"credentials": "same-origin",
"mode": "no-cors"
},
"addPath": "/locales/add/{{lng}}/{{ns}}",
"allowMultiLoading": false,
"reloadInterval": false,
"customHeaders": {},
"queryStringParams": {},
"crossDomain": false,
"withCredentials": false,
"overrideMimeType": false
},
"lng": "es",
"defaultLocale": "es",
"locales": [
"es"
],
"localeDetection": false,
"resources": {
"es": {
"translation": {}
},
"en": {
"translation": {}
},
"ca": {
"translation": {}
}
},
"ignoreJSONStructure": true
}
Thanks.
If you want to use always your custom loadPath (client and server side), you need to do this:
module.exports = {
debug: process.env.NODE_ENV === 'development',
reloadOnPrerender: process.env.NODE_ENV === 'development',
i18n: {
defaultLocale: 'es',
locales: ['es', 'en'],
localeDetection: false,
},
ns: ['translation'],
defaultNS: 'translation',
react: {
useSuspense: true,
},
backend: {
loadPath: 'http://localhost:3000/api/{{ns}}/{{lng}}',
requestOptions: {
cache: 'default',
credentials: 'same-origin',
mode: 'no-cors',
}
},
serializeConfig: false,
use: [I18NextHttpBackend],
}
You need to also conditionally set the backend options:
module.exports = {
debug: process.env.NODE_ENV === 'development',
reloadOnPrerender: process.env.NODE_ENV === 'development',
i18n: {
defaultLocale: 'es',
locales: ['es', 'en'],
localeDetection: false,
},
ns: ['translation'],
defaultNS: 'translation',
backend: isBrowser ? {
loadPath: 'http://localhost:3000/locales/{{lng}}/{{ns}}.json',
requestOptions: {
cache: 'default',
credentials: 'same-origin',
mode: 'no-cors',
},
} : undefined,
serializeConfig: false,
use: isBrowser ? [I18NextHttpBackend] : [],
}

how to remove the /[ defolt lang tag] from url using nextjs?

How do I make a redirect in next.js and i18n? I want it not to be possible to go to /en en to throw either on / or on 404.
dont path : /en
My next.config.js
i18n: {
// strategy: 'prefix_except_default',
defaultLocale: 'en',
localeDetection: false,
locales: ['en', 'ru', 'uk'],
},
async rewrites() {
return [{
source: '/en',
destination: '/404',
},
]
}
async redirects() {
return [
{
source: '/en',
destination: '/404',
permanent: false,
locale: false,
},
]
},

webpack - MiniCssExtractPlugin set absolute path for .css file

remove relative URLs and use absolute URLs in CSS imports.
webpack code -
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
bundle file in html is
this is relative path but i want to set abosolute path to this .css url from webpack config file using MiniCssExtractPlugin
Impact
An attacker may trick browsers into importing JavaScript or HTML code as a stylesheet. This has been shown to enable a number of different attacks, including
cross-site scripting (XSS) and exfiltration of CSRF tokens.
webpack.config file
/* eslint-disable global-require */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackCleanupPlugin = require('webpack-cleanup-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpackBundleAnalyzer = require('webpack-bundle-analyzer');
process.env.NODE_ENV = 'production';
module.exports = {
mode: 'production',
target: 'web',
entry: {
bundle: path.join(__dirname, 'src', 'index.jsx'),
},
output: {
path: path.join(__dirname, 'build'),
publicPath: '/',
filename: '[name].[contenthash].js',
},
devtool: 'source-map',
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules\/(?!objectmodel).+/,
loader: 'babel-loader',
resolve: {
extensions: ['.js', '.jsx'],
},
},
{
test: /\.(s[ac]ss|css)$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { sourceMap: true },
},
{
loader: 'sass-loader',
options: { sourceMap: true },
},
{
loader: 'postcss-loader',
options: {
plugins: () => [require('cssnano')],
sourceMap: true,
},
},
],
},
{
test: /\.(pdf|ico|png|jpg|svg)$/,
use: 'file-loader?name=assets/[name].[ext]',
},
],
},
plugins: [
new WebpackCleanupPlugin(),
new webpackBundleAnalyzer.BundleAnalyzerPlugin({ analyzerMode: 'static' }),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
new HtmlWebpackPlugin({
filename: 'index.html',
favicon: 'src/favicon.ico',
template: path.join(__dirname, 'src', 'index.html'),
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
],
};
You can change your output.publicPath to the absolute prefix, it will change all of your assets (in html, in css) to be prefixed with it.
output: {
path: path.join(__dirname, 'build'),
publicPath: 'https://my-cdn.com/',
filename: '[name].[contenthash].js',
},

Webpack not including css in HTML build

I am new to webpack and react, i downloaded one github project which suits my requirement and when i start my server, i see that css is not getting included in build html file.
'use strict';
const fs = require('fs');
const path = require('path');
const resolve = require('resolve');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const ManifestPlugin = require('webpack-manifest-plugin');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const publicPath = '/';
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
},
},
];
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
entry: [
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
paths.appBuild + '/static/css/main.chunk.css',
],
output: {
pathinfo: true,
filename: 'static/js/bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
},
optimization: {
splitChunks: {
chunks: 'all',
name: false,
},
runtimeChunk: true,
},
resolve: {
modules: ['node_modules'].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
'actions': path.resolve(__dirname, '../src/actions'),
'constants': path.resolve(__dirname, '../src/constants'),
'containers': path.resolve(__dirname, '../src/components/containers'),
'commons': path.resolve(__dirname, '../src/components/common'),
'reducers': path.resolve(__dirname, '../src/reducers'),
'domains': path.resolve(__dirname, '../src/domains'),
'libs': path.resolve(__dirname, '../src/libs')
},
plugins: [
PnpWebpackPlugin,
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
},
},
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: false,
sourceMaps: false,
},
},
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
}),
},
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader'),
},
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
{
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml
}),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
new ModuleNotFoundPlugin(paths.appPath),
new webpack.DefinePlugin(env.stringified),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrc,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
i am just clueless where to edit above code to solve my problem, i also observed that css in build folder has '#' at the beginning
Project build folder image
I think you'll need to add a css loader to your plugins. You'll have to
npm install css-loader style-loader --save-dev
Add a plugin to to your webpack.config.js
module:{
rules:[
// other plugins
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
}
Now webpack should bundle your css properly. Note you have to make sure you have imported you css file into your application !
import '../app.css';
Run webpack again and you should see the css inserted in s style tag in the header of your html page

Wrong CSS rules order compiled with webpack

I have an issue with css order that compiles with webpack.
I currently use these packages in dependencies:
"css-loader": "^0.28.4",
"style-loader": "^0.18.2",
"sass-loader": "^6.0.6",
"sass-resources-loader": "^1.3.0",
"webpack": "^3.5.5",
Here is my webpack.config.js
const { alias } = require('./webpack/common.js');
const path = require('path');
const webpack = require('webpack');
const Dashboard = require('webpack-dashboard');
const DashboardPlugin = require('webpack-dashboard/plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const nodeEnv = process.env.NODE_ENV || 'development';
const isProd = nodeEnv === 'production';
const sourcePath = path.join(__dirname, './src');
const staticPath = path.join(__dirname, './dist');
const commonCssOptions = {
sass: {
loaders: ['sass-loader', 'sass-resources-loader'],
},
context: path.resolve(__dirname, '.'),
output: {
path: 'dist',
},
};
const plugins = [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity,
filename: 'vendor.bundle.js',
}),
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify(nodeEnv) },
}),
new webpack.NamedModulesPlugin(),
new ExtractTextPlugin({ filename: 'css/bundle.css', disable: false, allChunks: true }),
new webpack.ContextReplacementPlugin(
/moment[/\\]locale/,
/(en-gb)\.js/
),
];
if (isProd) {
plugins.push(
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false,
options: commonCssOptions,
})
);
} else {
const dashboard = new Dashboard();
plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin({
options: commonCssOptions,
}),
new DashboardPlugin(dashboard.setData)
);
}
module.exports = {
devtool: isProd ? false : 'cheap-module-source-map',
entry: {
js: './src/index.js',
vendor: [
'babel-polyfill',
'bootstrap-loader',
'classnames',
'react',
'react-dom',
'react-redux',
'redux',
'react-router',
'react-router-dom',
// 'moment',
],
},
output: {
path: staticPath,
publicPath: '/',
filename: '[name].bundle.js',
},
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
use: {
loader: 'file-loader',
query: {
name: '[name].[ext]',
},
},
},
{
test: /\.s?css$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true,
localIdentName: '[name]__[local]_[hash:base64:5]',
},
},
{
loader: 'sass-loader',
options: {
includePaths: [
path.join(__dirname, './components-lib/src/assets/styles'),
],
},
},
{
loader: 'sass-resources-loader',
options: {
resources: [
'./components-lib/src/assets/styles/_variables.scss',
'./components-lib/src/assets/styles/_mixins.scss',
],
},
},
'postcss-loader',
],
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},
{
test: /\.(woff|woff2|eot|ttf|svg)(\?.*$|$)/,
loader: 'file-loader',
query: {
name: '[name].[ext]',
},
},
],
},
resolve: {
extensions: ['.webpack-loader.js', '.web-loader.js', '.loader.js', '.jsx', '.js'],
modules: [
'node_modules',
sourcePath,
],
alias,
},
plugins,
devServer: {
contentBase: './src',
historyApiFallback: true,
host: '0.0.0.0',
port: 3000,
compress: isProd,
inline: !isProd,
hot: !isProd,
quiet: true,
stats: {
assets: true,
children: false,
chunks: false,
hash: false,
modules: false,
publicPath: false,
timings: true,
version: false,
warnings: false,
colors: {
green: '\u001b[32m',
},
performance: {
hints: false,
},
},
},
externals: {
cheerio: 'window',
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true,
},
};
On initial load I have wrong css order
But on hot reload the order becomes correct
My component library is a git submodule (if it is important)
I've noticed that including HotModuleReplacementPlugin will change CSS order on occasion, still trying to figure out why.
I think because of the way things get re-written, the ordering is bound to change i.e. the new stuff will be at the bottom. I've also noticed Webpack not being able to guarantee the order of css. I wasn't able to find a 'webpack' solution and i'm not sure if there is one. Probably bot what you wanted to hear, sorry!
The only way i've solved it was by either using smaccs/BEM notation and/or ensuring writing css rarely/never over-writes other css. For example, if you need to use a 'modifier' to change a background from white to red, then that is actually two modifiers and the default 'base' class doesn't have a background set at all. This way you can guarantee ordering doesn't matter. TBH, this turned out to be a more readable and maintainable way of writing css, but i digress!

Resources