Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 3, 2022 10:42 am GMT

All side optimized Next.js translations

Writing Next.js code blurs the lines between client side and server side.
The code is written once and depending on your needs it is then executed as SSG (static-site generation), SSR (server-side rendering) or CSR (client-side rendering), etc.

So also the internationalization, right?

Let's take the example of next-i18next.
While next-i18next uses i18next and react-i18next under the hood, users of next-i18next simply need to include their translation content as JSON files and don't have to worry about much else.

By default, there is one next-i18next configuration that loads the translations from the local directory structure and renders the pages on server side.

This is ok, it works and is optimized for SEO etc. but there is more we could do.

What if we could power up the seo optimized website with always up-to-date translations without the need to redeploy your app?

We will discuss 2 different setups: One with an active backend and another one completely statically generated.

The basic target is always the same: We want everything to be SEO optimized in all languages and serve always the newest translations to our users.

Example with a backend server

Having a backend server does not mean you are forced to run your own server. It can also be a PaaS or serverless solution, like Vercel or Netlify, etc.

Ok, let's start with the default:

You followed the normal next-i18next setup guide and now your translations are organised more or less as such:

. public     locales         en        |    common.json         de             common.json

Now let's connect to an awesome translation management system and manage your translations outside of your code.

Let's synchronize the translation files with locize.
This can be done on-demand or on the CI-Server or before deploying the app.

What to do to reach this step:

  1. in locize: signup at https://locize.app/register and login
  2. in locize: create a new project
  3. in locize: add all your additional languages (this can also be done via API)
  4. install the locize-cli (npm i locize-cli)

Use the locize-cli

Use the locize sync command to synchronize your local repository (public/locales) with what is published on locize.

Alternatively, you can also use the locize download command to always download the published locize translations to your local repository (public/locales) before bundling your app.

But you were talking about having always up-to-date translations without the need to redeploy your app?

Yes, let's adapt for that:

We will use the i18next-locize-backend plugin, but only on client side.

Together with some other i18next dependencies:

npm install i18next-locize-backend i18next-chained-backend i18next-localstorage-backend

And we adapt the next-i18next.config.js file:

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const ChainedBackend= require('i18next-chained-backend').defaultconst LocalStorageBackend = require('i18next-localstorage-backend').defaultconst isBrowser = typeof window !== 'undefined'module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de', 'it'],  },  backend: {    backendOptions: [{      expirationTime: 60 * 60 * 1000 // 1 hour    }, {      projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',      version: 'latest'    }],    backends: isBrowser ? [LocalStorageBackend, LocizeBackend] : [],  },  serializeConfig: false,  use: isBrowser ? [ChainedBackend] : []}

And then remove the serverSideTranslation to getStaticProps or getServerSideProps (depending on your case) in the page-level components.

//// Without the getStaticProps or getServerSideProps function,// the translsations are loaded via configured i18next backend.//// export const getStaticProps = async ({ locale }) => {//   return {//     props: await serverSideTranslations(locale, ['common', 'footer'])//   }// }

That's it! Let's check the result:

The HTML returned from the server looks correctly translated. So this is well optimized for search engines.

And on client side, the up-to-date translations are directly fetched from the locize CDN.

This means you can fix translations without having to change your code or redeploy your app.

The code can be found here.

Additional hint:

If you've configured caching for your locize version, you may not need the i18next-localstorage-backend and i18next-chained-backend plugin.

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const isBrowser = typeof window !== 'undefined'module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de', 'it'],  },  backend: isBrowser ? {    projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',    version: 'production'  } : undefined,  serializeConfig: false,  use: isBrowser ? [LocizeBackend] : []}

Static Website example

With this example, we just need a static webserver, like GitHub Pages or similar.

It's pretty much the same as with above example, but there are some little things we need to additionally consider.

To work with static-site generation (SSG) we need to use the next export command, but...

Error: i18n support is not compatible with next export. See here for more info on deploying: https://nextjs.org/docs/deployment

This happens if you're using the internationalized routing feature and are trying to generate a static HTML export by executing next export.
Well, this features requires a Node.js server, or dynamic logic that cannot be computed during the build process, that's why it is unsupported.

There is a dedicated article with a solution to that Next.js problem. Follow that guide first!

Done so? Then let's continue here:

It's the same next-i18next.config.js config like in the previous example:

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const ChainedBackend= require('i18next-chained-backend').defaultconst LocalStorageBackend = require('i18next-localstorage-backend').default// If you've configured caching for your locize version, you may not need the i18next-localstorage-backend and i18next-chained-backend plugin.// https://docs.locize.com/more/cachingconst isBrowser = typeof window !== 'undefined'module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de', 'it'],  },  backend: {    backendOptions: [{      expirationTime: 60 * 60 * 1000 // 1 hour    }, {      projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',      version: 'latest'    }],    backends: isBrowser ? [LocalStorageBackend, LocizeBackend] : [],  },  serializeConfig: false,  use: isBrowser ? [ChainedBackend] : []}

