next-translate
next-translate copied to clipboard
getT on API route on Vercel (serverless)
First: Thanks for this great lib!
I'm using next-translate
@ 1.0.1 and next
@ 10.0.5, and I have trouble to use getT
on my API route.
The following error occurred in production on Vercel, when calling the API:
ERROR TypeError: Cannot read property 'loadLocaleFrom' of undefined
The API is called serverless on Vercel which mean the NodeJS global
didn't contain the i18nConfig
, which leads to this error message.
I tried to set the config to global inside the API route:
import getT from 'next-translate/getT'
import i18n from '../../i18n.json'
const handler = async (
req,
res,
) => {
global.i18nConfig = i18n
const t = await getT(req.query.__nextLocale, 'api')
const error = t('error')
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ error }))
}
export default handler
The error disappears, but I get only the translation key instead of the translated text.
You can reproduce this on your local machine: Build and start Next and call the API directly without any page load, because the page load will add the config to global.
Any ideas how to resolve this?
Can confirm that this bug exists, it virtually makes next-translate impossible to use on an API.
Here's a stacktrace I'm getting in Vercel (just trying to be helpful):
2021-02-07T09:11:37.129Z 6aba0774-c6e7-487b-9f6c-3d20f2172049 ERROR TypeError: Cannot read property 'loadLocaleFrom' of undefined
at /var/task/node_modules/next-translate/lib/cjs/getT.js:60:37
at step (/var/task/node_modules/next-translate/lib/cjs/getT.js:33:23)
at Object.next (/var/task/node_modules/next-translate/lib/cjs/getT.js:14:53)
at /var/task/node_modules/next-translate/lib/cjs/getT.js:8:71
at new Promise (<anonymous>)
at __awaiter (/var/task/node_modules/next-translate/lib/cjs/getT.js:4:12)
at getT (/var/task/node_modules/next-translate/lib/cjs/getT.js:48:12)
at handler (/var/task/.next/serverless/pages/api/tools.js:345:77)
at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:7)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
2021-02-07T09:11:37.130Z 6aba0774-c6e7-487b-9f6c-3d20f2172049 ERROR TypeError: Cannot read property 'loadLocaleFrom' of undefined
at /var/task/node_modules/next-translate/lib/cjs/getT.js:60:37
at step (/var/task/node_modules/next-translate/lib/cjs/getT.js:33:23)
at Object.next (/var/task/node_modules/next-translate/lib/cjs/getT.js:14:53)
at /var/task/node_modules/next-translate/lib/cjs/getT.js:8:71
at new Promise (<anonymous>)
at __awaiter (/var/task/node_modules/next-translate/lib/cjs/getT.js:4:12)
at getT (/var/task/node_modules/next-translate/lib/cjs/getT.js:48:12)
at handler (/var/task/.next/serverless/pages/api/tools.js:345:77)
at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:7)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
2021-02-07T09:11:37.130Z 6aba0774-c6e7-487b-9f6c-3d20f2172049 ERROR Unhandled Promise Rejection {"errorType":"Runtime.UnhandledPromiseRejection"
,"errorMessage":"TypeError: Cannot read property 'loadLocaleFrom' of undefined"
,"reason":{"errorType":"TypeError"
,"errorMessage":"Cannot read property 'loadLocaleFrom' of undefined"
,"stack":["TypeError: Cannot read property 'loadLocaleFrom' of undefined"
," at /var/task/node_modules/next-translate/lib/cjs/getT.js:60:37"
," at step (/var/task/node_modules/next-translate/lib/cjs/getT.js:33:23)"
," at Object.next (/var/task/node_modules/next-translate/lib/cjs/getT.js:14:53)"
," at /var/task/node_modules/next-translate/lib/cjs/getT.js:8:71"
," at new Promise (<anonymous>)"
," at __awaiter (/var/task/node_modules/next-translate/lib/cjs/getT.js:4:12)"
," at getT (/var/task/node_modules/next-translate/lib/cjs/getT.js:48:12)"
," at handler (/var/task/.next/serverless/pages/api/tools.js:345:77)"
," at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:7)"
," at processTicksAndRejections (internal/process/task_queues.js:97:5)"]}
,"promise":{}
,"stack":["Runtime.UnhandledPromiseRejection: TypeError: Cannot read property 'loadLocaleFrom' of undefined"
," at process.<anonymous> (/var/runtime/index.js:35:15)"
," at process.emit (events.js:326:22)"
," at processPromiseRejections (internal/process/promises.js:209:33)"
," at processTicksAndRejections (internal/process/task_queues.js:98:32)"]}
Thanks to @aralroca and @Fivedark :)
I was having the exact same issue and after applying the solution above from @Fivedark, everything is working as expected now. For the locale, I'm passing the value to my api function. Here's my code in case it could help anyone else:
// I18N
import getT from 'next-translate/getT'
import i18n from '../i18n'
const SENDGRID_API = 'https://api.sendgrid.com/v3/mail/send'
const sendEmail = async ({ name, email, message, locale }) => {
// I18N
global.i18nConfig = i18n
const t = await getT(locale, 'contact')
/* Send to client */
await fetch(SENDGRID_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
'Accept-Language': `${locale}`
},
body: JSON.stringify({
personalizations: [
{
to: [
{
email: email
}
],
subject: t('EmailSubject')
}
],
from: {
email: '[email protected]',
name: 'Guy Dumais'
},
content: [
{
type: 'text/html',
value: `${t('Thank You')} <b><a href='mailto:${email}'>${name}</a></b>,<br/><br/>${t('EmailConfirmation')}<br/><br/>Message:<br/>${message}`
}
]
})
})
}
export default sendEmail
any idea how make it work guys ? i wont re edit my all backend work to front end :S
It is also happening on localhost, not only Vercel. It happens when the first call made on the server is an api call.
I mean that, when I start the server, if I load a page in the browser, the subsequent api calls work fine, and global.i18nConfig
is correctly loaded.
But, if the first call made to the server is an API call, then global.i18nConfig
is undefined and I have this error Cannot read property 'loadLocaleFrom' of undefined
.
A simple workaround would be to load it manually:
import i18n from '../i18n'
global.i18nConfig = i18n
Before solving it, I would like to rethink the way of consuming the configuration...
@aralroca with the workaround I only get the keys and not the values returned.
And can the workaround be added once in the next.js project or do we have to add this on every api route?
@goellner No, the workaround will need to be added in every api route where you use getT
.
The following patch works for me until this issue is resolved:
// monkey-patches.ts
import getT from "next-translate/getT";
import i18n from "../i18n";
export const ensureAvailabilityOfGetT = () => {
global.i18nConfig = i18n;
return getT;
};
and then:
// some-api-route.ts
import { ensureAvailabilityOfGetT } from "../../../utils/api/utils/monkey-patches";
const parsedLocale = "...";
const getT = ensureAvailabilityOfGetT();
const errorsT = await getT(parsedLocale, "errors");
const feedbackT = await getT(parsedLocale, "feedback");
// do your thing
Once getT
from next-translate
works properly, refactoring will be a piece of cake.
@goellner I had the same problem than you, only keys where return and not the values, it was because my i18n.js
file didn't contain loadLocaleFrom
which is needed by getT
, I had to add it.
At first I had only locales
, defaultLocale
and pages
, when I added loadLocaleFrom
it worked :
{
locales: [ 'en' ],
defaultLocale: 'en',
pages: { '*': [ 'common' ] },
loadLocaleFrom: (lang, ns) =>
import(`./locales/${lang}/${ns}.json`).then((m) => m.default),
}
It's a shame that this bug/feature isn't implemented in a timely manner. Can you think about it again?
@Marc-Hoch the project is open-source, feel free to implement and do a PR
@aralroca
This is what I ended up doing:
import getT_ from "next-translate/getT";
import i18n from "path/to/i18n_config";
export const getT = (locale?: string, namespace?: string | string[]) => {
global.i18nConfig = i18n;
// This part in particular fixes a problem that happens only when
// I start the next server, and before any other navigation, I send a request to an API route
// that is supposed to return some localised text.
if (global?.__NEXT_TRANSLATE__?.config) {
if (!global.__NEXT_TRANSLATE__.config.loadLocaleFrom) {
global.__NEXT_TRANSLATE__.config.loadLocaleFrom = i18n.loadLocaleFrom;
}
}
return getT_(locale, namespace);
};