Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
taenri opened this issue Aug 23, 2023 · 4 comments
Open

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

taenri opened this issue Aug 23, 2023 · 4 comments

Comments

@taenri
Copy link

taenri commented Aug 23, 2023

🐛 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
@adrai
Copy link
Member

adrai commented Aug 23, 2023

Make sure intl-messageformat is installed (npm install intl-messageformat) and bundled...
Beside that, try to open an issue in the react-native github repo.

@taenri
Copy link
Author

taenri commented Aug 23, 2023

Yes, we are using version 10.5.0 of intl-messageformat as a dependency (not a dev-dependency). Everything works fine when we stop using ICU format altogether, so I do not think this is an issue with react-native.

@adrai
Copy link
Member

adrai commented Aug 23, 2023

is there any warning if you set debug to true?
If it works on dev mode but not on prod I would suggest to open an issue at the react-native repo

@taenri
Copy link
Author

taenri commented Aug 23, 2023

There is no warning when debug is set to true in the console. It may be an issue with the JavaScript engine we are trying to use. I will sync with react-native folks to see if they are familiar with any similar issues.

EDIT: I should also note that the issue repros even without using Hermes Bytecode optimization. It may be reproducible with similar production-ready JavaScript bundles for the web, not just with react-native. Would be worth trying if you have the time. If not, I can give it a try tomorrow.

EDIT2: Finally got around to testing. This seems to be an issue specific to the hermes JS engine.

Repro steps:

  1. Set up basic react-native-windows project using wiki steps: Get Started with Windows · React Native for Windows + macOS (microsoft.github.io)
  2. yarn add i18next react-i18next i18next-icu intl-messageformat --save
  3. Set up basic i18next instance with ICU component and basic interpolation string translation.
    a. Getting started - react-i18next documentation
    b. Using with ICU format - react-i18next documentation
  4. Try to use the interpolated string in the rendered JSX
  5. Ensure UseHermes and UseBundle build properties are set to true in ExperimentalFeatures.props
  6. Build for Release|x64
  7. Run and observe that the string does not get interpolated properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants