Skip to main content

@nimpl/i18n

(Former next-translation )

i18n library designed primarily with server components in mind and maximum optimization (due to the transfer of logic to the assembly stage and/or server side)

Installation 

npm i @nimpl/i18n

Why one more library? 

Server components are a recent feature in React. Existing translation libraries are not yet well-optimized for them. If they support it, then only by disabling Next.js static optimization.

This library is an attempt to create a highly optimized solution exclusively using the current capabilities of React.js, Next.js and node.js.

Usage 

getTranslation 

Server getter for adding translations to React Server Components and outside React DOM.

import getTranslation from '@nimpl/i18n/getTranslation';

const ServerComponent: React.FC = () => {
const { t } = getTranslation();

return (
<div>
{/* Welcome to Next Translation */}
{t('header.nav.home')}
</div>
)
}

You can also specify the desired language and translations will be added specifically for it:

const { t } = getTranslation({ language: 'fr' });

To add translations outside the React DOM, you can also use getTranslation, but always passing the desired language.

import createTranslation from '@nimpl/i18n/createTranslation'
// ...
export async function generateMetadata({ params }: { params: { lang: string } }) {
const { t } = await getTranslation({ language: params.lang });
return {
title: t('homePage.meta.title'),
}
}

You can pass opts as the second argument of t.

Now you can pass a query there to inject variable strings inside translations. In the translation, the variable should be isolated by two curly braces ({{example}} page title)

const Component: React.FC = () => {
return (
<div>
{/* Price starts from {{price}} */}
{t('pricing.tariffs.solo', { query: { price: '16.90' } })}
</div>
)
}

useTranslation 

Hook for adding translations to React Client Components.

To use i18n on the client side, you need to set up the I18nTransmitter
"use client";

import useTranslation from '@nimpl/i18n/useTranslation';

const ClientComponent: React.FC = () => {
const { t } = useTranslation();

return (
<div>
{/* Welcome to Next Translation */}
{t('header.nav.home')}
</div>
)
}
The client hook and component, unlike the server ones, do not support language customization. This is because translations are determined by server parents, as a result, the language is set in them.

You can pass opts as the second argument of t.

Now you can pass a query there to inject variable strings inside translations.

const Component: React.FC = () => {
return (
<div>
{/* Price starts from ${{price}} */}
{t('pricing.tariffs.solo', { query: { price: '16.90' } })}
</div>
)
}

I18nTransmitter 

For client-side translations to work, translations are passed in parent server components.

The I18nTransmitter is used for this. It takes an array of keys to namespaces or specific keys.

It is recommended to set it at the top level of the route. However, in some cases (for example in conditions for different sections) it may be useful to add a transmitter in another place.
import I18nTransmitter from '@nimpl/i18n/I18nTransmitter';
import ClientComponent from './ClientComponent';

const ServerComponent: React.FC = () => (
<I18nTransmitter terms={['header.nav']}>
<ClientComponent />
</I18nTransmitter>
)
Layout lives independently, so it needs to be wrapped in I18nTransmitter separately.

Also you can inject query to client terms on the server side. For example, when they depend on the server environment or when you get values from a database on the server.

Just add an array value instead of a string in terms arr, where the second element will be the query object.

import I18nTransmitter from '@nimpl/i18n/I18nTransmitter';
import ClientComponent from './ClientComponent';

const ServerComponent: React.FC = () => (
<I18nTransmitter terms={[['home.welcome', { stage: process.env.GITHUB_REF === 'main' ? 'production' : 'test' }]]}>
<ClientComponent />
</I18nTransmitter>
)

You can also specify the desired language and translations will be added specifically for it:

<I18nTransmitter terms={[['home.welcome']]} language="fr">
<ClientComponent />
</I18nTransmitter>

ServerTranslation 

A component for adding complex translations in React Server Components. Unlike getter, it supports adding components inside translations.

import ServerTranslation from '@nimpl/i18n/ServerTranslation';

const ServerComponent: React.FC = () => (
<ServerTranslation
term='intro.description' // We have {{number}} tariffs. Read more about pricings <link>special section</link>
components={{
link: <a href='#' />
}}
query={{ number: 5 }}
/>
)

You can also specify the desired language and translations will be added specifically for it:

<ServerTranslation
term='intro.description'
language="fr"
/>

ClientTranslation 

A component for adding complex translations in React Client Components. Unlike hooks, it supports adding components inside translations.

"use client";

import ClientTranslation from '@nimpl/i18n/ClientTranslation';

const ClientComponent: React.FC = () => (
<ClientTranslation
term='intro.description' // We have {{number}} tariffs. Read more about pricings <link>special section</link>
components={{
link: <a href='#' />
}}
query={{ number: 5 }}
/>
)
The ClientTranslation and client hook useTranslation, unlike server getTranslation and ServerTranslation, do not support language customization. This is because translations are determined by server parents, as a result, and the language is fixed in them.

Configuration 

Configuration file 

Create nimpl-i18n.js file in the app root directory

const fs = require("fs/promises");

/** @type {import('@nimpl/i18n/configuration/types').Config} */
module.exports = {
load: async (language) => {
const data = await fs.readFile(`./translates/${language}.json`);
const dictionary = JSON.parse(data);
return dictionary;
},
getLanguage: async ({ params }) => params.language,
languages: ['en', 'de', 'fr'],
};

Options 

load [required]

An asynchronous function that describes the logic of loading translations. Should return an object containing translations.

Function will receive 1 argument - language - current language;

languages [required]

Array of allowed languages. Languages outside of this array will be ignored.

getLanguage [required]

Функция для определения текущего языка. Принимает аргумент в виде объекта с ключами:

pathname - current route pathname;

params - current route segment params;

Configure app 

If you can transfer the package configuration file to the server in such a way that it remains readable - the instructions above will be enough.

However, if there is no way to transfer the file or the hosting has internal blocks (for example, Vercel considers this file unused and does not read it) - you need to wrap next.config.js in withI18n

const { default: i18n } = require('@nimpl/i18n/i18n');

const i18nWrapper = i18n();

/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = i18nWrapper(nextConfig);

App Organization 

Pages structure 

app
[lang]
page-name
page.tsx
page.tsx
layout.tsx
(root)
page-name
page.tsx
page.tsx
layout.tsx

Why so? 

You can only create one root layout. In the root (/app) we don't know the language, so we can't add a lang attribute there on the server side.

Therefore, the only way to do it is to create a root layout for localized pages in the [lang] folder.

type RootLayoutProps = { children: React.ReactNode; params: { lang: string } }

export default function RootLayout({ children, params }: RootLayoutProps) {
return (
<html lang={params.lang}>
<body>{children}</body>
</html>
)
}

Additional 

Please consider giving a star if you like it, it shows that the package is useful and helps continue work on this and other packages.

Create issues with wishes, ideas, difficulties, etc. All of them will definitely be considered and thought over.

Development 

Read about working on the package and making changes on the contribution page 

Examples 

Base example 

Previous
Server Getters
Next
Server Context
Return to navigation