API Reference

useModernI18n Hook

useModernI18n is a React Hook provided by the plugin for accessing internationalization functionality in components.

Return Value

interface UseModernI18nReturn {
  /** Current language code */
  language: string;

  /** Function to change language */
  changeLanguage: (newLang: string) => Promise<void>;

  /** i18next instance (for advanced usage) */
  i18nInstance: I18nInstance;

  /** Supported language list */
  supportedLanguages: string[];

  /** Check if language is supported */
  isLanguageSupported: (lang: string) => boolean;

  /** Indicates if translation resources for current language are ready to use */
  isResourcesReady: boolean;
}

Usage Example

import { useModernI18n } from '@modern-js/plugin-i18n/runtime';

function LanguageSwitcher() {
  const { language, changeLanguage, supportedLanguages, isLanguageSupported } =
    useModernI18n();

  return (
    <div>
      <p>Current language: {language}</p>
      <div>
        {supportedLanguages.map(lang => (
          <button
            key={lang}
            onClick={() => changeLanguage(lang)}
            disabled={lang === language}
          >
            {lang}
          </button>
        ))}
      </div>
      <button
        onClick={() => {
          if (isLanguageSupported('ja')) {
            changeLanguage('ja');
          }
        }}
      >
        Switch to Japanese
      </button>
    </div>
  );
}

changeLanguage Method

The changeLanguage method is used to switch languages. It will:

  1. Update the language of the i18next instance
  2. Update browser cache (Cookie/LocalStorage)
  3. Update URL path (if localePathRedirect is enabled)
const { changeLanguage } = useModernI18n();

// Switch language
await changeLanguage('zh');
Info

changeLanguage is an async function that returns a Promise.

Language Support Check

isLanguageSupported is used to check if a language is in the supported language list:

const { isLanguageSupported, changeLanguage } = useModernI18n();

function handleLanguageChange(lang: string) {
  if (isLanguageSupported(lang)) {
    changeLanguage(lang);
  } else {
    console.warn(`Language ${lang} is not supported`);
  }
}

Resource Loading State

isResourcesReady indicates whether the translation resources for the current language are loaded and ready to use. This is particularly useful when using SDK backend to load resources dynamically.

import { useModernI18n } from '@modern-js/plugin-i18n/runtime';

function MyComponent() {
  const { isResourcesReady } = useModernI18n();

  if (!isResourcesReady) {
    return <div>Loading translation resources...</div>;
  }

  return <div>Translation resources are ready</div>;
}

When to use:

  • Display a loading state while resources are being loaded
  • Prevent rendering content that depends on translations before resources are ready
  • Handle resource loading errors gracefully
Info

isResourcesReady automatically checks:

  • If i18n instance is initialized
  • If any resources for the current language are currently loading (SDK backend only)
  • If all required namespaces for the current language are loaded in the store

The I18nLink component is used to create links with language prefixes.

Props

interface I18nLinkProps {
  /** Target path (no need to include language prefix) */
  to: string;
  /** Child elements */
  children: React.ReactNode;
  /** Other Link component props (such as replace, state, etc.) */
  [key: string]: any;
}

Usage Example

import { I18nLink } from '@modern-js/plugin-i18n/runtime';

function Navigation() {
  return (
    <nav>
      <I18nLink to="/">Home</I18nLink>
      <I18nLink to="/about">About</I18nLink>
      <I18nLink to="/contact" replace>
        Contact
      </I18nLink>
    </nav>
  );
}

Runtime Plugin API

In the onBeforeRender hook of Runtime plugins, you can modify the language using the context.changeLanguage method. This is useful for scenarios where you need to dynamically set the language based on request information (such as user preferences, geographic location, etc.).

context.changeLanguage

In the onBeforeRender hook, the i18n plugin adds a changeLanguage method to the context for use by other Runtime plugins.

Type Definition:

interface TInternalRuntimeContext {
  i18nInstance?: I18nInstance;
  changeLanguage?: (lang: string) => Promise<void>;
}

Usage Example

import type { RuntimePlugin } from '@modern-js/runtime';

const myRuntimePlugin = (): RuntimePlugin => ({
  name: 'my-runtime-plugin',
  setup: api => {
    api.onBeforeRender(async context => {
      // Check if changeLanguage method exists (ensure i18n plugin is loaded)
      if (context.changeLanguage) {
        // Determine language based on some condition
        const userLang = getUserLanguageFromRequest(context);

        // Change language
        await context.changeLanguage(userLang);
      }
    });
  },
});

