# react-native: Internationalize a Mini Shop URL: https://generaltranslation.com/en-US/docs/react-native/tutorials/mini-shop.mdx --- title: Internationalize a Mini Shop description: A hands-on React Native tutorial that internationalizes a simple shop using GT React Native components, hooks, and shared strings --- `gt-react-native` is still experimental and may not work for all projects. Please let us know if you encounter any issues by [opening an issue on GitHub](https://github.com/generaltranslation/gt/issues). Get a small, fully local "mini shop" running and translated in React Native — no external services, no routing, no UI frameworks beyond Expo. You'll use the core GT React Native features end-to-end and see how they fit together in a simple, realistic mobile UI. Prerequisites: React Native (Expo), basic JavaScript/TypeScript What you'll build - A single-screen "shop" with a product list and a simple in-memory cart - Language switcher and shared navigation labels - Properly internationalized numbers, currency, and pluralization Links used in this tutorial - Provider: [``](/docs/react-native/api/components/gtprovider) - Components: [``](/docs/react-native/api/components/t), [``](/docs/react-native/api/components/var), [``](/docs/react-native/api/components/num), [``](/docs/react-native/api/components/currency), [``](/docs/react-native/api/components/datetime), [``](/docs/react-native/api/components/branch), [``](/docs/react-native/api/components/plural), [``](/docs/react-native/api/components/locale-selector) - Strings and Shared Strings: [`useGT`](/docs/react-native/api/strings/use-gt), [`msg`](/docs/react-native/api/strings/msg), [`useMessages`](/docs/react-native/api/strings/use-messages) - Guides: [Variables](/docs/react-native/guides/variables), [Branching](/docs/react-native/guides/branches), [Strings](/docs/react-native/guides/strings), [Local Translation Storage](/docs/react-native/guides/local-tx), [Changing Languages](/docs/react-native/guides/languages) --- ## Install and Wrap Your App Install packages and wrap your app with the provider. ```bash npm i gt-react-native npm i --save-dev gtx-cli ``` ```bash yarn add gt-react-native yarn add --dev gtx-cli ``` ```bash bun add gt-react-native bun add --dev gtx-cli ``` ```bash pnpm add gt-react-native pnpm add --save-dev gtx-cli ``` Optional: Starter Project (Expo) If you're starting from scratch, scaffold an Expo app and then install GT packages: ```bash copy npx create-expo-app mini-shop --template blank-typescript cd mini-shop npm i gt-react-native npm i --save-dev gtx-cli ``` Then add the files in the sections below (e.g., `App.tsx`, `components/*`, `data.ts`, `nav.ts`). Create a minimal provider setup. ```tsx title="App.tsx" copy import { GTProvider } from 'gt-react-native'; import Shop from './Shop'; export default function App() { return ( ); } ``` Optionally add a `gt.config.json` now (useful later for CI and local storage): ```json title="gt.config.json" copy { "defaultLocale": "en", "locales": ["es", "fr"] } ``` ### Development API keys You can follow this tutorial without keys (it will render the default language). To see live translations and test language switching in development, add development keys. Learn more in [Production vs Development](/docs/react-native/concepts/environments). ```bash title=".env" copy EXPO_PUBLIC_GT_API_KEY="your-dev-api-key" EXPO_PUBLIC_GT_PROJECT_ID="your-project-id" ``` - Dashboard: https://dash.generaltranslation.com/signup - Or via CLI: ```bash copy npx gtx-cli auth ``` --- ## Seed data and app structure We'll hardcode a tiny product array and keep everything client-side. No servers, no navigation. ```ts title="data.ts" copy export type Product = { id: string; name: string; description: string; price: number; currency: 'USD' | 'EUR'; inStock: boolean; addedAt: string; // ISO date string }; export const products: Product[] = [ { id: 'p-1', name: 'Wireless Headphones', description: 'Noise-cancelling over-ear design with 30h battery', price: 199.99, currency: 'USD', inStock: true, addedAt: new Date().toISOString() }, { id: 'p-2', name: 'Travel Mug', description: 'Double-wall insulated stainless steel (12oz)', price: 24.5, currency: 'USD', inStock: false, addedAt: new Date().toISOString() } ]; ``` --- ## Shared navigation labels with msg and useMessages Use [`msg`](/docs/react-native/api/strings/msg) to mark shared strings in config, then decode them via [`useMessages`](/docs/react-native/api/strings/use-messages) at render time. ```tsx title="nav.ts" copy import { msg } from 'gt-react-native'; export const navItems = [ { label: msg('Home'), id: 'home' }, { label: msg('Products'), id: 'products' }, { label: msg('Cart'), id: 'cart' } ]; ``` ```tsx title="components/Header.tsx" copy import { View, Text, StyleSheet } from 'react-native'; import { LocaleSelector, T } from 'gt-react-native'; import { useMessages } from 'gt-react-native'; import { navItems } from '../nav'; export default function Header() { const m = useMessages(); return ( Mini Shop {navItems.map(item => ( {m(item.label)} ))} ); } const styles = StyleSheet.create({ header: { paddingVertical: 16, paddingHorizontal: 12, borderBottomWidth: 1, borderBottomColor: '#eee', }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 8, }, nav: { flexDirection: 'row', gap: 12, marginBottom: 8, }, navItem: { fontSize: 14, color: '#555', }, }); ``` --- ## Product cards with T, variables, Branch, and Currency Use [``](/docs/react-native/api/components/t) for JSX translation. Wrap dynamic content with variable components like [``](/docs/react-native/api/components/var), [``](/docs/react-native/api/components/num), [``](/docs/react-native/api/components/currency), and [``](/docs/react-native/api/components/datetime). Handle stock state via [``](/docs/react-native/api/components/branch). ```tsx title="components/ProductCard.tsx" copy import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { T, Var, Currency, DateTime, Branch } from 'gt-react-native'; import type { Product } from '../data'; export default function ProductCard({ product, onAdd }: { product: Product; onAdd: () => void }) { return ( {product.name} {product.description} Price: {product.price} Added: {product.addedAt} In stock} false={Out of stock} /> Add to cart ); } const styles = StyleSheet.create({ card: { borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 12, marginBottom: 12, }, name: { fontSize: 18, fontWeight: '600', marginBottom: 4, }, description: { fontSize: 14, color: '#666', marginBottom: 8, }, price: { fontSize: 16, marginBottom: 4, }, date: { fontSize: 12, color: '#999', marginBottom: 8, }, inStock: { color: 'green', marginBottom: 8, }, outOfStock: { color: 'tomato', marginBottom: 8, }, button: { backgroundColor: '#007AFF', paddingVertical: 10, borderRadius: 6, alignItems: 'center', }, buttonDisabled: { backgroundColor: '#ccc', }, buttonText: { color: '#fff', fontWeight: '600', }, }); ``` --- ## Cart with Pluralization and Totals Use [``](/docs/react-native/api/components/plural) to express "X items in cart" and [``](/docs/react-native/api/components/currency) for totals. Combine with [``](/docs/react-native/api/components/t), [``](/docs/react-native/api/components/var), and [``](/docs/react-native/api/components/num). ```tsx title="components/Cart.tsx" copy import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { T, Plural, Var, Num, Currency } from 'gt-react-native'; import type { Product } from '../data'; export default function Cart({ items, onClear }: { items: Product[]; onClear: () => void }) { const total = items.reduce((sum, p) => sum + p.price, 0); const itemCount = items.length; return ( Cart Your cart is empty} one={You have {itemCount} item} other={You have {itemCount} items} /> {items.map((p) => ( {p.name}{p.price} ))} Total: {total} Clear cart ); } const styles = StyleSheet.create({ container: { borderTopWidth: 1, borderTopColor: '#eee', paddingTop: 16, marginTop: 16, }, heading: { fontSize: 20, fontWeight: 'bold', marginBottom: 8, }, empty: { color: '#999', marginBottom: 8, }, item: { fontSize: 14, marginVertical: 4, }, total: { fontSize: 16, fontWeight: '600', marginTop: 8, marginBottom: 12, }, button: { backgroundColor: '#FF3B30', paddingVertical: 10, borderRadius: 6, alignItems: 'center', }, buttonDisabled: { backgroundColor: '#ccc', }, buttonText: { color: '#fff', fontWeight: '600', }, }); ``` --- ## Attributes and Placeholders with `useGT` Use [`useGT`](/docs/react-native/api/strings/use-gt) for plain string translations like input placeholders and accessibility labels. ```tsx title="components/Search.tsx" copy import { TextInput, StyleSheet } from 'react-native'; import { useGT } from 'gt-react-native'; export default function Search({ onQuery }: { onQuery: (q: string) => void }) { const gt = useGT(); return ( ); } const styles = StyleSheet.create({ input: { borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 10, fontSize: 16, }, }); ``` --- ## Put it together A single-screen app with in-memory cart and simple search filter. ```tsx title="Shop.tsx" copy import { useMemo, useState } from 'react'; import { SafeAreaView, ScrollView, View, StyleSheet } from 'react-native'; import Header from './components/Header'; import Search from './components/Search'; import ProductCard from './components/ProductCard'; import Cart from './components/Cart'; import { products } from './data'; export default function Shop() { const [query, setQuery] = useState(''); const [cart, setCart] = useState([]); const filtered = useMemo(() => { const q = query.toLowerCase(); return products.filter(p => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q) ); }, [query]); const items = products.filter(p => cart.includes(p.id)); return (
{filtered.map(p => ( setCart(c => (p.inStock ? [...new Set([...c, p.id])] : c))} /> ))} setCart([])} /> ); } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: '#fff', }, scroll: { padding: 16, }, searchContainer: { marginVertical: 12, }, products: { gap: 0, }, }); ``` --- ## Run locally Start the Expo dev server: ```bash npx expo start ``` Then open in the Expo Go app on your phone, or press `i` for iOS simulator / `a` for Android emulator. --- ## What you learned - Translating JSX with [``](/docs/react-native/api/components/t) and handling dynamic content via [``](/docs/react-native/api/components/var), [``](/docs/react-native/api/components/num), [``](/docs/react-native/api/components/currency), [``](/docs/react-native/api/components/datetime) - Expressing conditional content with [``](/docs/react-native/api/components/branch) and quantities with [``](/docs/react-native/api/components/plural) - Translating attributes with [`useGT`](/docs/react-native/api/strings/use-gt) - Sharing navigation/config strings using [`msg`](/docs/react-native/api/strings/msg) and decoding them with [`useMessages`](/docs/react-native/api/strings/use-messages) - Switching languages with [``](/docs/react-native/api/components/locale-selector) ## Next steps - Explore: [Variables Guide](/docs/react-native/guides/variables), [Branching Guide](/docs/react-native/guides/branches), [Strings Guide](/docs/react-native/guides/strings), [Changing Languages](/docs/react-native/guides/languages)