useModernI18n is a React Hook provided by the plugin for accessing internationalization functionality in components.
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;
}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>
);
}The changeLanguage method is used to switch languages. It will:
localePathRedirect is enabled)const { changeLanguage } = useModernI18n();
// Switch language
await changeLanguage('zh');
changeLanguage is an async function that returns a Promise.
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`);
}
}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:
isResourcesReady automatically checks:
The I18nLink component is used to create links with language prefixes.
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;
}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>
);
}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.).
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>;
}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;Execution Order: Ensure the i18n plugin is registered before other plugins that use changeLanguage, so that context.changeLanguage is available.
Async Operation: changeLanguage is an async method and requires using await to wait for completion.
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);
}
}
});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);
}
}
});
The changeLanguage method will:
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.
The plugin is fully compatible with react-i18next and can use the useTranslation Hook and other react-i18next features.
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>
);
}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>;
}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;
};
}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;
}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;
}type Resources = {
[lng: string]: {
[ns: string]: string | Record<string, string>;
};
};The namespace value can be a string (for simple key-value pairs) or an object (for nested translation structures).