React Native & Expo
Use the Babelize SDK in Expo and bare React Native apps: the @babelize/metro plugin writes the translation lockfile before Metro bundles, and the app picks the device language.
The SDK runs in React Native as it is: @babelize/sdk and @babelize/sdk/react use no DOM APIs. What's specific to a native app:
- translations are bundled:
@babelize/metrowrites the lockfile before Metro starts bundling, and the app loads it; - the device language picks the locale;
- text renders inside
<Text>, as everything does in React Native.
Don't put an API key in the app
Anything shipped in a mobile app can be extracted from it. Translate at build time with the Metro plugin (the key stays on your machine or CI) and initialize the SDK without apiKey, so it only reads the lockfile.
To have Babelize add react-i18next to an existing React Native app instead (no babelize() calls in your code), see React Native & Expo in Integrations.
Install
npx expo install @babelize/sdk
npm install --save-dev @babelize/metroIn bare React Native, use npm install @babelize/sdk for the first line.
1. Generate the lockfile with Metro
Wrap your Metro config. The lockfile is written when Metro loads its config, so expo start, expo export, react-native start and react-native bundle always bundle this run's translations.
// metro.config.js (Expo)
const { getDefaultConfig } = require('expo/metro-config');
const { withBabelize } = require('@babelize/metro');
module.exports = withBabelize(getDefaultConfig(__dirname), {
locales: ['de', 'fr', 'ja'],
});// metro.config.js (bare React Native)
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withBabelize } = require('@babelize/metro');
module.exports = withBabelize(mergeConfig(getDefaultConfig(__dirname), {}), {
locales: ['de', 'fr', 'ja'],
});The API key comes from BABELIZE_API_KEY in the environment (or the apiKey option, read on your machine only). If any locale fails to translate, no lockfile is written, so a build never ships half-translated: in CI (CI set) or without an existing lockfile, Metro stops with the error; locally (say, offline during expo start) it warns and keeps the last lockfile.
| Option | Default | |
|---|---|---|
locales | — | Target locales to translate into |
apiKey | BABELIZE_API_KEY | Babelize API key |
output | babelize-lock.json | Lockfile path, relative to the project root |
include | root files, app/, src/, components/, screens/, navigation/, features/, hooks/, lib/, constants/ | Globs to scan. The native ios/ and android/ projects are never scanned |
functionName | babelize | The call name to scan for |
The lockfile is written when Metro starts. After adding strings during development, restart Metro (r doesn't reload the config).
In CI, without Metro
babelize-lock writes the same lockfile from the command line, for a CI step or a prebuild script:
BABELIZE_API_KEY=… npx babelize-lock --locales de,fr,jaOptions: --output <file>, --include <glob> (repeatable), --root <dir>, --api-url <url>. It exits 1 when a locale fails and 2 on a usage error.
2. Load it at startup
// app/_layout.tsx (Expo Router), or App.tsx
import { getLocales } from 'expo-localization';
import { babelize } from '@babelize/sdk';
import { BabelizeProvider } from '@babelize/sdk/react';
import lockfile from '../babelize-lock.json';
babelize.loadLockfile(lockfile);
babelize.init({
// The device's language. Without detectLocale the SDK reads it from Intl.
detectLocale: () => getLocales()[0]?.languageCode ?? null,
});
export default function RootLayout() {
return <BabelizeProvider>{/* your navigator */}</BabelizeProvider>;
}In bare React Native, react-native-localize's getLocales() works the same way. Without detectLocale, the SDK uses the device locale that Hermes's Intl reports, then fallbackLocale (en).
Commit babelize-lock.json, or generate it before every build: the import needs the file to exist.
3. Translate in components
Name the hook's function babelize: the scanner finds strings by that call name.
import { Text } from 'react-native';
import { useBabelize } from '@babelize/sdk/react';
export function OrderSummary({ count }: { count: number }) {
const babelize = useBabelize();
return (
<>
<Text>{babelize('Order history')}</Text>
<Text>{babelize.p('{count} order', '{count} orders', count)}</Text>
</>
);
}<BabelizeTag> renders plain text and your segments, so put it inside a <Text>:
<Text>
<BabelizeTag template="Read the {terms}" segments={{ terms: <Text style={styles.link}>terms</Text> }} />
</Text>To switch language, call babelize.setLocale('fr') (for example from a settings screen); components using useBabelize() re-render.
Testing
The SDK works under jest-expo and the React Native Jest preset without extra configuration. In tests, set the locale directly:
babelize.init({ locale: 'de' });Last updated: 2026-10-15
Next.js Plugin
The Babelize Next.js plugin generates the translation lockfile at build time. Setup, how it works, the available options, and use with @babelize/sdk/next.
Pluralization
Handle plurals with the Babelize SDK: simple singular and plural pairs, explicit CLDR forms such as few and many via Intl.PluralRules, React usage, and storage.