If you're currently using next-intl in your Next.js application and want to migrate to Clocale for better localization management, this guide will walk you through the entire process. Clocale offers a powerful import feature that allows you to seamlessly import your existing next-intl JSON files, making the migration process smooth and efficient.
Before diving into the migration process, let's understand why Clocale might be a better choice for your localization needs:
Centralized Management : All translations are managed in one place with a user-friendly interface
Real-time Updates : Changes to translations are reflected immediately without redeployment
Team Collaboration : Non-technical team members can manage translations without touching code
Import Feature : Seamlessly import existing next-intl JSON files
Advanced Features : Namespace management, translation progress tracking, and more
Before starting the migration, ensure you have:
An existing Next.js project with next-intl
Your current translation JSON files
A Clocale account (sign up at clocale.com )
Let's first examine a typical next-intl setup. Your current project likely has a structure like this:
src/
├── i18n/
│ ├── locales/
│ │ ├── en/
│ │ │ └── common.json
│ │ ├── np/
│ │ │ └── common.json
│ │ └── ja/
│ │ └── common.json
│ ├── config.ts
│ └── request.ts
├── app/
│ └── layout.tsx
└── next.config.ts
next.config.ts
:
import { NextConfig } from "next/types" ;
import createNextIntlPlugin from "next-intl/plugin" ;
const withNextIntl = createNextIntlPlugin ();
const nextConfig : NextConfig = {
// your config
};
export default withNextIntl (nextConfig);
src/i18n/request.ts
:
import { getRequestConfig } from "next-intl/server" ;
import { getCookie } from "@/actions/cookies" ;
export default getRequestConfig ( async () => {
let locale = await getCookie ( "language" );
if ( ! locale) {
locale = "en" ;
}
const messages = {
... ( await import ( `./locales/${ locale }/common.json` )).default,
};
return {
locale,
messages,
};
});
src/app/layout.tsx
:
import { NextIntlClientProvider } from "next-intl" ;
import { getLocale, getMessages } from "next-intl/server" ;
export default async function RootLayout ({
children ,
} : {
children : React . ReactNode ;
}) {
const locale = await getLocale ();
const messages = await getMessages ();
return (
< html lang = {locale} >
< body >
< NextIntlClientProvider messages = {messages} >
{ children }
</ NextIntlClientProvider >
</ body >
</ html >
);
}
First, install Clocale:
# Install Clocale
npm install @clocale/react
Sign up/login to your Clocale account
Create a new project
Add the languages you want to support (e.g., English, Japanese, Nepali)
Note down your project's integration URL from the developer settings
Create or update your .env.local
file:
CLOCALE_API_URL = https://api.clocale.com/YOUR-PROJECT-ID
Replace YOUR-PROJECT-ID
with your actual Clocale project ID.
This is where Clocale's powerful import feature comes into play. You can import your existing next-intl JSON files directly into Clocale.
Your current next-intl JSON files should look something like this:
src/i18n/locales/en/common.json
:
{
"home_page" : {
"title" : "Home" ,
"hero_main" : "Making Translation Effortless and Accessible" ,
"description" : "Make your website or app available in multiple languages without the technical hassle." ,
"get_started" : "Get Started"
},
"login_page" : {
"title" : "Welcome back" ,
"email" : "Email" ,
"password" : "Password" ,
"login" : "Sign in" ,
"forgot_password" : "Forgot password?"
},
"navbar" : {
"feature" : "Features" ,
"pricing" : "Pricing" ,
"blog" : "Blog" ,
"login" : "Login" ,
"signup" : "Signup"
}
}
src/i18n/locales/ja/common.json
:
{
"home_page" : {
"title" : "ホーム" ,
"hero_main" : "翻訳をもっと簡単で身近に" ,
"description" : "技術的な手間なく、あなたのウェブサイトやアプリを複数の言語で利用可能にしましょう。私たちのサービスは翻訳を保存・管理し、開発者の時間を無駄にせず、あなたのコンテンツを世界中に届けます。" ,
"get_started" : "始める"
},
"login_page" : {
"title" : "おかえり" ,
"email" : "メール" ,
"password" : "パスワード" ,
"login" : "サインイン" ,
"forgot_password" : "パスワードを忘れましたか?"
},
"navbar" : {
"feature" : "特徴" ,
"pricing" : "価格設定" ,
"blog" : "ブログ" ,
"login" : "サインイン" ,
"signup" : "サインアップ"
}
}
Go to your Clocale project dashboard
Navigate to the "Import" section
Click "Select Files" and choose your JSON files
For each file, select:
Language : The language of the file (e.g., English, Japanese, Nepali)
Namespace : The namespace (e.g., "common")
Click "Import" to upload your translations
The import interface allows you to easily upload your existing next-intl JSON files:
Remove the next-intl plugin from your next.config.ts
:
import { NextConfig } from "next/types" ;
const nextConfig : NextConfig = {
// your config without next-intl plugin
};
export default nextConfig;
Create a new file src/lib/clocale-provider.tsx
:
'use client' ;
import { ClocaleProvider, SSRTranslations } from '@clocale/react' ;
interface ClocaleTranslationProviderProps {
locale : string ;
children : React . ReactNode ;
ssrTranslations : SSRTranslations ;
isDev : boolean ;
}
export const ClocaleTranslationProvider = ({
children,
locale,
ssrTranslations,
isDev,
} : ClocaleTranslationProviderProps ) => {
return (
< ClocaleProvider
type = "server"
locale = {locale}
ssrTranslations = {ssrTranslations}
isDev = {isDev}
>
{ children }
</ ClocaleProvider >
);
};
Create src/lib/locale.ts
:
"use server" ;
import { cookies } from "next/headers" ;
const COOKIE_NAME = "language" ;
export const getLocale = async () => {
const cookieStore = await cookies ();
return cookieStore. get ( COOKIE_NAME )?.value ?? "en" ;
};
export const setLocale = async ( locale : string ) => {
const cookieStore = await cookies ();
cookieStore. set ( COOKIE_NAME , locale);
};
Create src/lib/clocale-client.ts
:
import { ClocaleNext, createServerClient } from "@clocale/react/next" ;
import { getLocale } from "./locale" ;
export const { getTranslation , getClocaleClient } = createServerClient ({
getLocale,
builder : async ( locale ) =>
ClocaleNext (). init ({
locale,
baseUrl: process.env.Clocale_API_URL ! ,
}),
});
Replace your src/app/layout.tsx
:
import type { Metadata } from "next" ;
import { Roboto, Roboto_Slab } from "next/font/google" ;
import { NuqsAdapter } from "nuqs/adapters/next/app" ;
import { Toaster } from "sonner" ;
import { ClocaleTranslationProvider } from "@/lib/clocale-provider" ;
import { getLocale } from "@/lib/locale" ;
import { getClocaleClient } from "@/lib/clocale-client" ;
import LanguageFloat from "@/components/language-float" ;
import QueryProvider from "@/lib/tanstack-query/provider" ;
import "./globals.css" ;
const robotoSlab = Roboto_Slab ({
weight: [ "100" , "200" , "300" , "400" , "500" , "600" , "700" , "800" , "900" ],
subsets: [ "latin" ],
variable: "--font-roboto-slab" ,
});
const roboto = Roboto ({
weight: [ "100" , "200" , "300" , "400" , "500" , "600" , "700" , "800" , "900" ],
subsets: [ "latin" ],
variable: "--font-roboto" ,
});
export const metadata : Metadata = {
title: "Clocale" ,
description: "Generated by create next app" ,
};
export default async function RootLayout ({
children ,
} : Readonly <{
children : React . ReactNode ;
}>) {
const locale = await getLocale ();
const clocale = await getClocaleClient ();
// Fetch translations for all namespaces used in your project
const translations = await clocale. fetchTranslations ([ 'common' ]);
return (
< html
translate = "no"
lang = {locale}
className = { `${ roboto . variable } ${ robotoSlab . variable } antialiased` }
suppressHydrationWarning
>
< body >
< ClocaleTranslationProvider
locale = {locale}
ssrTranslations = {translations}
isDev = {process.env. NODE_ENV === 'development' }
>
< NuqsAdapter >
< QueryProvider >
{ children }
< LanguageFloat />
</ QueryProvider >
</ NuqsAdapter >
</ ClocaleTranslationProvider >
< Toaster richColors />
</ body >
</ html >
);
}
Replace getTranslations
from next-intl with Clocale's getTranslation
:
// Before (next-intl)
import { getTranslations } from "next-intl/server" ;
export default async function Home () {
const t = await getTranslations ( "common.home_page" );
return (
< div >
< h1 >{ t ( "title" )} </ h1 >
< p >{ t ( "description" )} </ p >
</ div >
);
}
// After (Clocale)
import { getTranslation } from "@/lib/clocale-client" ;
export default async function Home () {
const t = await getTranslation ( "common.home_page" );
return (
< div >
< h1 >{ t ( "title" )} </ h1 >
< p >{ t ( "description" )} </ p >
</ div >
);
}
Replace useTranslations
from next-intl with Clocale's useTranslation
:
// Before (next-intl)
"use client" ;
import { useTranslations } from "next-intl" ;
export default function Navbar () {
const t = useTranslations ( "common.navbar" );
return (
< nav >
< a href = "/features" > { t ( "feature" )} </ a >
< a href = "/pricing" > { t ( "pricing" )} </ a >
< a href = "/blog" > { t ( "blog" )} </ a >
</ nav >
);
}
// After (Clocale)
"use client" ;
import { useTranslation } from "@clocale/react" ;
export default function Navbar () {
const t = useTranslation ( "common.navbar" );
return (
< nav >
< a href = "/features" > { t ( "feature" )} </ a >
< a href = "/pricing" > { t ( "pricing" )} </ a >
< a href = "/blog" > { t ( "blog" )} </ a >
</ nav >
);
}
Update your language switcher component:
"use client" ;
import { useTranslation } from "@clocale/react" ;
import { setLocale } from "@/lib/locale" ;
export default function LanguageSwitcher () {
const { locale , setLocale : setLocaleClient } = useTranslation ();
const handleLanguageChange = async ( newLocale : string ) => {
await setLocale (newLocale); // Update server-side cookie
setLocaleClient (newLocale); // Update client-side state
window.location. reload (); // Reload to apply new locale
};
return (
< div >
< button onClick = {() => handleLanguageChange ( "en" )} > English </ button >
< button onClick = {() => handleLanguageChange ( "ja" )} > 日本語 </ button >
< button onClick = {() => handleLanguageChange ( "np" )} > नेपाली </ button >
</ div >
);
}
After successful migration, you can remove the old next-intl files:
# Remove old i18n directory
rm -rf src/i18n
Test all pages to ensure translations are working correctly
Check language switching functionality
Verify that new translations can be added through the Clocale dashboard
Test the import feature by adding new translations
Clocale supports multiple namespaces for better organization:
// Create different namespaces for different features
const translations = await clocale. fetchTranslations ([
"common" ,
"auth" ,
"dashboard" ,
]);
// Use in components
const t = useTranslation ( "auth.login_page" );
const dashboardT = useTranslation ( "dashboard.overview" );
Monitor your translation completion in the Clocale dashboard:
View completion percentages by language
Identify missing translations
Track translation updates
With Clocale, you can update translations without redeploying your application:
Make changes in the Clocale dashboard
Changes are immediately available to your users
No need to rebuild and redeploy
Translations not loading : Check your CLOCALE_API_URL
environment variable
Language switching not working : Ensure both server and client locale updates are implemented
Import failures : Verify JSON structure matches Clocale's expected format
Enable debug mode during development:
< ClocaleTranslationProvider
locale = {locale}
ssrTranslations = {translations}
isDev = {true} // Enable debug mode
>
Migrating from next-intl to Clocale opens up a world of possibilities for managing your application's localization. The import feature makes the transition seamless, allowing you to preserve all your existing translations while gaining access to powerful new features.
Key benefits of the migration:
Seamless import of existing next-intl JSON files
Centralized translation management through web interface
Real-time updates In-Context editing
Advanced features like namespace management and progress tracking
The migration process is straightforward and the import feature ensures you don't lose any of your existing work. Once migrated, you'll have a much more powerful and user-friendly localization system that scales with your application's needs.
Start your migration today and experience the difference that Clocale can make in your localization workflow!