npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@autushka/next-translate

v0.16.0-canary.3

Published

Next.js utility to translate pages without the need of a server (static i18n pages generator).

Downloads

2

Readme

npm version PRs Welcome Join the community on Spectrum

1. About next-translate

Next-translate is a tool to translate Next.js pages.

The main goal of this library is to keep the translations as simple as possible in a Next.js environment.

This library is very tiny and tree shakable.

How is this lib handling the routes?

Instead of working on /pages directory to write our pages, we are going to generate this folder before building the app, and each page will have all the necessary translations from the locale.

Imagine that we are working in an alternative /pages_ to build our pages:

/pages_

.
├── about.js
├── index.js
└── nested
    └── index.js

Then, when we build the app, this /pages structure is going to be automatically generated:

.
├── about.js
├── ca
│   ├── about.js
│   ├── index.js
│   └── nested
│       └── index.js
├── en
│   ├── [...path].js
├── es
│   ├── about.js
│   ├── index.js
│   └── nested
│       └── index.js
├── index.js
└── nested
    └── index.js

Note: /en/[...path].js is a redirect from /en/some-route to /some-route

Each page and its components can consume the translations with the useTranslation hook.

const { t, lang } = useTranslation()
const title = t('common:title')

2. Getting started

This is the recommended way to get started. However, if you don't like the "build step" you can use an alternative.

Install

  • yarn add next-translate

Note: For a Next.js version below than 9.3.0, use [email protected] or below

In your package.json:

"scripts": {
  "dev": "next-translate && next dev",
  "build": "next-translate && next build",
  "start": "next start"
}

Use translations in your pages

You should create your namespaces files inside /locales. See how to do it

Add a configuration file i18n.json in the root of the project. Each page should have its namespaces. Take a look to the config section for more details.

{
  "allLanguages": ["en", "ca", "es"],
  "defaultLanguage": "en",
  "currentPagesDir": "pages_",
  "finalPagesDir": "pages",
  "localesPath": "locales",
  "pages": {
    "*": ["common"],
    "/": ["home", "example"],
    "/about": ["about"]
  }
}

Then, use the translations in the page and its components:

import useTranslation from 'next-translate/useTranslation'
// ...
const { t, lang } = useTranslation()
const example = t('common:variable-example', { count: 42 })
// ...
return <div>{example}</div>

⚠️ Important: _app.js, _document.js and _error.js are not going to be wrapped with the translations context, so it's not possible to direclty translate these files. In order to do that, you should take a look at DynamicNamespaces to load the namespaces dynamically.

Add /pages to .gitignore

/pages directory is going to be generated every time based on /pages_, so it's not necessary to track it in git.

3. Translation JSONs folder

The /locales directory should be like this:

/locales

.
├── ca
│   ├── common.json
│   └── home.json
├── en
│   ├── common.json
│   └── home.json
└── es
    ├── common.json
    └── home.json

Each filename matches the namespace, while each file content should be similar to this:

{
  "title": "Hello world",
  "variable-example": "Using a variable {{count}}"
}

In order to use each translation in the project, use the translation id composed by namespace:key(ex: common:variable-example).

4. Configuration

| Option | Description | Type | Default | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | -------------------------------------------------------------------------- | | defaultLanguage | ISO of the default locale ("en" as default). | string|function | "en" | | allLanguages | An array with all the languages to use in the project. | Array<string> | [] | | ignoreRoutes | An array with all the routes to ignore in the middleware. This config property only effects using the i18nMiddleware, SO MAYBE YOU'LL NEVER NEED THIS. | Array<string> | ['/_next/', '/static/', '/favicon.ico', '/manifest.json', '/robots.txt'] | | redirectToDefaultLang | When it's set to true, the route /some-page redirects to /en/some-path (if en is the default language). When it's set to false, entering to /some-path renders the page with the default language and the route /en/some-path redirects to /some-page. | boolean | false | | currentPagesDir | A string with the directory where you have the pages code. This is needed for the "build step". | string | "pages_" | | finalPagesDir | A string with the directory that is going to be used to build the pages. Only "pages" and "src/pages" are possible. This is needed for the "build step". | string | "pages" | | localesPath | A string with the directory of JSONs locales. This is needed for the "build step". | string | "locales" | | loadLocaleFrom | As an alternative to localesPath, if i18nMiddleware is used instead of the "build step". It's an async function that returns the dynamic import of each locale. See an example | Function | null | | pages | An object that defines the namespaces used in each page. Example of object: {"/": ["home", "example"]}. To add namespaces to all pages you should use the key "*", ex: {"*": ["common"]}. It's also possible to use regex using rgx: on front: {"rgx:/form$": ["form"]}. In case of using a custom server as an alternative of the "build step", you can also use a function instead of an array, to provide some namespaces depending on some rules, ex: { "/": ({ req, query }) => query.type === 'example' ? ['example'] : []} | Object<Array<string>/Function | {} | | logBuild | Configure if the build result should be logged to the console | Boolean | true |

