Skip to main content

Internationalization (i18n)

Shipnative comes with a robust internationalization system built on i18next and react-i18next. The app supports 7 languages out of the box and makes it easy to add more.

Supported Languages

LanguageCodeRTL Support
EnglishenNo
ArabicarYes
SpanishesNo
FrenchfrNo
HindihiNo
JapanesejaNo
KoreankoNo

Quick Start

Using Translations in Components

There are two main ways to use translations: The Text component (and many other components) support a tx prop for translation keys:
import { Text } from "@/components"

// Simple translation
<Text tx="homeScreen:goodMorning" />

// With interpolation
<Text
  tx="profileScreen:version"
  txOptions={{ version: "1.0.0", build: "12" }}
/>

2. Using the useTranslation Hook (For Dynamic Text)

When you need more control or want to use translations in logic:
import { useTranslation } from "react-i18next"

function MyComponent() {
  const { t } = useTranslation()

  return (
    <View>
      <Text>{t("homeScreen:goodMorning")}</Text>
      <Text>{t("profileScreen:version", { version: "1.0.0", build: "12" })}</Text>
    </View>
  )
}

Translation File Structure

Translation files are located in app/i18n/:
app/i18n/
├── index.ts          # i18n configuration
├── en.ts             # English (source of truth)
├── ar.ts             # Arabic
├── es.ts             # Spanish
├── fr.ts             # French
├── hi.ts             # Hindi
├── ja.ts             # Japanese
├── ko.ts             # Korean
├── translate.ts      # Helper function
└── languageSwitcher.ts # Language switching utilities

Translation Key Structure

Translation keys are organized by screen/feature:
// en.ts
const en = {
  common: {
    ok: "OK!",
    cancel: "Cancel",
    back: "Back",
  },
  homeScreen: {
    goodMorning: "Good morning,",
    dailyChallenge: "Daily Challenge",
    // ...
  },
  profileScreen: {
    title: "Profile",
    settingsTitle: "Settings",
    version: "Version {{version}} (Build {{build}})",
    // ...
  },
  // ... more screens
}

Adding New Translations

Step 1: Add Keys to English File

English (en.ts) is the source of truth. Always add new keys here first:
// app/i18n/en.ts
const en = {
  // ... existing keys
  myNewScreen: {
    title: "My New Screen",
    description: "This is a new feature",
    button: "Get Started",
    greeting: "Hello, {{name}}!",  // With interpolation
  },
}

Step 2: Update Other Language Files

Add the same keys to all other language files with translated values:
// app/i18n/es.ts
const es: Translations = {
  // ... existing keys
  myNewScreen: {
    title: "Mi Nueva Pantalla",
    description: "Esta es una nueva característica",
    button: "Comenzar",
    greeting: "¡Hola, {{name}}!",
  },
}

Step 3: Use in Components

// Using tx prop
<Text tx="myNewScreen:title" />
<Button tx="myNewScreen:button" onPress={handlePress} />

// Using useTranslation hook
const { t } = useTranslation()
<Text>{t("myNewScreen:greeting", { name: userName })}</Text>

Component Support for tx Props

Many components support translation props directly:
ComponentTranslation Props
Texttx, txOptions
Buttontx, txOptions
Badgetx
TextFieldlabelTx, placeholderTx, helperTx
CardheadingTx, contentTx, footerTx
ModaltitleTx, descriptionTx
EmptyStateheadingTx, contentTx, buttonTx
HeaderleftTx, rightTx
AuthScreenLayouttitleTx, subtitleTx
OnboardingScreenLayouttitleTx, subtitleTx

Interpolation (Dynamic Values)

Use {{variable}} syntax for dynamic values:
// In en.ts
profileScreen: {
  version: "Version {{version}} (Build {{build}})",
  greeting: "Hello, {{name}}!",
  itemCount: "You have {{count}} items",
}

// In component
<Text
  tx="profileScreen:version"
  txOptions={{ version: "1.0.0", build: "12" }}
/>

// Or with useTranslation
const { t } = useTranslation()
t("profileScreen:greeting", { name: "John" })

Language Switching

Programmatic Language Change

import { changeLanguage } from "@/i18n/languageSwitcher"

// Change language and persist the preference
await changeLanguage("es", true)

// Change without persisting
await changeLanguage("fr", false)

Built-in Language Selector

Shipnative includes a LanguageSelector component:
import { LanguageSelector } from "@/components"

<LanguageSelector
  visible={isVisible}
  onClose={() => setIsVisible(false)}
/>

Get Current Language

import { getCurrentLanguage, getPersistedLanguage } from "@/i18n/languageSwitcher"

const currentLang = getCurrentLanguage()      // Currently active
const savedLang = getPersistedLanguage()      // Saved preference

RTL (Right-to-Left) Support

Arabic is automatically detected and enables RTL layout:
// The system automatically:
// 1. Detects Arabic locale
// 2. Calls I18nManager.allowRTL(true)
// 3. Enables RTL layout for the entire app

Type Safety

The i18n system provides full TypeScript support. Invalid keys will cause TypeScript errors:
// ✅ Valid - TypeScript knows this key exists
<Text tx="homeScreen:goodMorning" />

// ❌ Invalid - TypeScript error: key doesn't exist
<Text tx="homeScreen:invalidKey" />

Best Practices

DO

// ✅ Use tx prop for static text
<Text tx="homeScreen:title" />

// ✅ Use interpolation for dynamic values
<Text tx="profile:greeting" txOptions={{ name: userName }} />

// ✅ Organize keys by screen/feature
loginScreen: { title: "...", subtitle: "..." }

// ✅ Keep English file as source of truth
// Always add new keys to en.ts first

DON’T

// ❌ Don't hardcode strings
<Text>Welcome to the app</Text>

// ❌ Don't concatenate translations
<Text>{t("hello") + " " + name}</Text>  // Use interpolation instead

// ❌ Don't use nested translation keys more than 2 levels deep
// Bad: screen.section.subsection.item.label
// Good: screenSection:itemLabel

Adding a New Language

  1. Create the language file:
// app/i18n/de.ts
import { Translations } from "./en"

const de: Translations = {
  common: {
    ok: "OK",
    cancel: "Abbrechen",
    back: "Zurück",
  },
  // ... translate all keys from en.ts
}

export default de
  1. Register in i18n config:
// app/i18n/index.ts
import de from "./de"

const resources = {
  en: { translation: en },
  es: { translation: es },
  de: { translation: de },  // Add new language
  // ...
}
  1. Add to language selector:
// app/i18n/languageSwitcher.ts
export const SUPPORTED_LANGUAGES = [
  { code: "en", name: "English", nativeName: "English", flag: "🇺🇸" },
  { code: "de", name: "German", nativeName: "Deutsch", flag: "🇩🇪" },
  // ...
]

Testing Translations

To verify translations are working:
  1. Change language in Profile settings
  2. Use the language selector component
  3. Check for missing keys (they’ll display as the key path)
# Run the app and look for untranslated keys like:
# "myScreen:missingKey" instead of actual text

Troubleshooting

Translations Not Updating

  • Ensure you’re importing from the correct i18n file
  • Check that the key exists in the language file
  • Verify the component supports the tx prop

TypeScript Errors for Valid Keys

  • Run yarn typecheck to regenerate types
  • Ensure en.ts exports the Translations type

RTL Layout Issues

  • Check that I18nManager.allowRTL(true) is called
  • Some custom styles may need flexDirection: 'row-reverse' for RTL