In my Next.js app, I have an i18n.config.js file in the root directory of my next.js app.
Inside i18n.config.js there is a configuration that is exported via module.exports={...}.
MyApp in _app.js is wrapped with that module.
Now, I want to initialize that module only if there is a specific query in the URL (e.g. "http://localhost:3000?enableEdit=true").
So, my question is, how can I set the config parameter inside module.exports object to depend based on a query?
Here is the _app.js
import { useStore } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import appWithTranslation from '../i18n.config';
import { reduxWrapper } from '../src/store/createStore';
const MyApp = ({ Component, router, pageProps }) => {
const store = useStore((state) => state);
return (
<PersistGate persistor={store.__persistor}>
{() => (
<Component router={router} {...pageProps} />
)}
</PersistGate>
);
};
export default reduxWrapper.withRedux(MyApp, appWithTranslation);
And here is the module in i18n.config.js
const path = require('path');
const PhraseInContextEditorPostProcessor = process.browser
? require('i18next-phrase-in-context-editor-post-processor').default
: require('i18next-phrase-in-context-editor-post-processor');
const NextI18Next = require('next-i18next').default;
module.exports = new NextI18Next({
defaultLanguage: 'en',
otherLanguages: ['de'],
localePath: path.resolve('./public/static/locales'),
use: [
new PhraseInContextEditorPostProcessor({
phraseEnabled: true;
projectId: 'projectID',
}),
],
postProcess: ['phraseInContextEditor'],
});
Related
I just updated my next-redux-wrapper package and did some changes according to their latest doc. However, the component didn't receive the props returned from MyComponent.getInitialProps = wrapper.getInitialPageProps(store => (context) => ({ foo: "bar }))
When I console the props on MyComponent, there is no "foo"
Has anyone solved this or know why this happens?
I tried to change my __app.js and use getInitialProps without wrapper.getInitialPageProps, but it changes nothing. Here is my __app.js
import { wrapper } from 'store';
import { Provider } from 'react-redux';
import App from 'next/app';
const MyApp = ({ Component, ...rest }) => {
const { store, props } = wrapper.useWrappedStore(rest);
return (
<Provider store={store}>
<Component {...props.pageProps} />
</Provider>
);
};
MyApp.getInitialProps = wrapper.getInitialAppProps(
(store) => async (appCtx) => {
const appProps = await App.getInitialProps(appCtx);
return {
pageProps: {
...appProps.pageProps,
},
};
}
);
export default MyApp;
I'm using next-i18next module for multilingual support.
I have some static pages and dynamic pages as well. both working fine on local.
I deployed all static pages on vercel, all worked fine on vercel. But dynamic page is not working on vercel. it shows 404 page for that dynamic page.
Below is the code of the dynamic page. (pages/test-page/[questionId].js)
import { useState, useEffect } from "react";
import {Layout} from "#components/common";
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { TestComponent } from '#components/TestComponent'
const TestPage = () =>
{
const { t } = useTranslation('common')
const router = useRouter()
const {questionId} = router.query;
const [isApiLoaded,setIsApiLoaded] = useState(false)
return (
<TestComponent
t={t}
isApiLoaded={isApiLoaded}
setIsApiLoaded={setIsApiLoaded}
/>
)
}
TestPage.Layout = Layout
export const getServerSideProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['home', 'common']))
}
});
export default TestPage;
How to fix this issue?
Adding localePath in next-i18next.config.js did help in my case.
const path = require('path')
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
localePath: path.resolve('./public/locales')
}
};
I was facing the same issue and for a temporary fix I used the i18n object from next-i18next that has a function called getResource that gets the current locale with its translations
// import { i18n } from 'next-i18next';
// import { useRouter } from 'next/router';
const [translation, setTranslation] = useState({});
useEffect(() => {
const bundle = i18n.getResource(locale, 'common');
setTranslation(bundle);
}, []);
And to avoid rewrite the code with the t function, you could use
// LINK https://stackoverflow.com/a/43849204/14263138
const t = (word) => word
.split('.')
.reduce((p, c) => (p && p[c]) || null, translation);
With this applied, you don't need to use the getServerSideProps
Although the post is now old, I share the solution that solved the problem in my project (focus on the addition of localePath):
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'it', 'de', 'es', 'fr', 'ja']
},
defaultNs: 'shared',
fallbackLng: { default: ['en', 'it', 'de', 'es', 'fr', 'ja'] },
localePath: path.resolve('./public/locales'),
};
I specify that localePath should not be included among the properties of i18n as indicated in another answer as doing so produces a type error.
Also make sure to use getServerSideProps and not getStaticProps on pages, for example:
export async function getServerSideProps({ locale }) {
return {
props: {
...(await ssrTranslations(locale, ['login', 'shared'])),
},
};
}
Import the serverSideTranslations
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
Now from the getServerSideProps, pass the ..(await serverSideTranslations(locale, ["common"])), with the props.
export const getServerSideProps: GetStaticProps = async ({
locale,
locales,
query
}: any) => {
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
}
}
};
Now add your language strings inside
/public/locales/en/common.json
For example
{
"home": {
"Test": "Test"
}
}
You can add more language strings inside the locales directory.
Restart your Next.js app. It will work.
I have a problem with routing in next.js.
Project on the server is in subdirectory: exmaple.com/app.
Routing i want to achieve: example.com/app/<locale>/<pageName>
(add prefix before url). When I'm trying to add it in href Link generates url this way: example.com/<locale>/app/<pageName>
Is there a way to add locale after prefix, not the other way around?
When I use basePath it servers my app in example.com/app/app because project is in subdirectory /app
//next.config.js
const { i18n } = require('./next-i18next.config');
module.exports = {
i18n,
basePath: process.env.NODE_ENV !== 'production' ? '/app' : '',
assetPrefix: '/app/',
};
Custom link component used to wrap links:
//link.tsx
import React from 'react';
import NextLink, { LinkProps } from 'next/link';
import { useRouter } from 'next/router';
export interface ILinkProps extends LinkProps {
children: React.ReactChild;
asLink?: boolean;
}
Link.defaultProps = {
asLink: false,
};
export default function Link(props: ILinkProps): React.ReactElement {
const { children, asLink, href, locale, ...rest } = props;
const router = useRouter();
const currentLocale = router.locale;
return (
<>
{asLink ? (
<NextLink
{...rest}
href={href}
locale={locale || currentLocale}
as={href}
passHref
>
{children}
</NextLink>
) : (
children
)}
</>
);
}
Next translation is displaying key instead of lang
common:menu.1.title common:read_more
i18n.js
module.exports = {
locales: ['en', 'de', 'cs'],
defaultLocale: 'en',
redirectToDefaultLang: true,
pages: {
'*': ['common'],
'/404': ['home'],
'/': ['home'],
'/about': ['about'],
},
interpolation: {
prefix: '${',
suffix: '}',
},
logger: true,
logBuild: true,
loadLocaleFrom: (locale, namespace) =>
import(`./public/locales/${locale}/${namespace}`).then((m) => m.default),
}
this is my next.config.js
const nextTranslate = require('next-translate')
module.exports = nextTranslate()
_app.js
import I18nProvider from 'next-translate/I18nProvider';
class MyApp extends App {
render () {
const { Component, pageProps, store } = this.props
return (
<I18nProvider lang={'en'} >
<Component {...pageProps} />
<GoTop scrollStepInPx="50" delayInMs="16.66" />
</I18nProvider>
);
}
}
export default MyApp
and following HOC
import React, {Component} from "react";
import withTranslation from 'next-translate/withTranslation'
class NavBarLink extends Component {
render() {
const { t, lang } = this.props.i18n
const description = t('menu.1.title')
const description2 = t('read_more')
return <p>{description + ' '+ description2}</p>
}
}
export default withTranslation(NavBarLink, 'common')
The return value is common:menu.1.title common:read_more
Please can someone tell me what is missing in my code?
In new version of next-translate wouldn't need to provide I18nProvider as said here.
But when you provider that you should provide all namespaces you wanna use.
You'd better to look at this migration guide.
I am currently trying to implement a universal app and am using route params throughout my whole application. As such I want to put the route params into state.
I am able to do this ok for the SSR using the below...
router.get('/posts/:id', (req, res) => {
res.locals.id = req.params.id
const store = createStore(reducers, getDefaultStateFromProps(res.locals), applyMiddleware(thunk));
const router = <Provider store={store}><StaticRouter location={req.url} context={}><App {...locals} /></StaticRouter></Provider>;
const html = renderToString(router);
const helmet = Helmet.renderStatic();
res.render('index', {
content: html,
context: JSON.stringify(store.getState()),
meta: helmet.meta,
title: helmet.title,
link: helmet.link
});
});
And from here the id is put into state using the getDefaultStateFromProps function... export function getDefaultStateFromProps({ id = ''} = {}) => ({ id })
This all works perfectly and puts the correct id into the redux state, which I can then use when hitting this route.
The problem I have is that when I change route on the client side, I'm not sure how to update the redux state for the id from the url.
In terms of my handling of routes I am using the following:
import React, {Component} from 'react';
import { Switch } from 'react-router-dom';
import Header from './header/';
import Footer from './footer';
import { renderRoutes } from 'react-router-config';
export default class App extends Component {
render() {
return (
<div>
<Header />
<Switch>
{renderRoutes(routes)}
</Switch>
<Footer />
</div>
);
}
}
export const routes = [
{
path: '/',
exact: true,
component: Home
},
{
path: '/posts/:id',
component: Post,
}
{
path: '*',
component: PageNotFound
}
];
And then use the following to hydrate...
const store = createStore(reducers, preloadedState, applyMiddleware(thunk));
const renderRouter = Component => {
ReactDOM.hydrate((
<Provider store={store}>
<Router>
<Component />
</Router>
</Provider>
), document.querySelectorAll('[data-ui-role="content"]')[0]);
};
So what I'm wondering is how when I make a route change... how can I update the redux state for the new :id from the route param?
I'm a little lost in how to approach this... any help is appreciated.
You'll need to import routes from your route definition file.
import { matchPath } from 'react-router';
import { LOCATION_CHANGE } from 'react-router-redux';
// LOCATION_CHANGE === '##router/LOCATION_CHANGE';
someReducerFunction(state, action){
switch(action.type){
case LOCATION_CHANGE:
const match = matchPath(action.payload.location.pathname, routes[1]);
const {id} = match.params;
// ...
default:
return state;
}
}
Fully working example:
https://codesandbox.io/s/elegant-chaum-7cm3m?file=/src/index.js