Skip to content

ICU Interpolation does not work with React Native production bundle #66

@taenri

Description

@taenri

🐛 Bug Report

We are running a React Native app for Windows using Hermes bytecode optimization to generate our JavaScript bundle. When trying to use the ICU format for i18next, everything seems to work fine when loading our JavaScript bundle ad-hoc via metro in our usual development flow. However, when we use a production bundle interpolation ceases to work.

To Reproduce

We do have a Suspense element wrapped around the component we are trying to render with the useTranslation hook. Everything works fine if we stop using the ICU format in the i18n object and change the interpolation prefix/suffix to single curly-braces.

import { initReactI18next } from 'react-i18next';
import Backend, { HttpBackendOptions, RequestCallback } from 'i18next-http-backend';
import { IErrorResponse, NativeDataHandler } from './common/dataHandler/NativeDataHandler';
import i18n, { InitOptions, ResourceKey } from 'i18next';

// Create wrapper for React Native Module APIs
const dataHandler = new NativeDataHandler();

i18n
  .use(ICU)
  .use(Backend)
  .use(initReactI18next)
  .init({
    // There is no need to define the language or fallback language here, as we use native code in a Route Handler
    // to read the system language and return the correct translation file.
    interpolation: {
      escapeValue: false // React is already safe from XSS attacks, so no need to escape.
    },
    react: {
      useSuspense: true
    },
    debug: false,
    initImmediate: false, // Avoid initializing immediately, since we defer to the native backend request to load translations.
    backend: {
      request: async (
        backendOptions: HttpBackendOptions,
        loadPath: string,
        payload: {} | string,
        callback: RequestCallback
      ): Promise<void> => {
        // Make a call through the NativeDataHandler to retrieve the correct translations file based on device locale.
        // Note that although we do not call an actual HTTP endpoint, the translations are retrieved in a similar async fashion.
        const response: ResourceKey = await dataHandler.getTranslations();
        if ((response as IErrorResponse).error) {
          callback(null, {
            status: 500,
            data: response
          });
        } else {
          callback(null, {
            status: 200,
            data: response
          });
        }
      }
    }
  } as InitOptions);

export default i18n;

Expected behavior

If we use a translation like this:
helloUser: "Hello {1}"

The following should work with production bundles as well as developer/debug mode via metro:

const t = useTranslation();
t('helloUser', {1: 'Frank'}); // Should display "Hello Frank", instead displays "Hello {1}"

Your Environment

  • runtime version: node v18, react-native-windows, hermes
  • i18next version: 23.4.4
  • os: Windows
  • Hermes bytecode compiled bundle

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions