logo
  • Guide
  • Config
  • Plugin
  • API
  • Examples
  • Community
  • Modern.js 2.x Docs
  • English
    • 简体中文
    • English
    • Start
      Introduction
      Quick Start
      Upgrading
      Glossary
      Tech Stack
      Core Concept
      Page Entry
      Build Engine
      Web Server
      Basic Features
      Routes
      Routing
      Config Routes
      Data Solution
      Data Fetching
      Data Writing
      Data Caching
      Rendering
      Server-Side Rendering
      Streaming SSR
      Rendering Cache
      Static Site Generation
      Render Preprocessing
      Styling
      Styling
      Use CSS Modules
      Using CSS-in-JS
      Using Tailwind CSS
      HTML Template
      Import Static Assets
      Import JSON Files
      Import SVG Assets
      Import Wasm Assets
      Debug
      Data Mocking
      Network Proxy
      Using Rsdoctor
      Using Storybook
      Testing
      Playwright
      Vitest
      Jest
      Cypress
      Path Alias
      Environment Variables
      Output Files
      Deploy Application
      Advanced Features
      Using Rspack
      Using BFF
      Basic Usage
      Runtime Framework
      Extend BFF Server
      Extend Request SDK
      File Upload
      Cross-Project Invocation
      Optimize Page Performance
      Code Splitting
      Inline Static Assets
      Bundle Size Optimization
      React Compiler
      Improve Build Performance
      Browser Compatibility
      Low-Level Tools
      Source Code Build Mode
      Server Monitor
      Monitors
      Logs Events
      Metrics Events
      Internationalization
      Basic Concepts
      Quick Start
      Configuration
      Locale Detection
      Resource Loading
      Routing Integration
      API Reference
      Advanced Usage
      Best Practices
      Custom Web Server
      Topic Detail
      Module Federation
      Introduction
      Getting Started
      Application-Level Modules
      Server-Side Rendering
      Deployment
      Integrating Internationalization
      FAQ
      Dependencies FAQ
      CLI FAQ
      Build FAQ
      HMR FAQ
      Deprecated
      📝 Edit this page
      Previous pageLocale DetectionNext pageRouting Integration

      #Resource Loading

      The plugin supports three resource loading methods: HTTP backend, File System backend (FS Backend), and SDK backend. Additionally, the plugin supports chained backend, which allows combining multiple backends.

      #HTTP Backend

      HTTP backend loads resource files through HTTP requests, suitable for client-side rendering (CSR) scenarios.

      #Configuration

      i18nPlugin({
        backend: {
          enabled: true,
          loadPath: '/locales/{{lng}}/{{ns}}.json',
        },
      });

      #Resource File Structure

      Resource files need to be placed in the config/public directory or the directory configured through server.publicDir:

      config/public/
      └── locales/
          ├── en/
          │   └── translation.json
          └── zh/
              └── translation.json

      Or configure a custom directory through server.publicDir:

      export default defineConfig({
        server: {
          publicDir: './locales', // Specify resource file directory
        },
        plugins: [
          i18nPlugin({
            backend: {
              enabled: true,
              loadPath: '/locales/{{lng}}/{{ns}}.json',
            },
          }),
        ],
      });

      Resource File Format:

      {
        "key1": "value1",
        "key2": "value2",
        "nested": {
          "key": "value"
        }
      }

      #Path Variables

      loadPath supports the following variables:

      • {{lng}}: Language code (e.g., en, zh)
      • {{ns}}: Namespace (e.g., translation, common)

      Examples:

      // Default path format
      loadPath: '/locales/{{lng}}/{{ns}}.json';
      // Actual loading paths:
      // /locales/en/translation.json
      // /locales/zh/translation.json
      
      // Custom path format
      loadPath: '/i18n/{{lng}}/{{ns}}.json';
      // Actual loading paths:
      // /i18n/en/translation.json
      // /i18n/zh/translation.json

      #File System Backend (FS Backend)

      File System backend reads resource files directly from the file system, suitable for server-side rendering (SSR) scenarios.

      #Configuration

      In SSR scenarios, the plugin will automatically use the file system backend. Resource files need to be placed in the project directory:

      Project Root/
      └── locales/
          ├── en/
          │   └── translation.json
          └── zh/
              └── translation.json

      #Resource File Path

      The default path format for file system backend is a relative path:

      ./locales/{{lng}}/{{ns}}.json

      You can customize the path through loadPath:

      i18nPlugin({
        backend: {
          enabled: true,
          loadPath: '/locales/{{lng}}/{{ns}}.json', // Use absolute path (recommended)
        },
      });
      Warning

      The loadPath configuration is used for both HTTP backend (frontend) and file system backend (server-side). If configured as an absolute path starting with / (e.g., /locales/{{lng}}/{{ns}}.json), the file system backend will automatically convert it to a relative path (./locales/{{lng}}/{{ns}}.json). Therefore, it's recommended to use absolute paths in the configuration, which can meet both frontend and backend requirements.

      #SDK Backend

      SDK backend allows loading resources through custom functions, suitable for scenarios where translation resources need to be loaded from external services, databases, or other custom sources.

      #Enable SDK Mode

      Step 1: Enable SDK mode in modern.config.ts

      i18nPlugin({
        backend: {
          enabled: true,
          sdk: true, // Enable SDK mode
        },
      });

      Step 2: Implement SDK function in modern.runtime.ts

      import { defineRuntimeConfig } from '@modern-js/runtime';
      import type { I18nSdkLoader, Resources } from '@modern-js/plugin-i18n/runtime';
      
      const mySdkLoader: I18nSdkLoader = async options => {
        // Implement resource loading logic
        if (options.all) {
          // Load all resources
          return await loadAllResources();
        }
      
        if (options.lng && options.ns) {
          // Load single resource
          return await loadSingleResource(options.lng, options.ns);
        }
      
        return {};
      };
      
      export default defineRuntimeConfig({
        i18n: {
          initOptions: {
            backend: {
              sdk: mySdkLoader,
            },
          },
        },
      });

      #Implement SDK Loader Function

      The SDK function receives an I18nSdkLoadOptions parameter and needs to return data in Resources format:

      interface I18nSdkLoadOptions {
        /** Single language code */
        lng?: string;
        /** Single namespace */
        ns?: string;
        /** Multiple language codes */
        lngs?: string[];
        /** Multiple namespaces */
        nss?: string[];
        /** Load all resources */
        all?: boolean;
      }
      
      type Resources = {
        [lng: string]: {
          [ns: string]: Record<string, string>;
        };
      };

      #Batch Loading Examples

      SDK backend supports multiple loading modes:

      1. Load single resource:

      const sdkLoader: I18nSdkLoader = async options => {
        if (options.lng && options.ns) {
          const response = await fetch(`/api/i18n/${options.lng}/${options.ns}`);
          const data = await response.json();
          return {
            [options.lng]: {
              [options.ns]: data,
            },
          };
        }
        return {};
      };

      2. Batch load multiple languages:

      const sdkLoader: I18nSdkLoader = async options => {
        if (options.lngs && options.ns) {
          const resources: Resources = {};
          for (const lng of options.lngs) {
            const response = await fetch(`/api/i18n/${lng}/${options.ns}`);
            resources[lng] = {
              [options.ns]: await response.json(),
            };
          }
          return resources;
        }
        return {};
      };

      3. Batch load multiple namespaces:

      const sdkLoader: I18nSdkLoader = async options => {
        if (options.lng && options.nss) {
          const resources: Resources = {
            [options.lng]: {},
          };
          for (const ns of options.nss) {
            const response = await fetch(`/api/i18n/${options.lng}/${ns}`);
            resources[options.lng][ns] = await response.json();
          }
          return resources;
        }
        return {};
      };

      4. Load all resources:

      const sdkLoader: I18nSdkLoader = async options => {
        if (options.all) {
          const response = await fetch('/api/i18n/all');
          return await response.json();
        }
        return {};
      };

      #Check Resource Loading State

      When using SDK backend, you can check if resources are loaded using isResourcesReady:

      import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
      
      function MyComponent() {
        const { isResourcesReady } = useModernI18n();
      
        if (!isResourcesReady) {
          return <div>Loading translation resources...</div>;
        }
      
        return <div>Resources are ready!</div>;
      }

      This is particularly useful when resources are loaded asynchronously, as it ensures all required namespaces for the current language are loaded before rendering content that depends on translations.

      #Chained Backend

      Chained backend allows using multiple backends simultaneously, enabling progressive resource loading and updates. When both loadPath (or FS backend) and sdk are configured, the plugin automatically uses i18next-chained-backend to chain resource loading.

      #How It Works

      The chained backend workflow:

      1. Initial Load: First load resources from HTTP/FS backend and display immediately (quick display of basic translations)
      2. Async Update: Then asynchronously load resources from SDK backend and update the i18next store (update/supplement translations)

      This ensures users see page content quickly while the latest translation resources are loaded in the background.

      #Configuration

      Step 1: Configure chained backend in modern.config.ts

      i18nPlugin({
        backend: {
          enabled: true,
          loadPath: '/locales/{{lng}}/{{ns}}.json', // HTTP/FS backend
          sdk: true, // SDK backend
          // cacheHitMode: 'refreshAndUpdateStore', // Default value, can be omitted
        },
      });

      Step 2: Implement SDK function in modern.runtime.ts

      import { defineRuntimeConfig } from '@modern-js/runtime';
      
      export default defineRuntimeConfig({
        i18n: {
          initOptions: {
            backend: {
              sdk: async options => {
                // SDK implementation
                if (options.lng && options.ns) {
                  return await mySdk.getResource(options.lng, options.ns);
                }
              },
            },
          },
        },
      });

      #Cache Hit Mode (cacheHitMode)

      The cacheHitMode option controls the behavior of chained backend:

      • '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.

      Configuration example:

      i18nPlugin({
        backend: {
          enabled: true,
          loadPath: '/locales/{{lng}}/{{ns}}.json',
          sdk: true,
          cacheHitMode: 'refreshAndUpdateStore', // Explicitly specify (default value)
        },
      });

      #Use Cases

      Chained backend is particularly suitable for the following scenarios:

      1. Progressive Loading: Display local/static resources first, then load latest translations from remote services
      2. Offline Support: Local resources as offline fallback, SDK resources provide online updates
      3. Performance Optimization: Quickly display basic translations, load complete translation content in the background
      4. A/B Testing: Local resources as default values, SDK provides dynamic translation variants

      #Complete Example

      // modern.config.ts
      i18nPlugin({
        backend: {
          enabled: true,
          loadPath: '/locales/{{lng}}/{{ns}}.json', // Local resources
          sdk: true, // Remote SDK resources
          cacheHitMode: 'refreshAndUpdateStore',
        },
      });
      
      // modern.runtime.ts
      import { defineRuntimeConfig } from '@modern-js/runtime';
      
      export default defineRuntimeConfig({
        i18n: {
          initOptions: {
            backend: {
              sdk: async options => {
                if (options.lng && options.ns) {
                  // Load latest translations from remote service
                  const response = await fetch(
                    `https://api.example.com/i18n/${options.lng}/${options.ns}`,
                  );
                  return {
                    [options.lng]: {
                      [options.ns]: await response.json(),
                    },
                  };
                }
                return {};
              },
            },
          },
        },
      });

      In this example:

      • When the page loads, resources are first loaded from /locales/{{lng}}/{{ns}}.json and displayed immediately
      • Latest translations are loaded asynchronously from https://api.example.com/i18n/... in the background
      • After SDK resources are loaded, the i18next store is automatically updated, and the UI text is automatically updated