i18n MVP Implementation Guide
Overview
This document demonstrates how to use the new internationalization formatting utilities without full translation infrastructure.
Quick Start
1. In Components (Client-Side)
Use the React hooks for automatic locale detection:
import { useFormat } from '~/lib/i18n';
export default function MyComponent() {
const { formatDate, formatCurrency, formatNumber } = useFormat();
const createdAt = new Date('2025-12-03');
const price = 99.99;
const users = 1234567;
return (
<div>
<p>Created: {formatDate(createdAt)}</p>
<p>Price: {formatCurrency(price)}</p>
<p>Users: {formatNumber(users)}</p>
</div>
);
}2. In Loaders/Actions (Server-Side)
Import utilities directly:
import { formatDate, formatCurrency } from '~/lib/i18n';
export async function loader({ request }: LoaderFunctionArgs) {
const locale = request.headers.get('Accept-Language')?.split(',')[0] || 'en-US';
const data = {
createdAt: formatDate(new Date(), locale),
price: formatCurrency(99.99, 'USD', locale),
};
return json(data);
}Available Functions
Dates
formatDate(new Date());
// Output: "Dec 3, 2025" (en-US)
// Output: "3 déc. 2025" (fr-FR)
// Output: "2025年12月3日" (ja-JP)
formatDateTime(new Date());
// Output: "Dec 3, 2025, 1:05 AM" (en-US)
formatRelativeTime(new Date(Date.now() - 3600000));
// Output: "1 hour ago" (en-US)
// Output: "il y a 1 heure" (fr-FR)Currency
formatCurrency(1234.56, 'USD', 'en-US');
// Output: "$1,234.56"
formatCurrency(1234.56, 'EUR', 'de-DE');
// Output: "1.234,56 €"
formatCurrency(1234.56, 'JPY', 'ja-JP');
// Output: "¥1,235"Numbers
formatNumber(1234567, 'en-US');
// Output: "1,234,567"
formatNumber(1234567, 'de-DE');
// Output: "1.234.567"
formatPercent(0.75, 'en-US', 1);
// Output: "75.0%"
formatFileSize(1536000, 'en-US');
// Output: "1.46 MB"Migration Examples
Before (Hardcoded)
// ❌ Not locale-aware
<p>Created: {new Date(client.createdAt).toLocaleString()}</p>
<p>Price: ${product.price} USD</p>
<p>Users: {organization.memberCount}</p>After (i18n-Ready)
// ✅ Respects user locale
import { useFormat } from '~/lib/i18n';
function Component() {
const { formatDateTime, formatCurrency, formatNumber } = useFormat();
return (
<>
<p>Created: {formatDateTime(client.createdAt)}</p>
<p>Price: {formatCurrency(product.price)}</p>
<p>Users: {formatNumber(organization.memberCount)}</p>
</>
);
}Custom Formatting Options
All functions accept Intl API options:
// Custom date format
formatDate(new Date(), 'en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
});
// Output: "Tuesday, December 3, 2025"
// Custom number format
formatNumber(1234.5678, 'en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
// Output: "1,234.57"Locale Detection Priority
The system detects locale in this order:
- User DB preference (if user is logged in and has set locale)
- Cookie preference (
localecookie) - Browser Accept-Language header
- Default fallback (
en-US)
Future Expansion Path
When you're ready for full translation:
- Install
i18nextorreact-intl - Extract hardcoded strings to translation files
- Keep these formatting utilities (they work alongside translation libraries)
- Add language switcher UI
The current implementation provides a solid foundation without the overhead of managing thousands of translation strings.
Performance Notes
- All formatting uses native
IntlAPI (built into browsers) - Zero external dependencies
- Zero bundle size increase beyond our utility code (~2KB)
- Formatting is fast (microseconds per call)
Browser Support
Intl API is supported in all modern browsers:
- Chrome 24+
- Firefox 29+
- Safari 10+
- Edge (all versions)
For older browsers, formatting gracefully falls back to default.