Skip to main content

Basic Usage

Learn the fundamentals of using LiveI18n in your Expo application.

Prerequisites

Make sure you have wrapped your app with LiveI18nProvider as shown in the installation guide. All examples below assume your components are inside the provider.

Core Component

LiveText Component

The <LiveText> component is the primary way to add translations to your app:

Note: <LiveText> is not a replacement for Expo's <Text> component. It should be used as a leaf node and wrapped in your own <Text> components for styling.

import { LiveText } from '@livei18n/react-native-expo-sdk';
import { View, Text } from 'react-native';

function WelcomeScreen() {
return (
<View>
<Text style={styles.heading}>
<LiveText>Welcome to our app!</LiveText>
</Text>
</View>
);
}

With Context and Tone

Add context and tone for better translation quality:

<LiveText context="main navigation" tone="friendly">
Home
</LiveText>

<LiveText context="customer support" tone="formal">
Contact our support team
</LiveText>

<LiveText context="shopping cart action" tone="casual">
Add to cart
</LiveText>

With Language Override

Force translation to a specific language:

function MultiLanguageWelcome() {
return (
<View>
<Text style={styles.greeting}>
<LiveText language="es-ES">Welcome to our app!</LiveText>
</Text>
<Text style={styles.greeting}>
<LiveText language="fr-FR">Welcome to our app!</LiveText>
</Text>
<Text style={styles.greeting}>
<LiveText language="zh-CN">Welcome to our app!</LiveText>
</Text>
</View>
);
}

The useLiveText Hook

The useLiveText hook provides a programmatic way to translate text and returns a string value. This is ideal for React Native scenarios where you need translated text in JavaScript logic, state management, or dynamic content generation.

Basic Usage

import { useLiveText } from '@livei18n/react-native-expo-sdk';
import { View, Text, Button } from 'react-native';

function ProfileScreen({ user }) {
const greeting = useLiveText("Good morning");
const profileTitle = useLiveText("User Profile");
const saveButtonTitle = useLiveText("Save Changes");

return (
<View>
<Text style={styles.greeting}>{greeting}, {user.name}!</Text>
<Text style={styles.title}>{profileTitle}</Text>
<Button
title={saveButtonTitle}
onPress={() => console.log('Save pressed')}
/>
</View>
);
}

Dynamic Content with Context

function NotificationHandler({ notifications }) {
const emptyMessage = useLiveText("No new notifications", {
context: "notification center",
tone: "neutral"
});

const countMessage = useLiveText(
`You have ${notifications.length} unread messages`,
{ context: "notification badge" }
);

const alertTitle = useLiveText("New Message", {
context: "push notification",
tone: "urgent"
});

const showAlert = () => {
Alert.alert(alertTitle, countMessage);
};

return (
<View>
<Text>{notifications.length === 0 ? emptyMessage : countMessage}</Text>
<TouchableOpacity onPress={showAlert}>
<Text>Show Alert</Text>
</TouchableOpacity>
</View>
);
}
function NavigationScreen() {
const homeTab = useLiveText("Home", { context: "bottom tab" });
const searchTab = useLiveText("Search", { context: "bottom tab" });
const profileTab = useLiveText("Profile", { context: "bottom tab" });

const navigation = useNavigation();

const navigateWithLabel = (screen: string, label: string) => {
navigation.navigate(screen, { title: label });
};

return (
<View style={styles.tabBar}>
<TouchableOpacity onPress={() => navigateWithLabel('Home', homeTab)}>
<Text>{homeTab}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigateWithLabel('Search', searchTab)}>
<Text>{searchTab}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigateWithLabel('Profile', profileTab)}>
<Text>{profileTab}</Text>
</TouchableOpacity>
</View>
);
}

Form Validation Messages

function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});

const emailError = useLiveText("Please enter a valid email address", {
context: "form validation",
tone: "helpful"
});

const passwordError = useLiveText("Password must be at least 8 characters", {
context: "form validation",
tone: "instructional"
});

const submitButton = useLiveText("Sign In", {
context: "authentication",
tone: "action"
});

