Quick Start
Expo
This page demonstrates the minimal setup of Intor in an Expo project.
Installation
This guide assumes that an Expo project has already been created.
For project setup, see: Expo
To enable locale persistence, install:
expo-localization@react-native-async-storage/async-storage
npx expo install expo-localization @react-native-async-storage/async-storage
Install Intor:
npm install intoryarn add intorpnpm add intorbun add intor
Project Structure
index.jsonUindex.jsonU_layout.tsxMindex.tsxMintor-config.tsUintor-client-provider.tsxU
Integration Steps
♯1 Messages
Create a messages directory in your project, and add a subdirectory for each locale.
Each locale provides an index.json file.
index.jsonUindex.jsonU
{ "hello": "Hello, {name}", "rich": "A <tag>text</tag>." }
{ "hello": "Bonjour, {name}", "rich": "Un <tag>texte</tag>." }
♯2 Config
Create an i18n/ directory under src/, and add an intor-config.ts file inside it.
This example uses the most basic translation loading approach by defining messages directly in the config.
For more details, see: Message Loading
intor-config.tsU
import { defineIntorConfig } from "intor"; import EN from "../../messages/en/index.json"; import FR from "../../messages/fr/index.json"; export const intorConfig = defineIntorConfig({ defaultLocale: "en", supportedLocales: ["en", "fr"], messages: { en: EN, fr: FR, }, });
♯3 Initialization
Create intor-client-provider.tsx inside the i18n/ directory.
intor-client-provider.tsxU
import AsyncStorage from "@react-native-async-storage/async-storage"; import { getLocales } from "expo-localization"; import { matchLocale } from "intor"; import { IntorProvider } from "intor/react"; import { useEffect, useState, type ReactNode } from "react"; import { intorConfig } from "./intor-config"; const STORAGE_KEY = "locale"; function resolveLocale(candidate?: string): string { const matched = matchLocale(candidate, intorConfig.supportedLocales); return matched ?? intorConfig.defaultLocale; } function getInitialLocale(): string { return resolveLocale(getLocales()[0]?.languageTag); } export function IntorClientProvider({ children }: { children: ReactNode }) { const [locale, setLocale] = useState(getInitialLocale); useEffect(() => { (async () => { const stored = await AsyncStorage.getItem(STORAGE_KEY); if (stored) setLocale(resolveLocale(stored)); })(); }, []); return ( <IntorProvider value={{ config: intorConfig, locale, onLocaleChange: async (locale) => { await AsyncStorage.setItem(STORAGE_KEY, locale); }, }} > {children} </IntorProvider> ); }
Then, wrap your application with this provider in _layout.tsx.
_layout.tsxM
// ... import { IntorClientProvider } from "../i18n/intor-client-provider"; export default function TabLayout() { const colorScheme = useColorScheme(); return ( <IntorClientProvider> <ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}> <AnimatedSplashOverlay /> <AppTabs /> </ThemeProvider> </IntorClientProvider> ); }
Usage Example
Use useTranslator to access translation capabilities:
t: returns the resolved texttRich: renders structured messages with semantic tags
index.tsxM
import { useTranslator } from "intor/react"; import { Button, Text, View } from "react-native"; export default function HomeScreen() { const { t, tRich, setLocale } = useTranslator(); return ( <View style={{ margin: 80, gap: 20, backgroundColor: "white" }}> <Text>{t("hello", { name: "Intor" })}</Text> <Text> {tRich("rich", { tag: (children) => ( <Text style={{ fontWeight: "bold" }}>{children}</Text> ), })} </Text> <Button title="English" onPress={() => setLocale("en")} /> <Button title="French" onPress={() => setLocale("fr")} /> </View> ); }