# React Native & Expo localization with TransLocale

Deliver approved wording to your React Native app, with bundled translations and a saved release for offline use.

Canonical page: https://translocale.io/integrations/react-native

## Install

@translocale/react-native 0.1.1. Expo SDK 57 · React Native 0.86 · Hermes. Message format: Native i18next JSON v4.

```bash
npm install @translocale/react-native @translocale/i18next i18next react-i18next
```

## Expo setup

```bash
npx expo install expo-crypto expo-localization expo-sqlite
npm install @formatjs/intl-locale @formatjs/intl-pluralrules
```

Load Intl.Locale and Intl.PluralRules polyfills, plus data for every bundled language, before initializing i18next. The tested Hermes runtime needed both APIs.

```text
import "@formatjs/intl-locale/polyfill.js";
import "@formatjs/intl-pluralrules/polyfill.js";
import "@formatjs/intl-pluralrules/locale-data/en.js";
import "@formatjs/intl-pluralrules/locale-data/fr.js";
```

Create a project with English as its source and French as its target. Import common.json with the i18next JSON v4 format, approve the French wording, and publish a Preview release. In Releases → App delivery, select its schema and create a Preview credential. Choose Download configuration while the token is shown. Save the file at src/translocale-delivery.json and keep it out of source control. The download supplies apiBaseUrl, projectId, channel, schemaHash, and deliveryToken.

- [Project, keys, translations, and credentials with screenshots](https://translocale.io/docs/getting-started)

locales/en/common.json:

```json
{ "welcome": "Hello {{name}}" }
```

locales/fr/common.json:

```json
{ "welcome": "Bonjour {{name}}" }
```

Save these same catalogs in your app. For a new setup, replace PROJECT_UUID with the ID under Project settings → Credentials and run the following command in the app directory. It generates translocale.config.json and src/translocale-resources.ts without cloud requests or a token. Keep an existing config rather than rerunning init.

```bash
npx translocale-i18next init --api https://translocale.io --project PROJECT_UUID --source en --locales en,fr --namespaces common
```

Save the bootstrap below as src/i18n.ts. It maps the common.json dashboard catalog to the common namespace. After checking Preview in your app, promote the release to Production and create a separate Production credential for that build. Changing the channel text alone does not change token permissions.

```text
import { AppState } from "react-native";
import { getLocales } from "expo-localization";
import Storage from "expo-sqlite/kv-store";
import { createInstance } from "i18next";
import { initReactI18next } from "react-i18next";
import { createI18nextOptions } from "@translocale/i18next/runtime";
import {
  assertNativeIntl,
  createExpoDelivery,
  resolveNativeLocale,
} from "@translocale/react-native/expo";
import bundle from "./translocale-resources";
import descriptor from "./translocale-delivery.json";

if (descriptor.channel !== "preview" && descriptor.channel !== "production") {
  throw new Error("Download a valid delivery configuration from App delivery.");
}

assertNativeIntl(bundle.locales);
const i18n = createInstance(); // Reuse your existing instance in an existing app.
const language = resolveNativeLocale({
  supportedLocales: bundle.locales,
  sourceLocale: bundle.sourceLocale,
  preferredLocales: getLocales().map((locale) => locale.languageTag),
});
await i18n.use(initReactI18next).init({
  ...createI18nextOptions(bundle, { language, escapeInterpolation: false }),
  react: { bindI18n: "languageChanged loaded" },
});

const delivery = createExpoDelivery({
  i18n,
  bundle,
  catalogs: [{ file: "common.json", namespace: "common" }],
  delivery: {
    projectId: descriptor.projectId,
    channel: descriptor.channel,
    apiBaseUrl: descriptor.apiBaseUrl,
    schemaHash: descriptor.schemaHash,
    appVersion: "1.0.0",
    token: descriptor.deliveryToken,
    storage: Storage,
  },
});
void delivery.start();

// If the app follows system language, re-read it on foreground.
// If the user chose an explicit app language, retain that preference instead.
const localeSubscription = AppState.addEventListener("change", (state) => {
  if (state !== "active") return;
  const language = resolveNativeLocale({
    supportedLocales: bundle.locales,
    sourceLocale: bundle.sourceLocale,
    preferredLocales: getLocales().map((locale) => locale.languageTag),
  });
  void i18n.changeLanguage(language);
});

// On final owner cleanup, not ordinary backgrounding:
// localeSubscription.remove();
// delivery.dispose();
```

Foreground checks pause in the background. Invalid responses retain the previous valid wording. Saved releases support offline launches. Keep authoring credentials and CLI imports outside the app. Refreshing wording never starts a translation job.

- [Recorded local delivery demo](https://translocale.io/docs/media/react-native/delivery-demo.mp4)
- [Prepare and publish reviewed wording](https://translocale.io/docs#deliver-updates-to-your-app)

## Project setup and credentials

The screenshot walkthrough covers project creation, catalog import, keys, review, releases, and credentials. Use this framework’s message format and catalog identities. Authoring tokens belong in developer tools and CI; app delivery credentials only read published wording. Flutter’s flutter-connect writes DELIVERY_* build settings, while the dashboard download uses projectId, channel, schemaHash, deliveryToken, and apiBaseUrl.

- [Follow the illustrated first-project guide](https://translocale.io/docs/getting-started)

## Compatibility and further setup

Physical devices, installed hosted-service connections, and apps without Expo modules remain unverified.

New languages, keys, native system-dialog translations, and layout changes may require another app build.

- [Complete framework guide, including runtime examples](https://translocale.io/integrations/react-native)
- [MCP guide](https://translocale.io/docs/mcp)
- [Free source file checker](https://translocale.io/app)
