/next-g11n

translate and localize your Next.js app smoothly

Primary LanguageTypeScriptMIT LicenseMIT

next-g11n logo

next-g11n ๐ŸŒ

Your toolbelt for translating Next.js apps

npm badge typescript badge

๐Ÿชก tailor-made for Next.js i18n Routing

๐Ÿ‘ฎ Type-safe dictionaries

โ˜๏ธ optimized for Server-Side Rendering

vscode screenshot of missing translation yielding warning

Getting started ๐Ÿ—

  1. Install the dependency yarn add next-g11n or npm i next-g11n

  2. Create your dictionary.ts (or any other name, actually)

type default
object -

The translation object/map. It can either carry one locale, or all. The object format is as follows:

{
  'locale-key': {
    'translation-key': 'value - number or string'
  }
}
Copy/paste example
// example dictionary.ts
const dictionary = {
  // top level are locales (de) or (de-at), for example
  en: {
    // these are the translation keys
    hello: 'Hi',
  },
  'de-de': {
    hello: 'Hallo',
  },
  'de-at': {
    hello: 'GrรผรŸ Gott',
  },
}
  1. Choose between Client-Side-only or Server-Side translations

Helper methods ๐Ÿง 

getLocale

Receives a router instance from Next.js useRouter and returns the current locale if supported by your app, or the defaultLocale.

const router = useRouter()
const g11nLocale = getLocale(router) as Locales

Client-side only ๐Ÿช

When you call your hook, establish the types for the dictionary

import type { NextPage } from 'next'
import { useG11n } from 'next-g11n'
import { DICTIONARY } from '../dictionary'

const Home: NextPage = () => {
  const { translate: t } = useG11n<typeof DICTIONARY>(DICTIONARY)

  return (
    <h1>{t('hello')}</h1>
  )
}

โš ๏ธ this strategy will bring in the entire Dictionary to your bundle

useFallback

When true, if the translation does not exist in dictionary, the key will show. When set to false, it will throw an error.

type default
boolean false

โ€  recomended for apps without TypeScript

Server-Side Rendering ๐ŸŒค

Static Translations

Translations which do not require interpolation, can be completely rendered on the Server-Side within getStaticProps. An object with all translated terms will be injected in your page props.

const Home = ({ hello }: InferGetStaticPropsType<typeof getStaticProps>) => {
  const router = useRouter()
  const g11nLocale = getLocale(router) as Locales

  return <h1 className={styles.title}>{hello[g11nLocale]}</h1>
}

export const getStaticProps = async () => {
  const hello = createStaticTerm<Keys, Locales>('hello', DICTIONARY)

  return {
    props: {
      hello,
    },
  }
}

Interpolated Terms

To interpolate terms there are 2 methods required. On the server-side itโ€™s required to map all possible translation terms (still with template variables) by locale with createFunctionTerm. That object can then be passed down to each page props. Once arriving on the client-side, itโ€™s required to use clientSideTranslate to map each translation term to its method to interpolate the term with each variable and assign the proper types.

Why 2 methods?

Functions are not serializable out-of-the-box in JavaScript, and getStaticProps only passes down serialized object as its returned props. It is possible to call both methods on the server if you handle serializing the output functions. But then again, it will be required to deserializing them on the client-side.

createFunctionTerm (server-side)

This method will receive a translation key, and the whole dictionary. It will output a raw translation term (with uninterpolated variables). This is an object literal ready to be serialized by getStaticProps on its way to the client-side.

const rawTerm = createFunctionTerm('term', DICTIONARY)

clientSideTranslate (client-side)

Once on the client-side, this method will receive the raw term from createFunctionTerm and ouput one method for each supported locale. This method is a TypeScript generic, first parameter is your Locales exported from your whole dictionary, and the second is a string union with each available parameter for that translation term.

export type Locales = keyof typeof DICTIONARY

const Page = ({ rawTerm }: InferGetStaticPropsType<typeof getStaticProps>) => {
  const router = useRouter()
  const g11nLocale = getLocale(router) as Locales
  const term = clientSideTranslate<Locales, 'var1' | 'var2'>(rawTerm)

  return <h1>{term[g11nLocale]({ var1: 'foo', var2: 'bar' })}</h1>
}

Examples ๐Ÿฑ

directory description
client-side minimal setup for Client-side translations
server-side-render minimal setup for SSR-friendly translations

Additional info โ“

  • Support starts with Next.js i18n Routing, so v10+
  • Next.js is a peerDependency
  • React is a peerDependency

Glossary ๐Ÿ“–

abbreviation full word meaning
i18n internationalization enable adaptation of a product to multiple languages
L10n localization translating and customizing to a specific place
g11n globalization addressing requirements to launch globally