#Next-translate error when trying to send mail via Resend

34 messages · Page 1 of 1 (latest)

idle needle
#

Hey there.
I've been trying to send mails with Resend in my Next.js app. So far so good, it works with "raw data".

But then I've tried to enhance my templates with translations, reusing my already in place next-translate.

At first, I've added at the beginning of my template a useTranslation which I believed was working server-side despite its hook name, but it crashed the whole process.

I've tried to replace it by createTranslation, and while working in local, it does not work in prod. By looking at vercel logs once deployed, I have

TypeError: Cannot read properties of undefined (reading 'localesToIgnore')

I wanted to try the getT method as well, but I haven't even been able to load a translation in local with this method.

Am I missing something, is there a common way to achieve this?

trim grailBOT
#

🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord

🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize

✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)

swift dragon
idle needle
# swift dragon ``useTranslation`` should work fine on the serverside as you can [see here](http...

That's what I thought but then I don't understand where the issue is. I just have an issue when the app is deployed on Vercel, coming from this template if it has the useTranslation :

import useTranslation from 'next-translate/useTranslation';

interface Props {
  otp: number;
}

const OneTimePasswordEmailTemplate: React.FC<Props> = ({ otp }) => {
  const { t } = useTranslation('emails');

  return (
    <div style={{ backgroundColor: '#0f445b', color: '#ffffff', fontSize: '18px', padding: '24px' }}>
      {t('generic.someTranslatedText')}
      {/* More content without any particular logic */}
    </div>
  );
};

export default OneTimePasswordEmailTemplate;

This code is called from within app/api/auth/send-otp/route.ts file which includes the following

export const POST = async (req: NextRequest) => {
  // Some code 
  await redis.set(key, otp, { ex: initialTtl }); // Last line that does not crash
  await sendEmail(email, 'One Time Password', OneTimePasswordEmailTemplate({ otp }));
  return Response.json({ ttl: initialTtl }); // Never triggered since it crashes at sendMail method
};

Knowing that sendMail is within an util email.ts file that has

import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY || '');

export const sendEmail = async (to: string, subject: string, Template: React.ReactNode) => {
  try {
    await resend.emails.send({
      from: 'Sidyq <[email protected]>',
      to: [to],
      subject,
      react: Template as React.ReactElement,
    });
  } catch (e) {
    console.error('Error sending email', e);
  }
};
swift dragon
idle needle
# swift dragon thanks for sharing all the files. We might need them later. Can you share the sp...

Once deployed, the send-otp route returns a 500 error with, within Vercel logs, this displayed:

⨯ TypeError: Cannot read properties of null (reading 'useContext')

And from what I've seen no further useful log.

Like I said, replacing by createTranslation just gives another error about an undefined property, trying to read localesToIgnore (from what I've seen on https://github.com/aralroca/next-translate/blob/master/src/createTranslation.tsx it means that config is not defined, but anyways if useTranslation is supposed to be working I'd be fine with any solution)

swift dragon
idle needle
swift dragon
idle needle
idle needle
# swift dragon I think it only crashes in prod, right?

Same error logged in Vercel:

⨯ TypeError: Cannot read properties of null (reading 'useContext')
    at t.useContext (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:183619)
    at /var/task/.next/server/app/api/auth/send-otp/route.js:1:6618
    at u (/var/task/.next/server/app/api/auth/send-otp/route.js:1:6699)
    at d (/var/task/.next/server/app/api/auth/send-otp/route.js:1:1455)
    at /var/task/node_modules/@sentry/nextjs/build/cjs/common/wrapRouteHandlerWithSentry.js:59:44
    at Object.handleCallbackErrors (/var/task/node_modules/@sentry/core/build/cjs/utils/handleCallbackErrors.js:26:26)
    at /var/task/node_modules/@sentry/nextjs/build/cjs/common/wrapRouteHandlerWithSentry.js:58:47
    at /var/task/node_modules/@sentry/opentelemetry/build/cjs/index.js:870:15
    at Object.handleCallbackErrors (/var/task/node_modules/@sentry/core/build/cjs/utils/handleCallbackErrors.js:26:26)
    at /var/task/node_modules/@sentry/opentelemetry/build/cjs/index.js:869:19
#

As expected, just wrapped the code in the POST method:

console.log('will init t from useTranslation "hook"');
  const { t } = useTranslation('emails');
  console.log('send-otp translate ', t('otp.isYourLoginCode'));

And the first one is displayed, then it crashes

swift dragon
idle needle
# swift dragon hm pretty weird. Looks like it's a vercel limitation 🤔

Maybe. I was thinking another ugly-ish solution which would just be to translate manually?
Since it's working for the whole application except mails, maybe creating a public/locales/XX/emails.json and then importing the json file in templates for direct use (considering I have the locale to do the right dynamic import?) would work... idk

swift dragon
idle needle
swift dragon
#

Increased payload / traffic weight w/ translated texts
of couse it would be a bit more. However not really that much more (you don't send the whole file. Only the parts that are needed)

Other use cases that would still be problematic, e.g. when stripe webhooks are triggered, there's no client side hence I'll still have to deal with translations in the backend
how do you expect rn the backend knows which language the output should be?

idle needle
# swift dragon > Increased payload / traffic weight w/ translated texts of couse it would be a ...

Yeah that's right, template are not that heavy but I guess it always counts.

From what I've browsed in the payload sent by stripe, there are several leads I can exploit

  • The country within customer address that could be normalized and used somehow (e.g. transform FR into fr and such); could be problematic if lots of countries are not tied to a language
  • Another lead which seems better, the return url. Since routing includes the lang within the app, the return url sent back by stripe contains the locale that was used during checkout.
#

More generally of course, if I ever needed more mails to be sent from the BE, I'd have to store the user's locale in db or something similar... But that's not in the plans for now, not sure if I should anticipate that.

swift dragon
idle needle
swift dragon
idle needle
idle needle
# swift dragon Sure thing. For reference: https://nextjs.org/docs/app/building-your-application...

Oh that's a similar idea in the end, it's cool. Maybe simpler or more explicit than mine. I've implemented it like this in my email.ts file:

export const loadEmailTranslations = async (lang: string) => {
  // Cannot be dynamic or files will not be included in the bundle
  switch (lang) {
    case 'en':
      return (await import('@/locales/en/emails.json')).default;
    case 'fr':
      return (await import('@/locales/fr/emails.json')).default;
    default:
      return (await import('@/locales/en/emails.json')).default;
  }
};

A bit more verbose since it's a switch rather than an object, but there's a fallback language 🤔
Haven't dived much deeper to see important differences w/ what's suggested on the page

swift dragon
idle needle
swift dragon
idle needle
#

Nvm I don't see what it could be linked to, since error also happens in local and it seems to be located on

const redisClientSingleton = () => {
  return new Redis({
    url: process.env.UPSTASH_REDIS_REST_URL,
    token: process.env.UPSTASH_REDIS_REST_TOKEN,
  });
};

Which is weird since I haven't touched it for 2 months. Somehow latest changes broke this, or at least it explodes there...

idle needle
#

If there was an issue on their side it would be an explanation but eh

swift dragon
#

If I would be in your situation I would migrate to next's default i18n system. I hate to have these kind of "unfixable" errors