const handleSubmit = () => {
const newErrors = {};
if (!isValidEmail(email)) newErrors.email = emailError;
if (password.length < 8) newErrors.password = passwordError;
setErrors(newErrors);
};

return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email"
/>
{errors.email && <Text style={styles.error}>{errors.email}</Text>}

<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
/>
{errors.password && <Text style={styles.error}>{errors.password}</Text>}

<TouchableOpacity onPress={handleSubmit}>
<Text style={styles.button}>{submitButton}</Text>
</TouchableOpacity>
</View>
);
}

Language-Specific Content

function MultiLanguageContent() {
const englishWelcome = useLiveText("Welcome to our app!", {
language: "en-US",
context: "app introduction"
});

const spanishWelcome = useLiveText("Welcome to our app!", {
language: "es-ES",
context: "app introduction"
});

const chineseWelcome = useLiveText("Welcome to our app!", {
language: "zh-CN",
context: "app introduction"
});

return (
<ScrollView>
<View style={styles.languageSection}>
<Text style={styles.label}>English:</Text>
<Text>{englishWelcome}</Text>
</View>
<View style={styles.languageSection}>
<Text style={styles.label}>Español:</Text>
<Text>{spanishWelcome}</Text>
</View>
<View style={styles.languageSection}>
<Text style={styles.label}>中文:</Text>
<Text>{chineseWelcome}</Text>
</View>
</ScrollView>
);
}

When to Use useLiveText vs LiveText

Use useLiveText when you need a translated string and can't use a component (JavaScript logic, state values, alerts, navigation titles, etc.)

Use <LiveText> when you can use a component directly in your JSX.

function MixedTranslationExample({ user, hasNotifications }) {
const dynamicTitle = useLiveText(
hasNotifications ? "You have messages" : "No new messages",
{ context: "notification status" }
);

return (
<View>
<Text style={styles.title}>{dynamicTitle}</Text>
<Text style={styles.subtitle}>
<LiveText context="user greeting">
Welcome back, {user.name}!
</LiveText>
</Text>
</View>
);
}

Automatic Language Updates

The useLiveText hook automatically responds to default language changes, just like the <LiveText> component:

function LanguageSwitcher() {
const { updateDefaultLanguage, defaultLanguage } = useLiveI18n();
const buttonText = useLiveText("Switch to Spanish");
const currentLangText = useLiveText("Current language");

const handleLanguageSwitch = () => {
const newLang = defaultLanguage === 'en-US' ? 'es-ES' : 'en-US';
updateDefaultLanguage(newLang);
// Both buttonText and currentLangText will automatically update
};

return (
<View>
<Text>{currentLangText}: {defaultLanguage}</Text>
<TouchableOpacity onPress={handleLanguageSwitch}>
<Text style={styles.button}>{buttonText}</Text>
</TouchableOpacity>
</View>
);
}

Language Management

Global Language Updates

Change the language for your entire app using the useLiveI18n hook:

import React from 'react';
import { View, Button } from 'react-native';
import { useLiveI18n } from '@livei18n/react-native-expo-sdk';

function LanguageSwitcher() {
const { defaultLanguage, updateDefaultLanguage } = useLiveI18n();

const changeLanguage = (language: string) => {
updateDefaultLanguage(language);
};

return (
<View style={{ flexDirection: 'row', gap: 10 }}>
<Button
title="English"
onPress={() => changeLanguage('en-US')}
/>
<Button
title="Español"
onPress={() => changeLanguage('es-ES')}
/>
<Button
title="Français"
onPress={() => changeLanguage('fr-FR')}
/>

{/* Show current language */}
<Text>Current: {defaultLanguage || 'Auto-detect'}</Text>
</View>
);
}

Programmatic Translation

Use the translate function from the hook for non-component translations:

import React from 'react';
import { View, Button, Alert } from 'react-native';
import { useLiveI18n } from '@livei18n/react-native-expo-sdk';

function ActionButton() {
const { translate } = useLiveI18n();

const handlePress = async () => {
// Translate text programmatically
const message = await translate('Operation completed successfully!', {
context: 'success message',
tone: 'celebratory'
});

Alert.alert('Success', message);
};

return (
<Button title="Complete Action" onPress={handlePress} />
);
}

