Internationalization
Language & Formatting

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,234

Moment.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 CodeLanguageRegion
enEnglishDefault
en-gbEnglishUnited Kingdom
frFrenchFrance
fr-caFrenchCanada
deGermanGermany
de-atGermanAustria
de-chGermanSwitzerland
esSpanishSpain/Mexico
itItalianItaly
ptPortuguesePortugal
pt-brPortugueseBrazil
zh-cnChineseSimplified
zh-twChineseTraditional
jaJapaneseJapan
koKoreanSouth Korea
ruRussianRussia
arArabicMiddle East
hiHindiIndia
nlDutchNetherlands
nl-beDutchBelgium
plPolishPoland
trTurkishTurkey
svSwedishSweden
daDanishDenmark
nbNorwegianBokmål
nnNorwegianNynorsk
fiFinnishFinland
csCzechCzech Republic
huHungarianHungary
roRomanianRomania
ukUkrainianUkraine
heHebrewIsrael
thThaiThailand
viVietnameseVietnam
idIndonesianIndonesia
msMalayMalaysia

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:

  1. Go to SettingsCompanyLocale & Currency Settings
  2. Select your preferred locale
  3. Preview how dates, numbers, and currency will display
  4. 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_locale is set correctly on company
  • Check that Moment locale loaded (no console errors)
  • Ensure using useDateFormatter hook

Numbers Have Wrong Separators

  • Check locale setting matches expected format
  • Use useCurrencyFormatter or formatNumber utilities
  • Verify no hardcoded formatting in components

Calendar Shows English Days/Months

  • Moment locale may not be loaded yet
  • Check localeLoaded status 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