5. API

useTranslation

📦Size: ~614b

This hook is the recommended way to use translations in your pages / components.

  • Input: void
  • Output: Object { t: Function, lang: string }

Example:

import React from 'react'
import useTranslation from 'next-translate/useTranslation'

export default function Description() {
  const { t, lang } = useTranslation()
  const title = t('common:title')
  const description = t`common:description` // also works as template string
  const example = t('common:example', { count: 3 }) // and with query params

  return (
    <>
      <h1>{title}</h1>
      <p>{description}</p>
      <p>{example}</p>
    <>
  )
}

The t function:

  • Input:
    • i18nKey: string (namespace:key)
    • query: Object (example: { name: 'Leonard' })
  • Output: string

withTranslation

📦Size: ~759b

It's an alternative to useTranslation hook, but in a HOC for these components that are no-functional.

The withTranslation HOC returns a Component with an extra prop named i18n (Object { t: Function, lang: string }).

Example:

import React from 'react'
import withTranslation from 'next-translate/withTranslation'

class Description extends React.Component {
  render() {
    const { t, lang } = this.props.i18n
    const description = t('common:description')

    return <p>{description}</p>
  }
}

export default withTranslation(NoFunctionalComponent)

Trans Component

📦Size: ~1.5kb

Sometimes we need to do some translations with HTML inside the text (bolds, links, etc), the Trans component is exactly what you need for this. We recommend to use this component only in this case, for other cases we highly recommend the usage of useTranslation hook instead.

Example:

// The defined dictionary enter is like:
// "example": "<0>The number is <1>{{count}}</1></0>",
<Trans
  i18nKey="common:example"
  components={[<Component />, <b className="red" />]}
  values={{ count: 42 }}
/>
  • Props:
    • i18nKey - string - key of i18n entry (namespace:key)
    • components - Array - Each index corresponds to the defined tag <0>/<1>.
    • values - Object - query params

appWithI18n

📦Size: ~4.7kb

Using the "build step" you'll never need this.

This HOC is the way to wrap all your app under translations in the case that you are using a custom server as an alternative to the "build step", adding logic to the getInitialProps to download the necessary namespaces in order to use it in your pages.

Example:

_app.js

import appWithI18n from 'next-translate/appWithI18n'
import i18nConfig from '../i18n'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default appWithI18n(MyApp, i18nConfig)

See more details about the config you can use.

DynamicNamespaces

📦Size: ~4.1kb

The DynamicNamespaces component is useful to load dynamic namespaces, for example, in modals.

Example:

import React from 'react'
import Trans from 'next-translate/Trans'
import DynamicNamespaces from 'next-translate/DynamicNamespaces'

export default function ExampleWithDynamicNamespace() {
  return (
    <DynamicNamespaces
      dynamic={(lang, ns) =>
        import(`../../locales/${lang}/${ns}.json`).then((m) => m.default)
      }
      namespaces={['dynamic']}
      fallback="Loading..."
    >
      {/* ALSO IS POSSIBLE TO USE NAMESPACES FROM THE PAGE */}
      <h1>
        <Trans i18nKey="common:title" />
      </h1>

      {/* USING DYNAMIC NAMESPACE */}
      <Trans i18nKey="dynamic:example-of-dynamic-translation" />
    </DynamicNamespaces>
  )
}

Remember that ['dynamic'] namespace should not be listed on pages configuration:

 pages: {
    '/my-page': ['common'], // only common namespace
  }