Common Patterns

const navigationItems = [
{ key: 'home', label: 'Home' },
{ key: 'about', label: 'About Us' },
{ key: 'products', label: 'Products' },
{ key: 'contact', label: 'Contact' }
];

function NavigationMenu() {
return (
<View>
{navigationItems.map(item => (
<TouchableOpacity key={item.key}>
<LiveText context="main navigation">
{item.label}
</LiveText>
</TouchableOpacity>
))}
</View>
);
}

Form Labels

function ContactForm() {
return (
<View>
<Text style={styles.label}>
<LiveText context="form label">Your Name</LiveText>
</Text>
<TextInput placeholder="Enter your name" />

<Text style={styles.label}>
<LiveText context="form label">Email Address</LiveText>
</Text>
<TextInput placeholder="Enter your email" />

<TouchableOpacity style={styles.button}>
<LiveText context="form button" tone="action">
Send Message
</LiveText>
</TouchableOpacity>
</View>
);
}

Status Messages

function StatusMessages({ status }) {
const getStatusMessage = () => {
switch (status) {
case 'loading':
return <LiveText context="status message">Loading...</LiveText>;
case 'success':
return <LiveText context="status message" tone="celebratory">Success!</LiveText>;
case 'error':
return <LiveText context="status message" tone="apologetic">Something went wrong</LiveText>;
default:
return null;
}
};

return <View>{getStatusMessage()}</View>;
}

Automatic Locale Detection

The SDK automatically detects the device's locale and provides utilities via the useLiveI18n hook:

import React from 'react';
import { View, Text } from 'react-native';
import { useLiveI18n } from '@livei18n/react-native-expo-sdk';

function LocaleInfo() {
const { getPreferredLocales, getDetailedLocale, isRTL } = useLiveI18n();

return (
<View>
<Text>Preferred Locales: {getPreferredLocales().join(', ')}</Text>
<Text>Current Locale: {getDetailedLocale().languageTag}</Text>
<Text>RTL Layout: {isRTL() ? 'Yes' : 'No'}</Text>
</View>
);
}

// The locale detection happens automatically when using LiveText

Supported Locale Formats

The SDK handles various locale formats:

  • Standard: en-US, es-ES, fr-FR
  • Language only: en, es, fr
  • Natural language: Spanish, French, English

Best Practices

1. Use Descriptive Context

// ✅ Good - provides clear context
<LiveText context="shopping cart button">Add to Cart</LiveText>

// ❌ Avoid - too generic
<LiveText context="button">Add to Cart</LiveText>

2. Choose Appropriate Tone

// ✅ Match tone to content
<LiveText tone="professional">Terms of Service</LiveText>
<LiveText tone="friendly">Welcome!</LiveText>
<LiveText tone="urgent">Action Required</LiveText>

3. Keep Text Semantic

// ✅ Semantic and translatable
<LiveText>Learn More</LiveText>

// ❌ Avoid technical jargon that doesn't translate well
<LiveText>onPress Handler</LiveText>

Performance Considerations

Caching Behavior

The SDK automatically caches translations:

// First call - API request (~200ms)
<LiveText>Welcome</LiveText>

// Subsequent calls - memory cache (< 1ms)
<LiveText>Welcome</LiveText>

// After app restart - AsyncStorage cache (~5ms)
<LiveText>Welcome</LiveText>

Avoiding Cache Pollution

// ✅ Good - stable cache keys
<LiveText context="greeting">Hello</LiveText>

// ⚠️ Be careful - creates many cache entries
<LiveText>Hello {Math.random()}</LiveText> // Don't do this

Memory Management

The SDK uses LRU (Least Recently Used) cache eviction:

  • 500 entries by default (customizable)
  • 1-hour TTL by default (customizable)
  • Automatic cleanup of expired entries

Error Handling

Graceful Fallbacks

The SDK automatically falls back to original text if translation fails:

// If translation fails, shows "Hello, world!"
<LiveText>Hello, world!</LiveText>