function getUserLanguageFromRequest(context: any): string {
  // Get user language from request headers, cookies, or other sources
  const acceptLanguage = context.ssrContext?.req?.headers['accept-language'];
  // Parse and return appropriate language code
  return parseAcceptLanguage(acceptLanguage) || 'zh';
}

export default myRuntimePlugin;

Notes

  1. Execution Order: Ensure the i18n plugin is registered before other plugins that use changeLanguage, so that context.changeLanguage is available.

  2. Async Operation: changeLanguage is an async method and requires using await to wait for completion.

  3. Error Handling: If an invalid language code is passed, an error will be thrown. It's recommended to add error handling:

api.onBeforeRender(async context => {
  if (context.changeLanguage) {
    try {
      await context.changeLanguage('zh');
    } catch (error) {
      console.error('Failed to change language:', error);
    }
  }
});
  1. Language Validation: It's recommended to verify that the language is in the supported language list before calling changeLanguage:
api.onBeforeRender(async context => {
  if (context.changeLanguage && context.i18nInstance) {
    const supportedLngs = context.i18nInstance.options?.supportedLngs || [];
    const targetLang = 'zh';

    if (supportedLngs.includes(targetLang)) {
      await context.changeLanguage(targetLang);
    }
  }
});
Info

The changeLanguage method will:

  • Update the language of the i18n instance
  • Cache the language selection in the browser environment (Cookie/LocalStorage)
  • Trigger callbacks related to language switching

However, it will not automatically update the URL path. If you need to update the URL, you need to coordinate with the routing plugin or handle it manually.

Integration with react-i18next

The plugin is fully compatible with react-i18next and can use the useTranslation Hook and other react-i18next features.

useTranslation Hook

import { useTranslation } from 'react-i18next';

function MyComponent() {
  const { t, i18n } = useTranslation();

  return (
    <div>
      <h1>{t('welcome')}</h1>
      <p>{t('description', { name: 'Modern.js' })}</p>
    </div>
  );
}

Accessing i18next Instance

You can get the i18next instance through useModernI18n:

import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
import { useTranslation } from 'react-i18next';

function MyComponent() {
  const { i18nInstance } = useModernI18n();
  const { t } = useTranslation();

  // Directly access i18next instance
  console.log(i18nInstance.language);
  console.log(i18nInstance.options);

  return <div>{t('hello')}</div>;
}

Type Definitions

I18nInstance Interface

interface I18nInstance {
  language: string;
  isInitialized: boolean;
  init: (options?: I18nInitOptions) => void | Promise<void>;
  changeLanguage: (lang: string) => void | Promise<void>;
  use: (plugin: any) => void;
  createInstance: (options?: I18nInitOptions) => I18nInstance;
  cloneInstance?: () => I18nInstance;
  services?: {
    languageDetector?: {
      detect: (request?: any, options?: any) => string | string[] | undefined;
      [key: string]: any;
    };
    [key: string]: any;
  };
  options?: {
    backend?: BackendOptions;
    [key: string]: any;
  };
}

I18nInitOptions Interface

interface I18nInitOptions {
  lng?: string;
  fallbackLng?: string;
  supportedLngs?: string[];
  initImmediate?: boolean;
  detection?: LanguageDetectorOptions;
  backend?: BackendOptions;
  resources?: Resources;
  ns?: string | string[];
  defaultNS?: string | string[];
  react?: {
    useSuspense?: boolean;
    [key: string]: any;
  };
  [key: string]: any;
}

LanguageDetectorOptions Interface

interface LanguageDetectorOptions {
  /** Detection order */
  order?: string[];
  /** Query parameter key name, default 'lng' */
  lookupQuerystring?: string;
  /** Cookie key name, default 'i18next' */
  lookupCookie?: string;
  /** LocalStorage key name, default 'i18nextLng' (browser only) */
  lookupLocalStorage?: string;
  /** SessionStorage key name (browser only) */
  lookupSession?: string;
  /** Starting index in path for language detection, default 0 */
  lookupFromPathIndex?: number;
  /** Cache method, can be false or string array (e.g., ['cookie', 'localStorage']) */
  caches?: boolean | string[];
  /** Cookie expiration time (minutes) */
  cookieMinutes?: number;
  /** Cookie expiration date (Date object, takes precedence over cookieMinutes) */
  cookieExpirationDate?: Date;
  /** Cookie domain */
  cookieDomain?: string;
  /** Request header key name, default 'accept-language' */
  lookupHeader?: string;
}

Resources Type

type Resources = {
  [lng: string]: {
    [ns: string]: string | Record<string, string>;
  };
};
Info

The namespace value can be a string (for simple key-value pairs) or an object (for nested translation structures).