I18nProvider

📦Size: ~1.8kb

The I18nProvider is a context provider internally used by next-translate to provide the current lang and the page namespaces. SO MAYBE YOU'LL NEVER NEED THIS.

However, it's exposed to the API because it can be useful in some cases. For example, to use multi-language translations in a page.

The I18nProvider is accumulating the namespaces, so you can rename the new ones in order to keep the old ones.

import React from 'react'
import I18nProvider from 'next-translate/I18nProvider'
import useTranslation from 'next-translate/useTranslation'

// Import English common.json
import commonEN from '../../locales/en/common.json'

function PageContent() {
  const { t, lang } = useTranslation()

  console.log(lang) // -> current language

  return (
    <div>
      <p>{t('common:example') /* Current language */}</p>
      <p>{t('commonEN:example') /* Force English */}</p>
    </div>
  )
}

export default function Page() {
  const { lang } = useTranslation()

  return (
    <I18nProvider lang={lang} namespaces={{ commonEN }}>
      <PageContent />
    </I18nProvider>
  )
}

i18nMiddleware

📦Size: ~1.4kb

If you are using Automatic Static Optimization, you don't need this. This middleware is an alternative to the "build step".

const i18nMiddleware = require('next-translate/i18nMiddleware').default
const i18nConfig = require('./i18n')

// ...
server.use(i18nMiddleware(i18nConfig))

See more details about the config you can use.

Props:

  • dynamic - Function - Generic dynamic import of all namespaces (mandatory).
  • namespaces - Array - List of namespaces to load dynamically (mandatory).
  • fallback - Any - Fallback to render meanwhile namespaces are loading (default: null)

Link

📦Size: ~11kb (next/link size included)

It's a wrapper of next/link that adds the current language at the beginning of the path, so you don't have to worry to add the language in every navigation. In order to change the language, you can pass the lang as props:

import Link from 'next-translate/Link'

// If the current language is 'en':

// -> Navigate to /en/some-path
<Link href="/some-path"><a>Navigate</a></Link>

 // -> Navigate to /es/route-in-spanish
<Link href="/route-in-spanish" lang="es"><a>Navigate</a></Link>

// -> Navigate to /some-path
<Link noLang href="/some-path"><a>Navigate</a></Link>

Props: Same props than next/link + only one additional prop:

  • lang: <String> prop useful to navigate to a different language than the current one. The default value, if this prop is not provided, is the current language. So you don't need to worry about passing this prop for normal navigation.
  • noLang: <Boolean> prop to disable appending the current language to the route.

Router

📦Size: ~10kb (next/router size included)

It's a wrapper of next/router so you can use the normal router of next.js, adding two extra methods:

  • Router.pushI18n: It is exactly the same as Router.push, except that it adds the current language at the beginning of the URL. In order to change the language, you can pass the lang into the options.
  • Router.replaceI18n: It is exactly the same as Router.replace, with the difference that it adds the current language at the beginning of the URL. In order to change the language, you can pass the lang into the options.
import Router from 'next-translate/Router'

// If the current language is 'en':

// -> Navigate to /en/some-path
Router.pushI18n('/some-path')

// -> Navigate to /es/route-in-spanish
Router.pushI18n({ url: '/route-in-spanish', options: { lang: 'es' } })
// or
Router.pushI18n('/route-in-spanish', undefined, { lang: 'es' })

clientSideLang

📦Size: ~590b

Useful to get the language outside Components.

import clientSideLang from 'next-translate/clientSideLang'

// ...

export function myClientSideHelper() {
  const lang = clientSideLang()
  // ...
}

It is not recommended to use the clientSideLang directly on the server-side because it's stored in a global variable and it can cause some concurrency issues.

6. Plurals

You can define plurals this way:

{
  "plural-example": "This is singular because the value is {{count}}",
  "plural-example_0": "Is zero because the value is {{count}}",
  "plural-example_2": "Is two because the value is {{count}}",
  "plural-example_plural": "Is in plural because the value is {{count}}"
}

Example:

