Next.js

Localize Next.js with Transifex Native

In this guide we'll be covering how to localize your Next.js app with Server-Side-Rendering (SSR), using Transifex Native and Over-The-Air translations.

Next.js has already built-in support for i18n routing, and we are going to leverage this, by binding Transifex Native on top of that.

Basic setup

Install Transifex Native

In your Next.js app, install the Transifex Native dependencies for React applications.

npm install @transifex/native @transifex/react @transifex/cli --save

Update your next.config.js

Edit or create a next.config.jsfile in your root folder and add the supported locales, as well as your Transifex Native public token.

// next.config.js
module.exports = {
  i18n: {
    // These are all the locales you want to support in
    // your application
    locales: ['en', 'fr', 'de', 'el'],
    // This is the default locale you want to be used when visiting
    // a non-locale prefixed path e.g. `/hello`
    defaultLocale: 'en',
    localeDetection: false,
  },
  publicRuntimeConfig: {
    TxNativePublicToken: 'YOUR-PUBLIC-TX-NATIVE-TOKEN',
  }
}

Read the official Next.js Internationalized Routing for more configuration options.

Create an Transifex Native utility

In the source folder, where the code exists, add a new i18n.jslibrary file, and paste the following:

// nextjs/i18n.js

import { tx, normalizeLocale } from '@transifex/native';
import getConfig from 'next/config';

const { publicRuntimeConfig } = getConfig();

/**
 * Used by SSR to pass translation to browser
 *
 * @param {*} { locale, locales }
 * @return {*} { locale, locales, translations }
 */
export async function getServerSideTranslations({ locale, locales }) {
  tx.init({
    token: publicRuntimeConfig.TxNativePublicToken,
  });
  
  // ensure that nextjs locale is in the Transifex format,
  // for example, de-de -> de_DE
  const txLocale = normalizeLocale(locale);

  // load translations over-the-air
  await tx.fetchTranslations(txLocale);
  return {
    locale,
    locales,
    translations: tx.cache.getTranslations(txLocale),
  };
}

/**
 * Initialize client side Transifex Native instance cache
 *
 * @param {*} { locale, translations }
 */
export function setClientSideTranslations({ locale, translations }) {
  if (!locale || !translations) return;
  tx.init({
    currentLocale: locale,
  });
  tx.cache.update(locale, translations);
}

Use getServerSideProps to load translations

On the pages you'd like i18n to be translated, load translations server side, and pass them to the client, using the sample below.

// nextjs/pages/index.js

import { useState } from 'react';
import { T, UT } from '@transifex/react';
import { useRouter } from 'next/router';
import { getServerSideTranslations, setClientSideTranslations } from '../i18n';

export default function Home(props) {
  // initialize client side translation from server side props
  setClientSideTranslations(props); // { locale, locales, translations }

  return (
    <div>
      <T _str="Hello world" />
    </div>
  );
}

export async function getServerSideProps(context) {
  const data = await getServerSideTranslations(context)
  return {
    props: {
      ...data, // { locale, locales, translations }
    }
  }
}

You can view a full blown example in the Transifex Native Sandbox and also read React guide on how to localize React applications.

Advanced features

Set translations globally

Instead of setting translations in every page, you can move the code in the Custom App space.

// nextjs/pages/_app.js

import { setClientSideTranslations } from '../i18n';

// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
  setClientSideTranslations(pageProps);  
  return <Component {...pageProps} />
}

However, due to custom app limitations, you will still need to implement getServerSidePropson each page, as Appdoes not support data fetching methods, like getStaticProps or getServerSideProps.

Auto refresh translations

By modifying our i18n.js utility function from the previous example, we can program some custom automatic refresh logic, in order to check for fresh translations at specific intervals.

// ------------ <NEW> ------------
const TRANSLATIONS_TTL_SEC = 10 * 60; // 10 minutes
// ------------ </NEW> -----------

/**
 * Used by SSR to pass translation to browser
 *
 * @export
 * @param {*} { locale, locales }
 * @return {*}
 */
export async function getServerSideTranslations({ locale, locales }) {
  tx.init({
    token: publicRuntimeConfig.TxNativePublicToken,
  });
  // ensure that nextjs locale is in the Transifex format,
  // for example, de-de -> de_DE
  const txLocale = normalizeLocale(locale);
  await tx.fetchTranslations(txLocale);

  // ------------ <NEW> ------------
  
  // bind a helper object in the Native instance for auto-refresh
  tx._autorefresh = tx._autorefresh || {};
  if (!tx._autorefresh[txLocale]) {
    tx._autorefresh[txLocale] = Date.now();
  }

  // check for stale content in the background
  if (Date.now() - tx._autorefresh[txLocale] > TRANSLATIONS_TTL_SEC * 1000) {
    tx._autorefresh[txLocale] = Date.now();
    tx.fetchTranslations(txLocale, { refresh: true });
  }
  
  // ------------ </NEW> -----------

  return {
    locale,
    locales,
    translations: tx.cache.getTranslations(txLocale),
  };
}

In the example above, translations are refreshed every 10min. Modify the TRANSLATIONS_TTL_SECvariable with an interval more suited to your own use case.

Limitations

  • For Next.js apps with lots of content, the setup would need some more complex coding to leverage Content Splitting in order to get optimal performance and reduce data use.
  • The basic setup does not offer Over-The-Air updates when new translations are updated in Transifex, thus a server restart is required. Use the Advanced Setup sample to enable automatic translation updates.