Extend the makeStaticProps function with options (emptyI18nStoreStore):

export function makeStaticProps(ns = [], opt = {}) {  return async function getStaticProps(ctx) {    const props = await getI18nProps(ctx, ns)    if (opt.emptyI18nStoreStore) {      // let the client fetch the translations      props._nextI18Next.initialI18nStore = null    }    return {      props    }  }}

...and use it accordingly:

const getStaticProps = makeStaticProps(['common', 'footer'], { emptyI18nStoreStore: true })export { getStaticPaths, getStaticProps }

That's it! Let's check the result:

The generated static HTML looks correctly translated. So this is well optimized for search engines.

And on client side, the up-to-date translations are directly fetched from the locize CDN.

This means you can fix translations without having to change your code or redeploy your app. And without owning an active server.

The code can be found here.

Continuous Localization

Since we're now "connected" to as smart translation management system, we can try to make use of its full potential.

save missing translations

I wish newly added keys in the code, would automatically be saved to locize.

Your wish is my command!

Extend the next-i18next config with the locize api-key and set saveMissing: true:

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const isBrowser = typeof window !== 'undefined'module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de'],  },  backend: {    projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',    apiKey: '14bbe1fa-6ffc-40f5-9226-7462aa4a042f',    version: 'latest'  },  serializeConfig: false,  use: isBrowser ? [LocizeBackend] : [],  saveMissing: true // do not set saveMissing to true for production and also not when using the chained backend}

Each time you'll use a new key, it will be sent to locize, i.e.:

<div>{t('new.key', 'this will be added automatically')}</div>

will result in locize like this:

but there's more...

Thanks to the locize-lastused plugin, you'll be able to find and filter in locize which keys are used or not used anymore.

With the help of the locize plugin, you'll be able to use your app within the locize InContext Editor.

Lastly, with the help of the auto-machinetranslation workflow and the use of the saveMissing functionality, new keys not only gets added to locize automatically, while developing the app, but are also automatically translated into the target languages using machine translation.

Check out this video to see how the automatic machine translation workflow looks like!

npm install locize-lastused locize

use them like this:

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const isBrowser = typeof window !== 'undefined'const locizeOptions = {  projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',  apiKey: '14bbe1fa-6ffc-40f5-9226-7462aa4a042f',  version: 'latest'}module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de'],  },  backend: locizeOptions,  locizeLastUsed: locizeOptions,  serializeConfig: false,  use: isBrowser ? [LocizeBackend, require('locize').locizePlugin, require('locize-lastused/cjs')] : [], // do not use locize-lastused on production  saveMissing: true // do not set saveMissing to true for production and also not when using the chained backend}

Automatic machine translation:

Last used translations filter:

InContext Editor:

Let's prepare for production

Now, we prepare the app for going to production.

First in locize, create a dedicated version for production. Do not enable auto publish for that version but publish manually or via API or via CLI.
Lastly, enable Cache-Control max-age for that production version.

Let's adapt the next-i18next.config.js file once again:

// next-i18next.config.jsconst LocizeBackend = require('i18next-locize-backend/cjs')const isBrowser = typeof window !== 'undefined'const locizeOptions = {  projectId: 'd3b405cf-2532-46ae-adb8-99e88d876733',  apiKey: '14bbe1fa-6ffc-40f5-9226-7462aa4a042f',  version: 'latest'}module.exports = {  // debug: true,  i18n: {    defaultLocale: 'en',    locales: ['en', 'de'],  },  backend: locizeOptions,  locizeLastUsed: locizeOptions,  serializeConfig: false,  use: isBrowser ? [LocizeBackend, require('locize').locizePlugin, require('locize-lastused/cjs')] : [], // do not use locize-lastused on production  saveMissing: true // do not set saveMissing to true for production and also not when using the chained backend}

Now, during development, you'll continue to save missing keys and to make use of lastused feature. => npm run dev

And in production environment, saveMissing and lastused are disabled. => npm run build && npm start

Caching:

Merging versions:

The complete code can be found here.

Check also the code integration part in this YouTube video.

Congratulations

Awesome! Thanks to next-i18next, i18next, react-i18next and locize your continuous localization workflow is ready to go.

So if you want to take your i18n topic to the next level, it's worth to try the localization management platform - locize.

The founders of locize are also the creators of i18next. So with using locize you directly support the future of i18next.


Original Link: https://dev.to/adrai/all-side-optimized-nextjs-translations-4n9i

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To