Issue with react-i18next / next-i18next
MoOx opened this issue · 12 comments
I am trying to use withRelay
on a page that need preloading of info (classic /account/edit
where I want fields to be filled with user data upfront).
So I started converting a simple page by adding withRelay
required logic.
I started with this (working, but no query to prefill)
import page from "../../src/pages/PageAccountEdit.bs.js";
import { node as pageQuery } from "../../src/__generated__/PageAccountEdit_getUser_Query_graphql.bs.js";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { getSession } from "next-auth/react";
export default page;
export async function getServerSideProps(context) {
const session = await getSession(context);
if (session === null) {
return {
redirect: {
destination: "/auth/login?next=" + encodeURIComponent(context.pathname),
permanent: false,
},
};
}
return {
props: {
session,
...(await serverSideTranslations(context.locale, ["common"])),
},
};
}
to this (same page, with a new query and withRelay
call)
import page from "../../src/pages/PageAccountEdit.bs.js";
import { node as pageQuery } from "../../src/__generated__/PageAccountEdit_getUser_Query_graphql.bs.js";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { getSession } from "next-auth/react";
async function getServerSideProps(context) {
const session = await getSession(context);
if (session === null) {
return {
redirect: {
destination: "/auth/login?next=" + encodeURIComponent(context.pathname),
permanent: false,
},
};
}
return {
props: {
session,
...(await serverSideTranslations(context.locale, ["common"])),
},
};
}
import { withRelay } from "relay-nextjs";
import * as RelayEnvClient from "../../src/RelayEnvClient.bs";
import * as RelayEnvServer from "../../src/RelayEnvServer.bs";
export default withRelay(page, pageQuery, {
createClientEnvironment: () => RelayEnvClient.getClientEnvironment(),
serverSideProps: getServerSideProps,
createServerEnvironment: async (ctx, serverSideProps) => {
return RelayEnvServer.createServerEnvironment(
serverSideProps.props.session
);
},
});
I am getting an warning in the browser
react-i18next:: You will need to pass in an i18next instance by using initReactI18next
And the translations are not available.
What is weird is that my getServerSideProps
is exactly the same, export serverSideTranslations
properly.
Any idea why withRelay
might conflict somehow with next-i18next ?
FYI, getInitialProps
used here is causing trouble to next-i18next
(see details here) poke @isaachinman
Does any of you see a way to be able to interact with each other ? I would love to be able to use both of you libraries together !
For those looking for something good as I am writing this, I encourage you to have a look to this vercel/next.js#33892
To be frank, getInitialProps
only still exists because a lot of older packages/utils rely on it, unfortunately. Its use should be phased out.
That's not true, getInitiaProps
is still used because it's superior to the alternatives. More detailed issues of getServerSideProps
here: #61 (comment)
I've personally hacked next-i18next
to work with getInitialProps
, but only for the features which we use in our app.
"Superior" is a subjective measure – it's not the direction the framework is headed in.
This library uses getInitialProps
because it's impossible to implement certain patterns using getServerSideProps
. Those issues are being addressed in the Next.js router RFC, but if we waited for that our users would have a bad experience until it's rolled out. Once that changes + Next.js integrates server components I imagine a few things will change and we'll re-evaluate our approach. Regrading react-i18next
, we don't have a need to use that library at Revere so I probably won't be writing up a guide on how to properly integrate it, as I would not be able to find all the edge cases + what real-world usage looks like, happy to add something to the docs if someone writes something up though!
I managed to get everything working using relay-nextjs except making it work with i18next. Has anyone found a workaround that can make it work?
@mortezaalizadeh relay-nextjs
doesn't currently support getServerSideProps
which is required by react-i18next
. The best workaround right now is to put translation data in your GraphQL API, query that as part of your page's query, and pass that data to the translation library.
I've found it simpler to just to call serverSideTranslations
in getInitialProps
and then "cache" it with useState
in _app
so that we don't lose translations when navigating. This simple solution only works if you can load all translations on first pageload (not using page specific namespaces) and if you are fine with full refresh when user changes language.
I'm using next-i18next
though, but I think if you're using only i18next
you can solve it the same way. I have this kind of helper function. You just call withTranslations(PageComponent)
and the page will have translations.
export default function withTranslations<T>(Page: NextPageWithLayout<T>) {
const originalGetInitialProps = Page.getInitialProps;
// Load translations in getInitialProps if running on server
Page.getInitialProps = async (ctx) => {
const originalProps = originalGetInitialProps
? await originalGetInitialProps(ctx)
: {};
if (typeof window === 'undefined') {
const getTranslations = (
await import('src/util/getTranslations')
).default;
const translationProps = await getTranslations({
locale: ctx.locale || 'en',
});
return {
...originalProps,
...translationProps,
};
}
return originalProps;
};
return Page;
}
You can implement getTranslations
function however you like depending on how your translations are stored. Note that you can call any backend only code inside getTranslations so you can just call whatever you'd call inside getServerSideProps
. As noted, the difference is that with the above solution translations are only loaded in the initial full page load instead of every navigation.
Hi, I just ran into this issue and I was able to fix it by following this: i18next/next-i18next#1917 (comment)
I just had to pass
import i18nextConfig from '../../next-i18next.config';
...
export default appWithTranslation(MyApp, i18nextConfig);
@mo-rally-dev Thank you for continuing to look into this! Would there be any way to see a full example of how your app is working with i18n? Would be great to put something in the docs.
Hey @rrdelaney ,
I sadly can't share my app as it's a private one but here's my next-i18next.config.js
file.
const HttpBackend = require('i18next-http-backend/cjs');
const ChainedBackend = require('i18next-chained-backend').default
const LocalStorageBackend = require('i18next-localstorage-backend').default
const yaml = require('js-yaml');
const isBrowser = typeof window !== 'undefined'
const isDev = process.env.NODE_ENV === 'development'
// @ts-check
/**
* @type {import('next-i18next').UserConfig}
*/
module.exports = {
debug: isDev,
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'es', 'it', 'ro'],
},
localePath: isBrowser ? '/lang' : require('path').resolve('./public/lang'),
localeExtension: 'yml',
reloadOnPrerender: isDev,
lng: 'en',
ns: ['app', 'research'],
defaultNS: 'app',
interpolation: {
escapeValue: false,
},
serializeConfig: false,
use: isBrowser ? [ChainedBackend] : [],
backend: {
// For the client side, we save the translations in localstorage and fetch them if they are missing
backends: isBrowser ? [LocalStorageBackend, HttpBackend] : [],
backendOptions: [
{
prefix: 'test_i18next_',
expirationTime: isDev ? 0 : 60 * 60 * 1000, // 1 hour
store: isBrowser ? window.localStorage : null
},
{
loadPath: '/lang/{{lng}}/{{ns}}.yml',
parse: function(data) { return yaml.load(data); },
},
],
},
};
I am storing my translations in the public folder under public/lang/<languageCode>/<namspace>.yml
.
Server side rendering works because I am calling serverSideTranslations
in my withRelay
method for the serverSideProps. Here's a snippet of the code block. You might need to return { props: ...(await etc)}
based on your setup.
if (typeof window === 'undefined') {
const { serverSideTranslations } = await import(
'next-i18next/serverSideTranslations'
);
const locale = context.locale; // TODO: when we are ready plug in this code below: (locale ?? 'en')
const namespaces = ['app', ...additionalLocaleNamespacesToLoad];
return {
...(await serverSideTranslations(
'en',
namespaces,
null,
SUPPORTED_LANGUAGES
)),
};
}
Hope this helps!