Skip to content
better-i18n.com
इस पेज पर

Complete API reference for the Next.js SDK.

Architecture #

@better-i18n/next is built on top of @better-i18n/core and adds Next.js-specific optimizations:

  • ISR (Incremental Static Regeneration) - Automatic cache revalidation via next.revalidate
  • Manifest caching - 1 hour default (3600s)
  • Messages caching - 30 seconds default for fast updates

Middleware #

The modern way to define middleware with Clerk-style callback pattern for auth integration.

TypeScript
import { createBetterI18nMiddleware } from "@better-i18n/next";
import { NextResponse } from "next/server";

// Simple usage (no callback)
export default createBetterI18nMiddleware({
  projectId: "org/project",
  defaultLocale: "en",
  localePrefix: "always",
});

// With callback (recommended for auth)
export default createBetterI18nMiddleware({
  projectId: "org/project",
  defaultLocale: "en",
  localePrefix: "always",
}, async (request, { locale, response }) => {
  // Auth logic here - locale is available!
  if (needsLogin) {
    return NextResponse.redirect(new URL(`/${locale}/login`, request.url));
  }
  // Return nothing = i18n response is used (headers preserved!)
});

Config Options:

OptionTypeDefaultDescription
projectstringRequiredProject identifier in org/project format
defaultLocalestringRequiredFallback locale
localePrefix"always" | "as-needed" | "never""as-needed"URL locale prefix behavior
detection.cookiebooleantrueEnable cookie-based detection
detection.browserLanguagebooleantrueEnable Accept-Language header detection
detection.cookieNamestring"locale"Cookie name for storing preference
detection.cookieMaxAgenumber31536000Cookie max age in seconds
detection.explicitCookieNamestring"locale_explicit"Cookie marking a deliberate user choice (read-only in middleware)

Callback Context:

PropertyTypeDescription
localestringDetected locale (e.g., "en", "tr")
detectedFrom"path" | "cookie" | "geo" | "header" | "default"Where the locale was resolved from
isExplicitbooleantrue when the explicit-marker cookie is present (deliberate user choice)
responseNextResponsei18n response with headers already set

Return Values:

ReturnBehavior
NextResponseShort-circuits (e.g., redirect to login)
void / undefinedUses i18n response (headers preserved)

Chains multiple middleware functions. May lose headers between middlewares.

TypeScript
// ❌ Deprecated - headers can be lost
import { createBetterI18nMiddleware, composeMiddleware } from "@better-i18n/next";

const i18n = createBetterI18nMiddleware(config);
const auth = authMiddleware;

export default composeMiddleware(i18n, auth);

Migration:

TypeScript
// ✅ Recommended - use callback pattern instead
export default createBetterI18nMiddleware(config, async (req, { locale }) => {
  // Auth logic here
});

createI18n #

The main entry point for the SDK. Returns an object with everything needed for Next.js i18n integration.

TypeScript
import { createI18n } from "@better-i18n/next";

const i18n = createI18n({
  projectId: "org/project",
  defaultLocale: "en",
  // Optional
  localePrefix: "as-needed", // "as-needed" | "always" | "never"
  manifestRevalidateSeconds: 3600, // ISR for manifest
  messagesRevalidateSeconds: 30, // ISR for messages
  debug: false,
});

Return Properties #

Configuration object for next-intl request handler.

TypeScript
// src/i18n/request.ts
export default i18n.requestConfig;

Create middleware with optional Clerk-style callback for auth integration. Recommended over middleware.

TypeScript
// middleware.ts - Simple usage
export default i18n.betterMiddleware();

// middleware.ts - With auth callback
export default i18n.betterMiddleware(async (request, { locale, response }) => {
  if (needsLogin) {
    return NextResponse.redirect(new URL(`/${locale}/login`, request.url));
  }
  // Return nothing = i18n response is used (headers preserved!)
});
ParameterTypeDescription
callbackMiddlewareCallbackOptional callback for auth/custom logic

Legacy middleware function. Use betterMiddleware() instead for Clerk-style callback support.

TypeScript
// middleware.ts
export default i18n.middleware;

Fetches the project manifest from CDN. Cached with ISR.

TypeScript
const manifest = await i18n.getManifest();

// Force refresh (bypass cache)
const manifest = await i18n.getManifest({ forceRefresh: true });

Fetches translation messages for a locale. Cached with 30s ISR.

