Language Support
TimberCloud provides locale-based formatting and regional customization for your global operations. This guide covers how language and locale settings affect your storefront, orders, and customer communications.
Current Language Support
TimberCloud's internationalization is currently focused on locale-based formatting rather than full UI translation. This means:
- Date/Time Display: Formatted according to regional conventions
- Number Formatting: Proper decimal and thousands separators
- Currency Display: Correct symbols, placement, and decimals
- Address Formats: Country-specific field labels and layouts
- Tax Labels: Regional tax terminology (VAT, GST, Sales Tax)
Primary Interface Language
The TimberCloud admin interface is in English. All navigation, buttons, system messages, and help text are displayed in English.
Locale-Aware Formatting
While the interface is in English, all data display adapts to your company's locale settings:
// Example: Same order viewed with different locales
// Company locale: en-US
Order Date: 12/31/2024
Total: $1,234.56
// Company locale: de-DE
Order Date: 31.12.2024
Total: 1.234,56 €
// Company locale: ja-JP
Order Date: 2024/12/31
Total: ¥1,234Moment.js Locale Support
TimberCloud uses Moment.js for date formatting with dynamic locale loading:
Fully Supported Date Locales
These locales have complete Moment.js support for date/time formatting:
| Locale Code | Language | Region |
|---|---|---|
| en | English | Default |
| en-gb | English | United Kingdom |
| fr | French | France |
| fr-ca | French | Canada |
| de | German | Germany |
| de-at | German | Austria |
| de-ch | German | Switzerland |
| es | Spanish | Spain/Mexico |
| it | Italian | Italy |
| pt | Portuguese | Portugal |
| pt-br | Portuguese | Brazil |
| zh-cn | Chinese | Simplified |
| zh-tw | Chinese | Traditional |
| ja | Japanese | Japan |
| ko | Korean | South Korea |
| ru | Russian | Russia |
| ar | Arabic | Middle East |
| hi | Hindi | India |
| nl | Dutch | Netherlands |
| nl-be | Dutch | Belgium |
| pl | Polish | Poland |
| tr | Turkish | Turkey |
| sv | Swedish | Sweden |
| da | Danish | Denmark |
| nb | Norwegian | Bokmål |
| nn | Norwegian | Nynorsk |
| fi | Finnish | Finland |
| cs | Czech | Czech Republic |
| hu | Hungarian | Hungary |
| ro | Romanian | Romania |
| uk | Ukrainian | Ukraine |
| he | Hebrew | Israel |
| th | Thai | Thailand |
| vi | Vietnamese | Vietnam |
| id | Indonesian | Indonesia |
| ms | Malay | Malaysia |
How Locale Loading Works
Locales are loaded dynamically to keep the initial bundle size small:
// English is always available (no loading required)
// Other locales are loaded on-demand when needed
import { useDateFormatter } from '@/hooks/useDateFormatter';
function DateDisplay() {
const { formatDate, localeLoaded } = useDateFormatter();
// localeLoaded will be true once the locale is ready
if (!localeLoaded) {
return <span>Loading...</span>;
}
return <span>{formatDate(new Date(), 'date.long')}</span>;
}Translatable Content
While the system UI is in English, your content can be in any language:
Product Information
Enter product details in your customers' language:
- Product names
- Descriptions
- Category names
- Attribute labels
- Option names
Customer Communications
Customize templates with localized content:
- Order confirmation messages
- Shipping notifications
- Custom email content
- Terms and conditions
- Invoice notes
Storefront Content
For the embedded storefront:
- Welcome messages
- Call-to-action text
- Custom page content
- Help text and instructions
Right-to-Left (RTL) Support
TimberCloud supports Right-to-Left languages for proper text display:
Supported RTL Languages
- Arabic (ar-SA, ar-AE)
- Hebrew (he-IL)
RTL Considerations
- Number formatting respects RTL context
- Currency symbols positioned correctly
- Mixed LTR/RTL content handled properly
// Arabic locale with RTL number formatting
// Locale: ar-SA
// Numbers can display with Eastern Arabic numerals: ١٬٢٣٤٫٥٦Configuration
Setting Default Locale
The company's default locale affects all formatting:
- Go to Settings → Company → Locale & Currency Settings
- Select your preferred locale
- Preview how dates, numbers, and currency will display
- Save changes
API Response Formatting
API responses include both raw and formatted values:
{
"order": {
"id": "12345",
"created_at": "2024-12-31T15:30:00Z",
"total": 123456,
"currency": "USD",
"formatted": {
"created_at": "December 31, 2024",
"total": "$1,234.56"
}
}
}Developer Guide
Formatting Dates
import { useDateFormatter } from '@/hooks/useDateFormatter';
function OrderHistory({ orders }) {
const {
formatDate,
formatRelative,
formatCalendar,
locale
} = useDateFormatter();
return (
<ul>
{orders.map(order => (
<li key={order.id}>
{/* Locale-aware date: "December 31, 2024" or "31. Dezember 2024" */}
<span>{formatDate(order.createdAt, 'date.long')}</span>
{/* Relative time: "2 days ago" */}
<span>{formatRelative(order.updatedAt)}</span>
{/* Calendar: "Yesterday at 3:30 PM" */}
<span>{formatCalendar(order.dueDate)}</span>
</li>
))}
</ul>
);
}Formatting Numbers
import { useCurrencyFormatter } from '@/hooks/useCurrencyFormatter';
function PricingTable({ company, items }) {
const { format, formatNumber } = useCurrencyFormatter(company);
return (
<table>
{items.map(item => (
<tr key={item.id}>
<td>{item.name}</td>
{/* Locale-aware number: "1,234" or "1.234" */}
<td>{formatNumber(item.quantity)}</td>
{/* Locale-aware currency: "$1,234.56" or "1.234,56 €" */}
<td>{format(item.price)}</td>
</tr>
))}
</table>
);
}Getting Localized Labels
import i18n from '@/util/i18n';
function AddressForm({ countryCode }) {
// Get country-specific labels
const postalConfig = i18n.getPostalCodeConfig(countryCode);
const taxLabel = i18n.getTaxLabel(countryCode);
const companyRegLabel = i18n.getCompanyRegLabel(countryCode);
return (
<form>
<label>
{/* "ZIP Code", "Postcode", "Postal Code", etc. */}
{postalConfig.label}
<input
placeholder={postalConfig.placeholder}
maxLength={postalConfig.maxLength}
/>
</label>
<label>
{/* "Sales Tax", "VAT", "GST", etc. */}
{taxLabel} Rate
<input type="number" />
</label>
<label>
{/* "EIN", "Company Number", "ABN", etc. */}
{companyRegLabel}
<input type="text" />
</label>
</form>
);
}Best Practices
1. Use Formatting Hooks
Always use provided hooks for locale-aware formatting:
// ✓ Good: Locale-aware
const { formatDate } = useDateFormatter();
return <span>{formatDate(date, 'date.short')}</span>;
// ✗ Bad: Hardcoded format
return <span>{date.toLocaleDateString('en-US')}</span>;2. Store Content Language-Agnostic
Store raw values, format only for display:
// ✓ Good: Store raw ISO date
order.createdAt = '2024-12-31T15:30:00Z';
// ✗ Bad: Store formatted string
order.createdAt = 'December 31, 2024';3. Consider Text Length
When translating product content, remember that:
- German text is often ~30% longer than English
- Asian languages may be more compact
- Plan for text expansion in UI layouts
4. Test Multiple Locales
Verify your storefront with different locale settings:
- Date formats (MM/DD vs DD/MM)
- Number separators (1,234.56 vs 1.234,56)
- Currency symbol placement ($100 vs 100€)
Future Roadmap
Full UI translation support is planned for future releases:
Planned Features
- Multi-language admin interface
- Storefront language switching
- Translation management system
- Automatic translation integration
- Language-specific email templates
Current Workarounds
For now, you can customize user-facing content:
- Enter product descriptions in your target language
- Customize email templates with localized text
- Use your language for category and attribute names
Troubleshooting
Dates Show Wrong Format
- Verify
default_localeis set correctly on company - Check that Moment locale loaded (no console errors)
- Ensure using
useDateFormatterhook
Numbers Have Wrong Separators
- Check locale setting matches expected format
- Use
useCurrencyFormatterorformatNumberutilities - Verify no hardcoded formatting in components
Calendar Shows English Days/Months
- Moment locale may not be loaded yet
- Check
localeLoadedstatus before rendering - English is the fallback for unavailable locales
RTL Text Not Displaying Correctly
- Verify HTML
dir="rtl"attribute where needed - Check CSS for RTL-aware properties
- Test with actual RTL content, not placeholder text