Network Errors

Translations continue working during network issues thanks to caching:

// Works offline if previously cached
<LiveText>Welcome back!</LiveText>

Common Use Cases

E-commerce App

function ProductCard({ product }) {
return (
<View style={styles.productCard}>
<Text style={styles.productName}>{product.name}</Text>
<TouchableOpacity style={styles.addButton}>
<LiveText context="shopping cart action">Add to Cart</LiveText>
</TouchableOpacity>
</View>
);
}

Dashboard Screen

function Dashboard({ stats }) {
return (
<ScrollView style={styles.dashboard}>
<Text style={styles.title}>
<LiveText context="dashboard title">Analytics Dashboard</LiveText>
</Text>

<View style={styles.statsGrid}>
<View style={styles.stat}>
<Text style={styles.statNumber}>{stats.users}</Text>
<LiveText context="dashboard metric">Active Users</LiveText>
</View>
<View style={styles.stat}>
<Text style={styles.statNumber}>{stats.revenue}</Text>
<LiveText context="dashboard metric">Total Revenue</LiveText>
</View>
</View>
</ScrollView>
);
}

Testing Your Implementation

Getting Supported Languages

The SDK provides a method to fetch the list of languages supported by LiveI18n, which you can use to build language selectors or display available languages to users.

Using the Hook

import React, { useState, useEffect } from 'react';
import { View, Text, Modal, FlatList, TouchableOpacity } from 'react-native';
import { useLiveI18n } from '@livei18n/react-native-expo-sdk';

function LanguageSelector() {
const { getSupportedLanguages, updateDefaultLanguage } = useLiveI18n();
const [languages, setLanguages] = useState([]);
const [loading, setLoading] = useState(true);
const [modalVisible, setModalVisible] = useState(false);

useEffect(() => {
const fetchLanguages = async () => {
try {
// Get top 20 most popular languages
const response = await getSupportedLanguages();
setLanguages(response.languages);
} catch (error) {
console.error('Failed to fetch languages:', error);
} finally {
setLoading(false);
}
};

fetchLanguages();
}, []);

const handleLanguageChange = (locale: string) => {
updateDefaultLanguage(locale);
setModalVisible(false);
};

if (loading) {
return (
<View>
<Text>Loading languages...</Text>
</View>
);
}

return (
<View>
<TouchableOpacity onPress={() => setModalVisible(true)}>
<Text>Select Language</Text>
</TouchableOpacity>

<Modal visible={modalVisible} transparent={true} animationType="slide">
<View style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.5)' }}>
<View style={{
backgroundColor: 'white',
marginTop: 100,
borderRadius: 10,
margin: 20
}}>
<FlatList
data={languages}
keyExtractor={(item) => item.locale}
renderItem={({ item }) => (
<TouchableOpacity
style={{ padding: 15, borderBottomWidth: 1 }}
onPress={() => handleLanguageChange(item.locale)}
>
<Text>{item.flag} {item.name}</Text>
</TouchableOpacity>
)}
/>
</View>
</View>
</Modal>
</View>
);
}

Getting All Languages

By default, getSupportedLanguages() returns the top 20 most popular languages. To get all supported languages:

// Get all supported languages (not just top 20)
const response = await getSupportedLanguages(true);
console.log(`Total languages: ${response.total}`);

Response Format

The getSupportedLanguages() method returns:

interface SupportedLanguagesResponse {
languages: SupportedLanguage[];
total: number;
}

interface SupportedLanguage {
name: string; // "Spanish (Spain)"
locale: string; // "es-ES"
flag: string; // "🇪🇸"
}

Caching Behavior

Language lists are cached for 1 hour to improve performance:

// First call - fetches from API
const languages1 = await getSupportedLanguages();

// Second call within 1 hour - returns from cache
const languages2 = await getSupportedLanguages();

The cache maintains separate entries for top 20 vs all languages.

Language Picker Component

Here's a complete reusable language picker component:

import React, { useState, useEffect } from 'react';
import {
View,
Text,
Modal,
FlatList,
TouchableOpacity,
StyleSheet
} from 'react-native';
import { useLiveI18n } from '@livei18n/react-native-expo-sdk';