function PluralExample() {
  const [count, setCount] = useState(0)
  const { t } = useTranslation()

  useEffect(() => {
    const interval = setInterval(() => {
      setCount((v) => (v === 5 ? 0 : v + 1))
    }, 1000)

    return () => clearInterval(interval)
  }, [])

  return <p>{t('namespace:plural-example', { count })}</p>
}

Result:

plural

Note: Only works if the name of the variable is {{count}}.

7. Use HTML inside the translation

You can define HTML inside the translation this way:

{
  "example-with-html": "<0>This is an example <1>using HTML</1> inside the translation</0>"
}

Example:

import Trans from 'next-translate/Trans'
// ...
const Component = (props) => <p {...props} />
// ...
<Trans
  i18nKey="namespace:example-with-html"
  components={[<Component />, <b className="red" />]}
/>

Rendered result:

<p>This is an example <b class="red">using HTML</b> inside the translation</p>

Each index of components array corresponds with <index></index> of the definition.

In the components array, it's not necessary to pass the children of each element. Children will be calculated.

8. Nested translations

In the namespace, it's possible to define nested keys like this:

{
  "nested-example": {
    "very-nested": {
      "nested": "Nested example!"
    }
  }
}

In order to use it, you should use "." as id separator:

t`namespace:nested-example.very-nested.nested`

9. How to change the language

In order to change the current language you don't need anything of this library, you can do it directly with the next navigation:

  • https://nextjs.org/learn/basics/navigate-between-pages

The only thing to remember is to navigate with the /lang/ on front.

An example of a possible ChangeLanguage component:

import React from 'react'
import Link from 'next-translate/Link'
import useTranslation from 'next-translate/useTranslation'
import i18nConfig from '../i18n.json'

const { allLanguages } = i18nConfig

function ChangeLanguage() {
  const { t, lang } = useTranslation()

  return allLanguages.map((lng) => {
    if (lng === lang) return null

    // Or you can attach the current pathame at the end
    // to keep the same page
    return (
      <Link href="/" lang={lng} key={lng}>
        {t(`layout:language-name-${lng}`)}
      </Link>
    )
  })
}

10. Get language in the special Next.js functions

If you are using an alternative to the "build step", this section is not applicable. In order to use the lang in the special Next.js functions, the lang property is added to the context.

getStaticProps

export async function getStaticProps({ lang }) {
  return {
    props: {
      data: fetchMyDataFromLang(lang),
    },
  }
}

See here the official Next.js docs about getStaticProps.

getStaticPaths

export async function getStaticPaths({ lang }) {
  return {
    paths: generatePathsFromLang(lang),
    fallback: false,
  }
}

See here the official Next.js docs about getStaticPaths.

getServerSideProps

export async function getServerSideProps({ lang }) {
  return {
    props: {
      data: queryDataFromDB(lang),
    },
  }
}

See here the official Next.js docs about getServerSideProps

getInitialProps

Recommended: Use getStaticProps or getServerSideProps instead.

MyPage.getInitialProps = async ({ lang }) => {
  return {
    data: fetchMyDataFromLang(lang),
  }
}

See here the official Next.js docs about getInitialProps

11. How to use multi-language in a page

In some cases, when the page is in the current language, you may want to do some exceptions displaying some text in another language.

In this case, you can achive this by using the I18nProvider.

Learn how to do it here.

12. Do I need this "build step"? Is there an alternative?

The "build step" exists only to simplify work with Automatic Static Optimization, so right now it is the recommended way. However, if you prefer not to do the "build step", there are two alternatives.

First alternative

You can achive the same with dynamic routes.

Pros and cons:

  • 🟢 Automatic Static Optimization
  • 🔴 Hard to configure

See a full example here

In future major releases, we may evolve simplifying this and removing the "build step". If you want to help on this, there is an open issue here to discuss.

Second alternative

If you don't need Automatic Static Optimization in your project, you can achive the same by using a custom server.

Pros and cons:

  • 🔴 Automatic Static Optimization is not an option
  • 🟢 Easy to configure

Learn more: Docs · Example

13. Demos

Using the "build step"

  • yarn install
  • yarn example:static-site

Using an alternative to the build step: dynamic routes

  • yarn install
  • yarn example:with-dynamic-routes

Using an alternative to the build step: custom server

  • yarn install
  • yarn example:with-server

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!