Basic Usage
Learn the fundamentals of using LiveI18n in your Expo application.
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>
);
}
Navigation and UI Labels
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
Navigation Menu
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
- Change your device language settings
- Force-close and reopen your app
- Verify translations appear correctly
Using Expo Dev Tools
Monitor translation behavior:
- Open Expo dev tools
- Check console logs for translation requests
- Monitor network requests and caching
Testing Your Setup
Try the Expo Demo
Test your setup with our complete Expo demo application:
- Clone the repository
- Add your API keys to
.env.local - Run with
npm start - Test translations and language switching
Next Steps
Now that you understand basic usage:
- Try the Expo demo - Complete working example
- Learn best practices - Optimization tips
- Configure caching - Performance optimization
Troubleshooting
Translations Not Appearing
- Check console for errors in Expo dev tools
- Verify SDK initialization
- Test API keys with a simple text
- Check network connectivity
Slow Performance
- Monitor cache hit rates
- Avoid translating dynamic content
- Use consistent context and tone
- Check for excessive re-renders
Need help? Contact our support team or check the troubleshooting guide.