TypeScript
// Full fetch (all namespaces)
const messages = await i18n.getMessages("tr");
// { common: { welcome: "Hoş geldiniz" }, ... }

// Selective — only fetch these namespaces (namespaced_folders projects)
const subset = await i18n.getMessages("tr", {
  namespaces: ["common", "hero"],
});
ParameterTypeDescription
localestringLocale code
options.namespacesstring[]?Fetch only these namespaces. Silently ignored for single_file projects.

Returns available locale codes from manifest.

TypeScript
const locales = await i18n.getLocales();
// ["en", "tr", "de", "fr"]

createNextI18nCore #

Low-level factory that wraps @better-i18n/core with Next.js ISR support. Use this when you need direct access to the core instance.

TypeScript
import { createNextI18nCore } from "@better-i18n/next";

const i18nCore = createNextI18nCore({
  projectId: "org/project",
  defaultLocale: "en",
  manifestRevalidateSeconds: 3600,
  messagesRevalidateSeconds: 30,
});

// All core methods available
const manifest = await i18nCore.getManifest();
const messages = await i18nCore.getMessages("en");
const locales = await i18nCore.getLocales();
const languages = await i18nCore.getLanguages();

Server Utilities #

Import from @better-i18n/next/server:

TypeScript
import {
  createNextIntlRequestConfig,
  getManifest,
  getMessages,
  getLocales,
  getManifestLanguages,
} from "@better-i18n/next/server";

Creates a next-intl request config with Better i18n CDN.

TypeScript
// src/i18n/request.ts
import { createNextIntlRequestConfig } from "@better-i18n/next/server";

export default createNextIntlRequestConfig({
  projectId: "org/project",
  defaultLocale: "en",
});

Standalone function to fetch messages.

TypeScript
import { getMessages } from "@better-i18n/next/server";

// Full fetch
const messages = await getMessages(
  { projectId: "org/project", defaultLocale: "en" },
  "tr"
);

// Selective — only fetch these namespaces
const subset = await getMessages(
  { projectId: "org/project", defaultLocale: "en" },
  "tr",
  { namespaces: ["common", "pricing"] }
);

Standalone function to fetch available locales.

TypeScript
import { getLocales } from "@better-i18n/next/server";

const locales = await getLocales({
  projectId: "org/project",
  defaultLocale: "en",
});

Get languages with full metadata (name, native name, flag URL).

TypeScript
import { getManifestLanguages } from "@better-i18n/next/server";

const languages = await getManifestLanguages({
  projectId: "org/project",
  defaultLocale: "en",
});
// [{ code: "tr", name: "Turkish", nativeName: "Türkçe", flagUrl: "..." }]

Client Utilities #

Import from @better-i18n/next/client:

TypeScript
import {
  BetterI18nProvider,
  useSetLocale,
  useManifestLanguages,
} from "@better-i18n/next/client";

Enables instant client-side locale switching via useSetLocale — no page navigation or router refresh needed. Wrap your Root Layout to activate client-side mode.

TSX
import { BetterI18nProvider } from '@better-i18n/next/client';

// In your Root Layout:
<BetterI18nProvider config={i18n.config} locale={locale} messages={messages}>
  {children}
</BetterI18nProvider>
PropTypeDefaultDescription
configI18nConfigRequiredThe i18n config from createI18n()
localestringFrom cookieOverride active locale
messagesMessagesPre-loaded SSR messages
timeZonestringconfig.timeZoneIANA timezone
nowDatePin "now" for SSR hydration consistency

Switches the active locale. Behavior depends on whether BetterI18nProvider is in the tree.

TSX
// With BetterI18nProvider in tree → instant CDN fetch + re-render
const setLocale = useSetLocale();
setLocale('tr');

// Without provider → cookie update + router.refresh()
const setLocale = useSetLocale({ config: i18n.config });
setLocale('tr');
ModeTriggerResult
With providersetLocale('tr')Instant CDN fetch + client re-render
Without providersetLocale('tr')Cookie update + router.refresh()

React hook to fetch available languages on the client. Includes request deduplication.

TSX
"use client";

import { useManifestLanguages } from "@better-i18n/next/client";

