aralroca / next-translate-plugin

Next-translate plugin for i18n in Next.js 🌍 - Load page translations and use them in an easy way!
MIT License
30 stars 17 forks source link

Non-hook version of useTranslation which works with vanilla JS #45

Closed rolandjlevy closed 1 year ago

rolandjlevy commented 1 year ago

What version of this package are you using? 2.0.5

What problem do you want to solve? I want to use a non-hook version of useTranslation inside a vanilla JS helper function which works outside a component. I have looked at getT function but that only seems to work inside components.

What do you think is the correct solution to this problem?

It would be great if I could do the following:

The validation function formErrorText.required is called within Formik here


<Formik
  onSubmit={onSubmit}
  initialValues={{
    email: '',
  }}
  validationSchema={Yup.object().shape({
    email: Yup.string()
      .trim()
      .required(formErrorText.required("Email"))
      .email(formErrorText.email()),
  })}
>

formErrorText is vanilla JS and currently gets imported from helpers.js:

export const formErrorText = {
  required: (field) => `${field} Required`,
  max: (field) => `${field} too long`,
  min: (field) => `${field} too short`,
  maxNumber: (field) => `${field} too high`,
  minNumber: (field) => `${field} too low`,
  email: () => 'Invalid Email Format',
  minWithPrompt: (field, minLength) =>
    `${field} too short. Minimum of ${minLength} characters required`,
  length: (field, length) => `${field} must be ${length} characters long`,
  emoji: () => 'Emojis are not allowed',
};

I want to change the required function inside formErrorText to this

required: (field) => translatedErrorText.required[field],

So that the non-hook version of useTranslation gets used like this (let's call the non-hook function getTranslation)

const translatedErrorText = {
  required: {
    Email: getTranslation('formValidation:emailRequired'),
    Password: getTranslation('formValidation:passwordRequired'),
  },
};

The formValidation.json namespace files in locales would look like this:

en-gb (English)

{
  "emailRequired": "Email Required",
  "passwordRequired": "Password Required"
}

hu (Hungarian)

{
  "emailRequired": "E-mail szükséges",
  "passwordRequired": "Jelszó szükséges"
}

Are you willing to submit a pull request to implement this change? Yes, I can give it a try

aralroca commented 1 year ago

getT is not supported yet in appDir, probably is this the problem. But should work outside components using pages dir. I'm going to prioritize to adapt getT inside appDir

aralroca commented 1 year ago

I hope now works getT:

rolandjlevy commented 1 year ago

@aralroca thanks so much. This will be really great if it works.

How would it be possible to get the getT function working inside the pages dir? I've checked the docs and it looks like its only possible to call getT using getStaticProps.

How could I use getT outside the components but within the pages dir? I can't see how this is possible.

Thanks!

rolandjlevy commented 1 year ago

@aralroca I've just seen your PR - thank you! 😊

Do I just need to reinstall next-translate or do I need to wait a while?

and then I can just do the following outside the components?

const t = await getT('en', 'common');

aralroca commented 1 year ago

You need to update to the prerelease version 2.4.0-canary.5 for both (next-translate and next-translate-plugin)

rolandjlevy commented 1 year ago

OK, great - thank you @aralroca I will try it

But one thing I will be stuck on is getting the locale.

Normally, I would get the local from the router, using useRouter() like this


// inside a component
(async () => {
  const namespace = 'forgotten-password';
  const router = useRouter();
  const translate = await getT(router.locale, namespace);
  const pageTitle = translate(`${namespace}:pageTitle`);
  console.log({ pageTitle }); // working fine
})();

But this means I'm still limited and stuck inside a component.

I tried to get the locale from process.env.LANG but it's giving me 'en_US.UTF-8' when it should be 'en-gb'. I also tried window.navigator.language but get 'en-us'.

Do you know how to get the current locale which comes from the ì18n.js config, outside the component so I don't need to import useRouter()?

aralroca commented 1 year ago

Inside components you can use useTranslation instead of getT.

The useTranslation hook already know the language of the page, so you don't need to pass it.

The getT function was created for the API (now router.ts files in app dir), and yes, in getT is necessary to pass the language, but make no sense to use getT inside components.