Plugin configuration is divided into two parts: CLI configuration (modern.config.ts) and runtime configuration (modern.runtime.ts). Both need to be used together - CLI configuration is for basic plugin settings, while runtime configuration is for i18next initialization options.
Function-type configurations (such as SDK loader functions) can only be set in runtime configuration (modern.runtime.ts), not in CLI configuration. This is because CLI configuration is executed at build time and cannot serialize functions.
Configure plugin options in modern.config.ts:
import { i18nPlugin } from '@modern-js/plugin-i18n';
export default defineConfig({
plugins: [
i18nPlugin({
localeDetection: {
// Language detection configuration
},
backend: {
// Backend resource loading configuration
},
}),
],
});localeDetection is used to configure language detection related options:
If localePathRedirect is enabled, the detection configuration must be placed in CLI configuration (modern.config.ts), because the server-side plugin needs to read this configuration to get language information and perform path redirection.
interface BaseLocaleDetectionOptions {
/** Whether to enable path redirection, adds language prefix to URL when enabled */
localePathRedirect?: boolean;
/** Whether to enable i18next language detector */
i18nextDetector?: boolean;
/** Supported language list */
languages?: string[];
/** Default fallback language */
fallbackLanguage?: string;
/** Custom detection configuration */
detection?: LanguageDetectorOptions;
/** Routes to ignore automatic redirection (array of path patterns or function)
*
* Can be a string array (path patterns) or a function to determine if redirection should be ignored.
* Supports exact match and prefix match (e.g., '/api' will match '/api' and '/api/users').
*
* @example
* // String array
* ignoreRedirectRoutes: ['/api', '/admin']
*
* // Function
* ignoreRedirectRoutes: (pathname) => pathname.startsWith('/api')
*/
ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean);
}
interface LocaleDetectionOptions extends BaseLocaleDetectionOptions {
/** Configure language detection by entry (multi-entry scenarios) */
localeDetectionByEntry?: Record<string, BaseLocaleDetectionOptions>;
}Example:
i18nPlugin({
localeDetection: {
localePathRedirect: true,
i18nextDetector: true,
languages: ['zh', 'en', 'ja'],
fallbackLanguage: 'en',
detection: {
order: ['path', 'cookie', 'header'],
lookupCookie: 'i18next',
caches: ['cookie'],
},
},
});backend is used to configure resource loading methods:
Auto-detection: The plugin automatically detects and enables backend in the following scenarios:
If you configure loadPath or addPath: The backend will be automatically enabled (enabled: true) without checking for locales directory, since you've already specified the resource path.
If you don't configure backend: The plugin will automatically detect if a locales directory exists in:
{projectRoot}/locales{projectRoot}/config/public/localesserver.publicDir: {projectRoot}/{publicDir}/localesIf the directory exists and contains JSON files, the backend will be automatically enabled.
If you explicitly set enabled: false: No auto-detection will be performed, and the backend will remain disabled.
This automatic detection helps reduce unnecessary backend registration when locales directory doesn't exist, improving performance.
interface BaseBackendOptions {
/** Whether to enable backend resource loading */
enabled?: boolean;
/** Resource file loading path (HTTP backend) */
loadPath?: string;
/** Missing translation save path (optional) */
addPath?: string;
/** Cache hit mode for chained backend (only effective when both `loadPath` and `sdk` are provided)
*
* - `'none'` (default, only when chained backend is not configured): If the first backend returns resources, stop and don't try the next backend
* - `'refresh'`: Try to refresh the cache by loading from the next backend and update the cache
* - `'refreshAndUpdateStore'` (default for chained backend): Try to refresh the cache by loading from the next backend,
* update the cache and also update the i18next resource store. This allows FS/HTTP resources to be displayed first,
* then SDK resources will update them asynchronously.
*
* @default 'refreshAndUpdateStore' when both loadPath and sdk are provided
*/
cacheHitMode?: 'none' | 'refresh' | 'refreshAndUpdateStore';
/** SDK loader function (custom backend)
*
* Note: In CLI configuration, can only be set to `true` or a string identifier to enable SDK mode.
* The actual SDK function must be provided through `initOptions.backend.sdk` in runtime configuration (`modern.runtime.ts`).
*
* When both `loadPath` (or FS backend) and `sdk` are provided, the plugin will automatically use `i18next-chained-backend`
* to chain multiple backends. The loading order will be:
* 1. HTTP/FS backend (primary) - loads from `loadPath` or file system first for quick initial display
* 2. SDK backend (update) - loads from the SDK function to update/refresh translations
*
* With `cacheHitMode: 'refreshAndUpdateStore'` (default), FS/HTTP resources will be displayed immediately,
* then SDK resources will be loaded asynchronously to update the translations.
*/
sdk?: I18nSdkLoader | boolean | string;
}
interface BackendOptions extends BaseBackendOptions {
/** Configure backend by entry (multi-entry scenarios) */
backendOptionsByEntry?: Record<string, BaseBackendOptions>;
}Examples:
1. HTTP/FS backend only:
You can explicitly enable backend:
i18nPlugin({
backend: {
enabled: true,
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
});Or simply configure loadPath or addPath, and backend will be automatically enabled:
i18nPlugin({
backend: {
// enabled will be automatically set to true
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
});Auto-detection without configuration:
If you don't configure backend at all, the plugin will automatically detect locales directory:
i18nPlugin({
// No backend config - plugin will auto-detect locales directory
localeDetection: {
languages: ['zh', 'en'],
fallbackLanguage: 'en',
},
});If locales directory exists with JSON files, backend will be automatically enabled with default loadPath: '/locales/{{lng}}/{{ns}}.json'.
2. Chained backend (recommended): Use both HTTP/FS backend and SDK backend
When backend.enabled = true and sdk is configured, if loadPath is not explicitly configured, the default loadPath will be used automatically and chained backend will be enabled:
i18nPlugin({
backend: {
enabled: true,
// When loadPath is not configured, default '/locales/{{lng}}/{{ns}}.json' will be used
sdk: true, // SDK backend
// cacheHitMode: 'refreshAndUpdateStore', // Default value, can be omitted
},
});You can also explicitly configure loadPath:
i18nPlugin({
backend: {
enabled: true,
loadPath: '/locales/{{lng}}/{{ns}}.json', // HTTP/FS backend
sdk: true, // SDK backend
},
});Provide the SDK function in modern.runtime.ts:
export default defineRuntimeConfig({
i18n: {
initOptions: {
backend: {
sdk: async options => {
// SDK implementation
if (options.lng && options.ns) {
return await mySdk.getResource(options.lng, options.ns);
}
},
},
},
},
});When using chained backend, the system will:
/locales/{{lng}}/{{ns}}.json and display immediately (quick initial display of basic translations)This ensures users see page content quickly while the latest translation resources are loaded in the background.
3. SDK backend only:
If you need to disable HTTP/FS backend and use only SDK backend, you can explicitly set loadPath: '':
i18nPlugin({
backend: {
enabled: true,
loadPath: '', // Explicitly disable HTTP/FS backend
sdk: true, // Use SDK backend only
},
});
When using SDK backend only, you must provide the actual SDK function in modern.runtime.ts, otherwise it will fallback to HTTP/FS backend.
If the project has multiple entries, you can configure each entry separately:
i18nPlugin({
localeDetection: {
localePathRedirect: true,
languages: ['zh', 'en'],
fallbackLanguage: 'en',
// Override configuration for specific entry
localeDetectionByEntry: {
admin: {
localePathRedirect: false, // admin entry does not use path redirection
},
},
},
backend: {
enabled: true,
// Override configuration for specific entry
backendOptionsByEntry: {
admin: {
loadPath: '/admin/locales/{{lng}}/{{ns}}.json',
},
},
},
});You can configure runtime options in src/modern.runtime.ts:
import { defineRuntimeConfig } from '@modern-js/runtime';
import i18next from 'i18next';
// It's recommended to create a new i18next instance to avoid using the global default instance
const i18nInstance = i18next.createInstance();
export default defineRuntimeConfig({
i18n: {
// Use custom i18next instance (optional)
i18nInstance: i18nInstance,
// i18next initialization options
initOptions: {
fallbackLng: 'en',
supportedLngs: ['zh', 'en'],
// Other i18next configuration options
},
},
});If you need to use a custom i18next instance, you can provide it in runtime configuration:
import { defineRuntimeConfig } from '@modern-js/runtime';
import i18next from 'i18next';
// Create custom instance
const customI18n = i18next.createInstance({
// Custom configuration
});
export default defineRuntimeConfig({
i18n: {
i18nInstance: customI18n,
},
});initOptions will be passed to i18next's init method and supports all i18next configuration options:
If localePathRedirect is enabled, the detection configuration should be set in CLI configuration, not in initOptions. This is because the server-side plugin needs to read the detection option from CLI configuration to perform language detection and path redirection.
export default defineRuntimeConfig({
i18n: {
initOptions: {
// Language related
lng: 'en',
fallbackLng: 'en',
supportedLngs: ['zh', 'en'],
// Namespace related
ns: ['translation', 'common'],
defaultNS: 'translation',
// React related
react: {
useSuspense: false,
},
// Other i18next options
interpolation: {
escapeValue: false,
},
},
},
});If using SDK backend, you need to provide the actual SDK function in runtime configuration:
Function-type configurations can only be set in runtime configuration. In CLI configuration, sdk can only be set to true or a string identifier to enable SDK mode. The actual function implementation must be provided in modern.runtime.ts.
Enable SDK mode in modern.config.ts:
i18nPlugin({
backend: {
enabled: true,
sdk: true, // Enable SDK mode
},
});Provide SDK function in modern.runtime.ts:
import { defineRuntimeConfig } from '@modern-js/runtime';
import type { I18nSdkLoader } from '@modern-js/plugin-i18n/runtime';
const mySdkLoader: I18nSdkLoader = async options => {
if (options.all) {
// Load all resources
return await fetchAllResources();
}
if (options.lng && options.ns) {
// Load single resource
const response = await fetch(`/api/i18n/${options.lng}/${options.ns}`);
return response.json();
}
// Handle other cases
return {};
};
export default defineRuntimeConfig({
i18n: {
initOptions: {
backend: {
sdk: mySdkLoader,
},
},
},
});