function LanguageSwitcher() {
  const { languages, isLoading, error } = useManifestLanguages({
    projectId: "org/project",
    defaultLocale: "en",
  });

  if (isLoading) return <span>Loading...</span>;
  if (error) return <span>Error: {error.message}</span>;

  return (
    <select>
      {languages.map((lang) => (
        <option key={lang.code} value={lang.code}>
          {lang.nativeName || lang.name || lang.code}
        </option>
      ))}
    </select>
  );
}
ReturnTypeDescription
languagesLanguageOption[]Available languages
isLoadingbooleanLoading state
errorError | nullError if fetch failed

Configuration #

I18nConfig #

Main configuration type for createI18n and server utilities.

TypeScript
interface I18nConfig {
  // Required
  projectId: string; // "org/project" slug or canonical UUID
  defaultLocale: string; // Fallback locale

  // Optional - CDN
  cdnBaseUrl?: string; // Default: "https://cdn.better-i18n.com"

  // Optional - Next.js ISR
  manifestRevalidateSeconds?: number; // Default: 3600 (1 hour)
  messagesRevalidateSeconds?: number; // Default: 30

  // Optional - Routing
  localePrefix?: "as-needed" | "always" | "never"; // Default: "as-needed"

  // Optional - Locale persistence
  cookieName?: string; // Default: "locale"
  explicitCookieName?: string; // Default: `${cookieName}_explicit` — marks a deliberate user choice

  // Optional - Timezone
  timeZone?: string; // IANA timezone (set explicitly to avoid ENVIRONMENT_FALLBACK warnings)

  // Optional - Offline support
  storage?: TranslationStorage; // Persistent storage adapter
  staticData?: Record<string, Messages> | (() => Promise<Record<string, Messages>>); // Last-resort bundled translations

  // Optional - Resilience
  fetchTimeout?: number; // Default: 10000 (ms)
  retryCount?: number; // Default: 1

  // Optional - Debugging
  debug?: boolean; // Enable debug logging
  logLevel?: "debug" | "info" | "warn" | "error" | "silent";
}

I18nMiddlewareConfig #

Configuration for createBetterI18nMiddleware.

TypeScript
interface I18nMiddlewareConfig {
  projectId: string;
  defaultLocale: string;
  /** URL locale prefix behavior (passed to next-intl) */
  localePrefix?: "as-needed" | "always" | "never"; // Default: "as-needed"
  detection?: {
    cookie?: boolean;        // Default: true
    browserLanguage?: boolean; // Default: true
    cookieName?: string;     // Default: "locale"
    cookieMaxAge?: number;   // Default: 31536000 (1 year)
    explicitCookieName?: string; // Default: `${cookieName}_explicit`
  };
}

Types #

Commonly used types for manual integrations and custom logic.

TypeScript
import type {
  // Main configuration
  I18nConfig,
  I18nMiddlewareConfig,

  // Client-side provider
  BetterI18nProviderProps,

  // Middleware callback types (Clerk-style pattern)
  MiddlewareContext,
  MiddlewareCallback,

  // Language metadata from manifest
  LanguageOption,
  ManifestLanguage,
  ManifestResponse,

  // Locale types
  Locale,
  LocalePrefix,
  LogLevel,

  // Translation messages
  Messages,
} from "@better-i18n/next";

Context passed to the middleware callback.

TypeScript
interface MiddlewareContext {
  /** Detected locale from the request */
  locale: string;
  /** Where the locale was resolved from: path > cookie > geo > header > default */
  detectedFrom: "path" | "cookie" | "geo" | "header" | "default";
  /** true when the explicit-marker cookie is present (deliberate user choice) */
  isExplicit: boolean;
  /** The i18n response with headers already set - can be modified */
  response: NextResponse;
}

Callback function signature for Clerk-style middleware composition.

TypeScript
type MiddlewareCallback = (
  request: NextRequest,
  context: MiddlewareContext
) => Promise<NextResponse | void> | NextResponse | void;

Usage:

TypeScript
const myCallback: MiddlewareCallback = async (request, { locale, response }) => {
  // Return NextResponse to short-circuit (e.g., redirect)
  // Return void to continue with i18n response
};

Language metadata from the CDN manifest.

TypeScript
interface LanguageOption {
  code: string; // "en", "tr", etc.
  name?: string; // "English", "Turkish"
  nativeName?: string; // "English", "Türkçe"
  flagUrl?: string | null; // URL to flag image
}

Translation messages dictionary.

TypeScript
type Messages = Record<string, unknown>;

// Example
const messages: Messages = {
  common: {
    welcome: "Welcome",
    goodbye: "Goodbye",
  },
  errors: {
    notFound: "Page not found",
  },
};