# 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)