interface LanguagePickerProps {
onLanguageChange?: (locale: string) => void;
}

export function LanguagePicker({ onLanguageChange }: LanguagePickerProps) {
const {
getSupportedLanguages,
updateDefaultLanguage,
defaultLanguage
} = useLiveI18n();

const [languages, setLanguages] = useState([]);
const [loading, setLoading] = useState(true);
const [modalVisible, setModalVisible] = useState(false);

useEffect(() => {
const fetchLanguages = async () => {
try {
const response = await getSupportedLanguages();
setLanguages(response.languages);
} catch (error) {
console.error('Failed to fetch languages:', error);
// Fallback languages
setLanguages([
{ locale: 'en-US', name: 'English (US)', flag: '🇺🇸' },
{ locale: 'es-ES', name: 'Spanish (Spain)', flag: '🇪🇸' },
{ locale: 'fr-FR', name: 'French (France)', flag: '🇫🇷' }
]);
} finally {
setLoading(false);
}
};

fetchLanguages();
}, []);

const handleLanguageChange = (locale: string) => {
updateDefaultLanguage(locale);
onLanguageChange?.(locale);
setModalVisible(false);
};

const currentLanguage = languages.find(
lang => lang.locale === defaultLanguage
) || languages[0];

if (loading || !currentLanguage) {
return (
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Loading...</Text>
</TouchableOpacity>
);
}

return (
<>
<TouchableOpacity
style={styles.button}
onPress={() => setModalVisible(true)}
>
<Text style={styles.buttonText}>
{currentLanguage.flag} {currentLanguage.name}
</Text>
</TouchableOpacity>

<Modal visible={modalVisible} transparent animationType="slide">
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Select Language</Text>

<FlatList
data={languages}
keyExtractor={(item) => item.locale}
renderItem={({ item }) => (
<TouchableOpacity
style={[
styles.languageItem,
item.locale === defaultLanguage && styles.selectedItem
]}
onPress={() => handleLanguageChange(item.locale)}
>
<Text style={styles.languageText}>
{item.flag} {item.name}
</Text>
{item.locale === defaultLanguage && (
<Text style={styles.checkmark}></Text>
)}
</TouchableOpacity>
)}
/>

<TouchableOpacity
style={styles.closeButton}
onPress={() => setModalVisible(false)}
>
<Text style={styles.closeButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</>
);
}

const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
padding: 12,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontWeight: '600',
},
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
backgroundColor: 'white',
borderRadius: 12,
padding: 20,
width: '80%',
maxHeight: '70%',
},
modalTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 15,
textAlign: 'center',
},
languageItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
},
selectedItem: {
backgroundColor: '#f0f8ff',
},
languageText: {
fontSize: 16,
},
checkmark: {
color: '#007AFF',
fontWeight: 'bold',
},
closeButton: {
marginTop: 15,
padding: 12,
backgroundColor: '#f0f0f0',
borderRadius: 8,
alignItems: 'center',
},
closeButtonText: {
fontSize: 16,
fontWeight: '600',
},
});

Manual Testing

  1. Change your device language settings
  2. Force-close and reopen your app
  3. Verify translations appear correctly

Using Expo Dev Tools

Monitor translation behavior:

  1. Open Expo dev tools
  2. Check console logs for translation requests
  3. Monitor network requests and caching

Testing Your Setup

Try the Expo Demo

Test your setup with our complete Expo demo application:

LiveI18n Expo Demo

  1. Clone the repository
  2. Add your API keys to .env.local
  3. Run with npm start
  4. Test translations and language switching

Next Steps

Now that you understand basic usage:

Troubleshooting

Translations Not Appearing

  1. Check console for errors in Expo dev tools
  2. Verify SDK initialization
  3. Test API keys with a simple text
  4. Check network connectivity

Slow Performance

  1. Monitor cache hit rates
  2. Avoid translating dynamic content
  3. Use consistent context and tone
  4. Check for excessive re-renders

Need help? Contact our support team or check the troubleshooting guide.