next-intl-translations
v0.0.9
Published
Automate and streamline all aspects of translation management for scaling projects built with next-intl, from organizing files to adding new languages and migrating from legacy approaches.
Readme
Internationalization (i18n) for Next.js
README in Russian | README in Chinese
Features
Automate and streamline all aspects of translation management for scaling projects built with next-intl, from organizing files to adding new languages and migrating from legacy approaches.
next-intl-translations complements the capabilities of next-intl by extending and adding the following features:
- 🧩 Distributed Translations: Place translations directly in components for maximum clarity and ease of support.
- ✅ Type-safe: Avoid translation errors by using typed
translations.ts, which ensure that translations between locales match directly in the code, complementing the typesecurity ofnext-intl.
Explanations
Using next-intl-translations, you can easily scale a project when working with translations.
Usually, all translations are stored in the messages section files. The larger the project, the more the messages files grow, which makes it difficult to manage translations. For example, when deleting any code, it is necessary to find and delete the corresponding translations, otherwise they will remain in the messages files, thereby further increasing their size.
This library works with next-intl, inspired by next-intl-split. The idea of next-intl-translations is to store messages for different locales in a single translations file located next to the corresponding component. The library then collects these translations into the messages files necessary for the operation of next-intl. And when you delete a component from its translations, they will also be deleted in the messages files.
Getting Started
Install the dependencies:
npm install next-intl next-intl-splitnpm install next-intl-translations -DConfigure the
next-intlin yournext.config.js, provider and other settings according to documentation:// next.config.js import type { NextConfig } from 'next'; import createNextIntlSplitPlugin from 'next-intl-split/plugin'; const withNextIntlSplit = createNextIntlSplitPlugin( './messages', './src/shared/lib/i18n/request.ts', ); const nextConfig: NextConfig = { /* config options here */ }; export default withNextIntlSplit(nextConfig);Specify the available languages:
// src/lib/i18n/config.ts export const LOCALES = ['ru', 'en', 'zh'] as const;Specify the type
TLocalesinglobal.d.tsso that it works in thetranslations.tsfiles, or your version starts with[email protected], then in theglobal.tsfile:// global.d.ts declare type IntlMessages = typeof import('@/messages/en.json'); type TLocale = (typeof import('@/src/shared/lib/i18n/config').LOCALES)[number]; declare type TLocales<T> = { [key in TLocale]: T; };// global.ts // Начиная с версии [email protected] import messages from './messages/en.json'; import type { Locale } from '@/src/shared/lib/i18n/config'; declare module 'next-intl' { interface AppConfig { Messages: typeof messages; } } declare global { type TLocales<T> = { [key in Locale]: T; }; }Create the file
translations.ts(you can use the formatsjsandjson):const en = { logo: 'Logo', form: 'Form', }; const zh = { logo: '标志', form: '表格', } satisfies typeof en; const ru = { logo: 'Логотип', form: 'Форма', } satisfies typeof en; export default { en, zh, ru } satisfies TLocales<typeof en>;To avoid mistakes and ensure consistency of translations, the structure of the first language (
en) becomes the type (typeof en) for all others (zh,ru). This allows TypeScript to check for all necessary keys in translations for each language directly in the code. satisfies TLocales completes the process by verifying that all the languages specified in the project (defined in TLocales) have their own translations.After launching and updating the page, the
messages/directory files will be updated according to thetranslationsfiles.Enable
messages. In theproductioncondition, we use staticmessages/files, and in thedevelopmentwe dynamically download translations from thetranslationsfiles:// src/lib/i18n/request.ts import { getRequestConfig } from 'next-intl/server'; import { getUserLocale } from './locale'; import { loadTranslationsForDev } from './load-dev'; export default getRequestConfig(async () => { const locale = await getUserLocale(); const messages = (process.env.NODE_ENV === 'production' ? (await import(`@/messages/${locale}.json`)).default : await loadTranslationsForDev()) || {}; return { locale, messages, }; });Configure the
messagesloader fordevelopment:// src/lib/i18n/load-dev.ts import { loadTranslations } from 'next-intl-translations'; import { LOCALES } from './config'; import { getUserLocale } from './locale'; export const loadTranslationsForDev = async () => { const locale = await getUserLocale(); return await loadTranslations({ currentLocale: locale, locales: [...LOCALES], enableTypeCheck: true, deepTranslationComparison: true, loaderFileScript: async (file) => { let translations: Record<string, any> | null = null; await import(`@/src/${file.replace(/\\/g, '/')}`) .then((module) => { translations = module.default; }) .catch((error) => { console.error(`Error importing or parsing ${file}:`, error); }); return translations; }, }); };extractionPath(default is'./src'): the path from where the search fortranslationsfiles will startOutputPath(default is'./messages'): the section where themessagesfiles will arriveenableTypeCheck' (default istrue): determines whether the translation filesmessagesshould be written to themessages` directorycurrentLocale: current localelocales: list of project localesdeepTranslationComparison: additional verification of all translations, relevant forjson,js. Less relevant fortsif you follow the typesloaderFileScript: Specifiesloaderso that translations from thetsandjsfiles can be imported. If omitted, only thejsonfiles will be read.translationsFileName(default istranslations): file names with translations
Use translations the same way as in
next-intl:// src/pages/home import { useTranslations } from 'next-intl'; export function HomePage() { const t = useTranslations('pages.home'); return ( <main> <h1>{t('logo')}</h1> <form> <h2>{t('form')}</h2> </form> </main> ); }
When working with translations.json
In addition to working with the ts and js files, you can work with translations.json. The file itself should look like this:
{
"en": {
"logo": "Logo",
"form": "Form"
},
"zh": {
"logo": "标志",
"logoFull": "表格"
},
"ru": {
"logo": "Логотип",
"form": "Форма"
}
}To work with the json format, you don't need to specify `loaderFileScript'.
Adding a new language
In the case of dividing translations into multiple translations.ts, it becomes difficult to add new translations, in such cases there is a special script:
npx extract-translationsYou need to add a new locale to messages/, for example de.json and translate the values for all keys, then run the extract-translations script. Translations for de will be added to the existing translations.ts (json, js) files.
Additional parameters, they need to be specified in .env:
NIT_MESSAGES_DIR(default is'./messages'): the section from where translations ofmessageswill be extractedNIT_EXTRACTION_PATH(default is'src'): the section from which thetranslationsfiles should be locatedNIT_TRANSLATIONS_FILE_NAME(default istranslations): file names with translationsNIT_LOG_OUTPUT(default istrue): whether to show logs in the consoleNIT_NEW_TRANSLATIONS_FILE(default isfalse): it will create newtranslations.tsif it doesn't exist (more details about this below)
Migration from messages/[locale].json in src/**/**/translations.ts
In case the project uses the standard next-intl approach with messages/[locale].json, you can also create split translation files, it works in a similar way as with the point above, only the problem is that the translations.ts files do not exist yet. But using the npx extract-translations script and the NIT_NEW_TRANSLATIONS_FILE=true variable, you can create these files.
You need to create a directory structure so that the translation keys match them.
For example, for these transfer keys:
{
"pages": {
"home": {
"form": {
...
}
},
...
}
}If you need the translations.ts file to be created in the home directory, then you need to create such a structure:
pages/
└── home/
└── translations.ts // It will be created hereIf you need to create a translations.ts in a form, then this:
pages/
└── home/
└── form/
└── translations.ts // It will be created hereAnd this is how you need to create for all transfer keys. But be careful with a large number of translations, as the creation of translations.ts can be unpredictable.
Planned
VS Code Plugin: Next-Intl-Translations Generator
The ability to quickly create translations.ts in a directory with all locales and types.
Glossarium
messages: Translations for one language (en,ru). Thenext-intlis used in the application. Files:messages/en.json,messages/ru.json.translations: Translations for all languages. The data source for generatingmessages. File:translations.ts(ortranslations.json).
