Onboarding
- ๐ | I18n Knowledge Center
- I18n Challenges and Best Practices
- ๐ | Regional Time Zone Systems
- ๐ฐ | Number & Currency Formatting
- ๐ | Date & Time Formatting
- ๐ท๏ธ | Language & Region Identifiers
- ๐ | Start Day of the Week
- ๐ | Conclusion & Resources
๐ | I18n Knowledge Center
๐ Welcome from the L10n Team
Hello and welcome! We're the Localization (L10n) and Internationalization (i18n) Team, your partners in making our products accessible to users around the globe. Our mission is to ensure that every user, regardless of their language or location, has an exceptional experience with our software.
As Product Managers and L10n specialists, we bridge the gap between development and the diverse needs of our international users. We're here to guide you through the complexities of building software that truly speaks to users in their own language, respects their cultural conventions, and feels native wherever it's used.
๐ What You'll Learn
This knowledge center is designed to equip you with the essential understanding and practical skills needed to develop internationally-aware applications. Whether you're new to i18n or looking to deepen your expertise, you'll find comprehensive guidance here.
๐ Regional Time Zone Systems
Understand time zones, UTC offsets, daylight saving time, and how to handle temporal data across regions.
๐ฐ Number & Currency Formatting
Master decimal separators, thousand groupings, currency symbols, and locale-specific number conventions.
๐ Date & Time Formatting
Learn about diverse date formats, time representations, and patterns used across different cultures.
๐ท๏ธ Language & Region Identifiers
Deep dive into locale codes, BCP 47 tags, IETF language tags, and proper identifier usage.
๐ Start Day of the Week
Explore calendar conventions and how different cultures define the beginning of the week.
๐ฏ Why Internationalization Matters
๐ก The Impact of Poor i18n
- User Trust: Incorrectly formatted dates or currency can erode user confidence
- Functionality: Time zone bugs can cause missed meetings and incorrect schedules
- Market Reach: Poor localization limits your product's global adoption
- Technical Debt: Retrofitting i18n is 10x harder than building it in from the start
โจ The Benefits of Good i18n
- Global Scale: Seamlessly expand to new markets without code rewrites
- User Experience: Users feel at home with properly localized interfaces
- Compliance: Meet regional legal and cultural requirements
- Competitive Edge: Stand out with truly native-feeling experiences
๐ How to Use This Knowledge Center
- Follow the Journey: We recommend reading the topics in sequence, as concepts build upon each other
- Interactive Examples: Each page includes code samples and interactive demonstrations
- Practical Application: Try the examples in your own development environment
- Reference Guide: Bookmark pages for quick reference when implementing features
- Stay Updated: This is a living documentโcheck back for updates and new best practices
๐ค Common i18n Challenges We'll Address
Challenge | Impact | Solution Coverage |
|---|---|---|
String Concatenation | Breaks translation, ignores grammar rules | โ Throughout guide |
Hard-coded Formats | Forces single format on all users | โ Topics 2-5 |
Locale Detection | Shows wrong language/format to users | โ Topic 4 |
UTF-8 Handling | Corrupts non-Latin text | โ Best practices |
Right-to-Left (RTL) | Broken UI for Arabic, Hebrew users | โ Conclusion |
๐ Let's Get Started!
You're about to embark on a journey that will transform how you think about software development. Internationalization isn't just a technical requirementโit's about respecting and serving our diverse global user base.
Ready to dive in? Let's start with Regional Time Zone Systems and build your i18n expertise step by step.
๐ง Questions or Feedback? The L10n team is here to help! Reach out anytime as you work through these materials.
I18n Challenges and Best Practices
๐ | Regional Time Zone Systems
๐ฏ Learning Objectives
- Understand the fundamentals of time zones and UTC
- Handle Daylight Saving Time (DST) transitions correctly
- Store and transmit temporal data properly
- Display times appropriately for user's local context
- Avoid common pitfalls in time zone handling
๐ The Fundamentals
What is UTC?
Coordinated Universal Time (UTC) is the primary time standard by which the world regulates clocks and time. It is not affected by Daylight Saving Time and serves as the foundation for all time zone calculations.
Key Concepts
Term | Definition | Example |
|---|---|---|
UTC Offset | Time difference from UTC | EST: UTC-5, JST: UTC+9 |
Time Zone | Region with uniform standard time | America/New_York, Europe/London |
DST | Seasonal clock adjustment | Spring forward, Fall back |
IANA TZ Database | Authoritative time zone data | tzdata, Olson database |
Time Zone Identifiers
Always use IANA time zone identifiers (e.g., America/New_York, Asia/Tokyo) rather than abbreviations like EST or PST. Abbreviations are ambiguous and don't account for DST.
โ ๏ธ Common Mistake: Using Abbreviations
โ Don't Do This:
// Ambiguous - which CST?
// Central Standard Time (US)?
// China Standard Time?
// Cuba Standard Time?
timezone = "CST"
โ Do This Instead:
// Unambiguous IANA identifier
timezone = "America/Chicago"
// or
timezone = "Asia/Shanghai"
// or
timezone = "America/Havana"
๐ป Implementation Guidelines
The Golden Rule: Store in UTC, Display in Local
โ Best Practice Pattern
- Storage: Always store timestamps in UTC (ISO 8601 format recommended)
- Transmission: Send timestamps in UTC between services
- Display: Convert to user's local time zone only for presentation
- Input: Accept user input in local time, immediately convert to UTC
Code Examples
JavaScript Example
// Store: Always use UTC
const eventTime = new Date().toISOString();
// โ "2025-11-05T14:30:00.000Z"
// Database storage (example)
await db.events.insert({
title: "Team Meeting",
startTime: eventTime, // UTC timestamp
timezone: "America/Los_Angeles" // Store user's timezone separately
});
// Display: Convert to user's local time
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
});
console.log(formatter.format(new Date(eventTime)));
// โ "November 5, 2025, 06:30 AM PST"
Python Example
from datetime import datetime, timezone
import pytz
# Store: Always use UTC
event_time = datetime.now(timezone.utc)
# Store as ISO 8601: event_time.isoformat()
# Display: Convert to user's timezone
user_tz = pytz.timezone('Europe/London')
local_time = event_time.astimezone(user_tz)
print(local_time.strftime('%Y-%m-%d %H:%M:%S %Z'))
# โ "2025-11-05 14:30:00 GMT"
# During BST (British Summer Time):
# โ "2025-06-15 15:30:00 BST"
Java Example
import java.time.*;
import java.time.format.DateTimeFormatter;
// Store: Always use UTC
Instant eventTime = Instant.now();
// Store in database as: eventTime.toString()
// โ "2025-11-05T14:30:00Z"
// Display: Convert to user's timezone
ZoneId userZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime localTime = eventTime.atZone(userZone);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss z"
);
System.out.println(localTime.format(formatter));
// โ "2025-11-05 23:30:00 JST"
โก Handling Daylight Saving Time
DST transitions create ambiguous and non-existent times. Your code must handle these edge cases gracefully.
Spring Forward (Non-existent Hour)
When clocks "spring forward," one hour doesn't exist. For example, on March 10, 2024, in the US, 2:00 AM โ 3:00 AM instantly.
Problem: 2:30 AM doesn't exist on that day!
Fall Back (Ambiguous Hour)
When clocks "fall back," one hour occurs twice. On November 3, 2024, 2:00 AM โ 1:00 AM, repeating the 1 AM hour.
Problem: 1:30 AM happens twice!
โ Solution: Let Libraries Handle It
Modern date/time libraries (like Intl in JavaScript, pytz in Python, java.time in Java) automatically handle DST transitions. Never implement your own DST logic!
// JavaScript example: Library handles DST automatically
const date1 = new Date('2024-03-10T02:30:00'); // During "spring forward"
const date2 = new Date('2024-11-03T01:30:00'); // During "fall back"
// The Intl API handles these transitions correctly
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
});
// Results are handled properly by the system
๐ Real-World Scenarios
Scenario 1: Scheduling a Meeting Across Time Zones
Situation: A user in New York schedules a meeting for 3:00 PM their time, inviting participants in London and Tokyo.
Location | Time Zone | Display Time |
|---|---|---|
New York | America/New_York | 3:00 PM EST |
London | Europe/London | 8:00 PM GMT |
Tokyo | Asia/Tokyo | 5:00 AM JST (next day) |
Stored Value | 2025-11-05T20:00:00Z (UTC) | |
Scenario 2: Recurring Events During DST Transition
Situation: A weekly meeting scheduled for "every Monday at 9:00 AM local time" needs to maintain the local time even when DST changes.
Solution: Store the time zone identifier along with the local time, not just a UTC offset. This allows the system to recalculate the correct UTC time after DST transitions.
{
"eventTitle": "Weekly Standup",
"recurrence": "weekly",
"dayOfWeek": "Monday",
"localTime": "09:00",
"timeZone": "America/Los_Angeles", // Critical: Store TZ, not offset!
"duration": 30
}
// Before DST (PST, UTC-8): 9:00 AM = 17:00 UTC
// After DST (PDT, UTC-7): 9:00 AM = 16:00 UTC
// Users still see 9:00 AM local time โ
๐ฏ Best Practices Checklist
Practice | Priority |
|---|---|
โ Always store timestamps in UTC | CRITICAL |
โ Use IANA time zone identifiers (America/New_York, not EST) | CRITICAL |
โ Use ISO 8601 format for date/time strings | HIGH |
โ Let standard libraries handle DST transitions | CRITICAL |
โ Display times in user's local time zone with TZ name | HIGH |
โ Test with edge cases (DST transitions, year boundaries) | HIGH |
โ Allow users to explicitly set their time zone preference | MEDIUM |
โ Keep time zone database updated (IANA releases) | HIGH |
๐ Additional Resources
- IANA Time Zone Database: www.iana.org/time-zones
- ISO 8601: International date/time standard
- moment-timezone (JavaScript): Comprehensive time zone library
- pytz (Python): World timezone definitions for Python
- java.time (Java 8+): Modern date-time API
Next Topic: Number & Currency Formatting โ
๐ฐ | Number & Currency Formatting
๐ฏ Learning Objectives
- Understand global variations in number formatting
- Handle decimal separators and digit grouping correctly
- Format currency with proper symbols and placement
- Work with different numbering systems
- Implement locale-aware number parsing and validation
๐ The Global Number Challenge
The same number can look completely different around the world. What appears as 1,234.56 in the US becomes 1.234,56 in Germany and 1 234,56 in France.
Same Number, Different Representations
Locale | Number Format | Currency Format |
|---|---|---|
English (US) |
|
|
German (Germany) |
|
|
French (France) |
|
|
Hindi (India) |
|
|
Arabic (Saudi Arabia) |
|
|
Japanese (Japan) |
|
(no decimals) |
Swiss (German) |
|
|
โ ๏ธ Critical Insight
Never assume a specific number format! Hard-coding formats like "1,234.56" will confuse or alienate users from other regions. Always use locale-aware formatting functions.
๐ค Key Formatting Elements
Decimal Separator
The character that separates the integer part from the fractional part.
- Period (.) โ US, UK, China, Japan
- Comma (,) โ Most of Europe, Latin America
- Arabic decimal (ูซ) โ Middle East (Arabic)
Group Separator
The character that groups digits for readability (typically thousands).
- Comma (,) โ US, UK, China, Japan
- Period (.) โ Germany, Italy, Netherlands
- Space ( ) โ France, Sweden, Czech
- Apostrophe (') โ Switzerland
Grouping Size
How many digits appear in each group.
- 3 digits โ Most of the world: 1,234,567
- Indian system โ After first 3, then 2: 12,34,567
- Chinese โ Groups of 4: 1234,5678
Digit Symbols
Not all locales use Western Arabic numerals (0-9).
- Western: 0 1 2 3 4 5 6 7 8 9
- Arabic-Indic: ู ูก ูข ูฃ ูค ูฅ ูฆ ูง ูจ ูฉ
- Devanagari: เฅฆ เฅง เฅจ เฅฉ เฅช เฅซ เฅฌ เฅญ เฅฎ เฅฏ
๐ป Implementation Guidelines
Number Formatting
JavaScript Example
const number = 1234567.89;
// US English
const usFormatter = new Intl.NumberFormat('en-US');
console.log(usFormatter.format(number));
// โ "1,234,567.89"
// German
const deFormatter = new Intl.NumberFormat('de-DE');
console.log(deFormatter.format(number));
// โ "1.234.567,89"
// French
const frFormatter = new Intl.NumberFormat('fr-FR');
console.log(frFormatter.format(number));
// โ "1 234 567,89"
// Arabic (Saudi Arabia) - with Arabic-Indic digits
const arFormatter = new Intl.NumberFormat('ar-SA');
console.log(arFormatter.format(number));
// โ "ูกูฌูขูฃูคูฌูฅูฆูงูซูจูฉ"
// With options: 2 decimal places, minimum integer digits
const formatted = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
minimumIntegerDigits: 1
}).format(42);
console.log(formatted); // โ "42.00"
Python Example
import locale
from babel.numbers import format_decimal, format_currency
number = 1234567.89
# Using locale module (system-dependent)
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
formatted = locale.format_string('%.2f', number, grouping=True)
print(formatted) # โ "1,234,567.89"
# Better: Using Babel (recommended)
# US English
us_format = format_decimal(number, locale='en_US')
print(us_format) # โ "1,234,567.89"
# German
de_format = format_decimal(number, locale='de_DE')
print(de_format) # โ "1.234.567,89"
# French
fr_format = format_decimal(number, locale='fr_FR')
print(fr_format) # โ "1 234 567,89"
# With custom decimal places
precise = format_decimal(42, locale='en_US', decimal_quantization=False)
print(f"{precise:.2f}") # โ "42.00"
Java Example
import java.text.NumberFormat;
import java.util.Locale;
double number = 1234567.89;
// US English
NumberFormat usFormat = NumberFormat.getInstance(Locale.US);
System.out.println(usFormat.format(number));
// โ "1,234,567.89"
// German
NumberFormat deFormat = NumberFormat.getInstance(Locale.GERMANY);
System.out.println(deFormat.format(number));
// โ "1.234.567,89"
// French
NumberFormat frFormat = NumberFormat.getInstance(Locale.FRANCE);
System.out.println(frFormat.format(number));
// โ "1 234 567,89"
// With custom decimal places
NumberFormat customFormat = NumberFormat.getInstance(Locale.US);
customFormat.setMinimumFractionDigits(2);
customFormat.setMaximumFractionDigits(2);
System.out.println(customFormat.format(42));
// โ "42.00"
๐ต Currency Formatting
Currency formatting is more complex than simple numbers. You must consider symbol placement, spacing, negative formatting, and currency-specific decimal places.
Currency Format Variations
Locale | Positive Amount | Negative Amount | Notes |
|---|---|---|---|
en-US |
|
| Symbol before, no space |
de-DE |
|
| Symbol after, with space |
fr-FR |
|
| Space group separator |
ja-JP |
|
| No decimal places for JPY |
ar-SA |
|
| Arabic digits, RTL |
en-GB |
|
| Symbol before |
Currency Formatting Code Examples
JavaScript Example
const amount = 1234.56;
// US Dollar
const usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(usdFormatter.format(amount));
// โ "$1,234.56"
// Euro in Germany
const eurDeFormatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(eurDeFormatter.format(amount));
// โ "1.234,56 โฌ"
// Japanese Yen (no decimal places)
const jpyFormatter = new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency: 'JPY'
});
console.log(jpyFormatter.format(amount));
// โ "ยฅ1,235" (automatically rounded)
// Display currency code instead of symbol
const codeFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'code'
});
console.log(codeFormatter.format(amount));
// โ "USD 1,234.56"
// Accounting format (parentheses for negative)
const accountingFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencySign: 'accounting'
});
console.log(accountingFormatter.format(-amount));
// โ "($1,234.56)"
Python Example
from babel.numbers import format_currency
amount = 1234.56
# US Dollar
usd_format = format_currency(amount, 'USD', locale='en_US')
print(usd_format) # โ "$1,234.56"
# Euro in Germany
eur_de_format = format_currency(amount, 'EUR', locale='de_DE')
print(eur_de_format) # โ "1.234,56 โฌ"
# Japanese Yen (automatically handles no decimals)
jpy_format = format_currency(amount, 'JPY', locale='ja_JP')
print(jpy_format) # โ "๏ฟฅ1,235"
# British Pound
gbp_format = format_currency(amount, 'GBP', locale='en_GB')
print(gbp_format) # โ "ยฃ1,234.56"
# Display currency code
code_format = format_currency(
amount, 'USD', locale='en_US',
format_type='name'
)
print(code_format) # โ "1,234.56 US dollars"
โ Pro Tip: Currency-Specific Decimal Places
Not all currencies use 2 decimal places! The formatting library automatically handles this:
- 0 decimals: JPY (ยฅ), KRW (โฉ), VND (โซ)
- 2 decimals: USD ($), EUR (โฌ), GBP (ยฃ) โ Most common
- 3 decimals: BHD (BD), JOD (JD), KWD (KD), OMR (OR), TND (TD)
Always use locale-aware formatting libraries to handle these variations automatically!
โ ๏ธ Common Pitfalls & Solutions
โ Pitfall 1: String Concatenation for Currency
Wrong:
// Hard-coded format
const price = "$" + amount.toFixed(2);
// Result: "$1234.56"
// Problems: No thousands separator,
// wrong for most countries
Right:
// Locale-aware formatting
const price = new Intl.NumberFormat(
userLocale, {
style: 'currency',
currency: userCurrency
}).format(amount);
โ Pitfall 2: Parsing User Input Without Locale
When users enter "1.234,56", parseFloat() will fail or give wrong results. You need locale-aware parsing.
// JavaScript: Use a library like numbro or parser
import { parse } from 'numbro';
const germanInput = "1.234,56";
const parsed = parse(germanInput, { locale: 'de-DE' });
console.log(parsed); // โ 1234.56
// Python: Use locale or babel
from babel.numbers import parse_decimal
german_input = "1.234,56"
parsed = parse_decimal(german_input, locale='de_DE')
print(parsed) # โ Decimal('1234.56')
โ Pitfall 3: Storing Formatted Strings in Database
Never store formatted numbers or currency in your database!
Store: Raw numeric values (preferably as integers for currency)
// Store cents/pence to avoid floating-point issues
const priceInCents = 123456; // $1,234.56
// Or use Decimal type in your database
Format: Only when displaying to users
const displayPrice = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(priceInCents / 100);
๐ฏ Best Practices Checklist
Practice | Priority |
|---|---|
โ Use locale-aware formatting libraries (Intl, Babel, ICU) | CRITICAL |
โ Store raw numeric values, format only for display | CRITICAL |
โ Use integers for currency (cents/pence) to avoid float errors | HIGH |
โ Always specify currency code (USD, EUR) when formatting | CRITICAL |
โ Use locale-aware parsing for user input | HIGH |
โ Test with multiple locales (US, Germany, Japan, Arabic) | HIGH |
โ Handle right-to-left (RTL) for Arabic/Hebrew currencies | MEDIUM |
โ Provide clear error messages for invalid number input | MEDIUM |
๐ Additional Resources
- Unicode CLDR: Common Locale Data Repository for number patterns
- ISO 4217: Currency code standard
- Intl.NumberFormat (JavaScript): MDN documentation
- Babel (Python): Comprehensive i18n library
- ICU (C/C++/Java): International Components for Unicode
Next Topic: Date & Time Formatting โ
๐ | Date & Time Formatting
๐ฏ Learning Objectives
- Understand date and time format variations across cultures
- Master date/time pattern strings and placeholders
- Handle 12-hour vs 24-hour time formats
- Format dates correctly for user's locale
- Avoid ambiguous date representations
๐ The Date Format Challenge
The date "03/04/05" could mean:
- ๐บ๐ธ March 4, 2005 (US: MM/DD/YY)
- ๐ฌ๐ง April 3, 2005 (UK: DD/MM/YY)
- ๐ฏ๐ต May 3, 2004 (Japan: YY/MM/DD)
- And many more interpretations!
โ ๏ธ Critical Insight
There is no universal date format that works everywhere. A date written as 12/01/2025 is ambiguous and will confuse international users. Always format dates according to the user's locale or use an unambiguous format like ISO 8601.
Common Date Format Patterns
Region/Locale | Short Format | Long Format | Pattern |
|---|---|---|---|
US (en-US) |
| November 5, 2025 | M/D/YYYY |
UK (en-GB) |
| 5 November 2025 | D/M/YYYY |
Japan (ja-JP) |
| 2025ๅนด11ๆ5ๆฅ | YYYY/M/D |
Germany (de-DE) |
| 5. November 2025 | D.M.YYYY |
China (zh-CN) |
| 2025ๅนด11ๆ5ๆฅ | YYYY/M/D |
Korea (ko-KR) |
| 2025๋ 11์ 5์ผ | YYYY. M. D. |
ISO 8601 |
| 2025-11-05 | YYYY-MM-DD |
๐ Time Format Variations
Time formatting varies primarily between 12-hour (with AM/PM) and 24-hour formats, but there are also differences in separators and period markers.
Time Format Examples (2:30 PM)
Region/Locale | Short Time | Long Time | Format Type |
|---|---|---|---|
US (en-US) |
|
| 12-hour |
UK (en-GB) |
|
| 24-hour |
Germany (de-DE) |
|
| 24-hour |
Japan (ja-JP) |
|
| 24-hour |
India (hi-IN) |
|
| 12-hour |
France (fr-FR) |
|
| 24-hour |
12-Hour Format Regions
- United States
- Canada (English)
- Australia
- Philippines
- India
- Pakistan
24-Hour Format Regions
- Most of Europe
- Latin America
- Asia (China, Japan, Korea)
- Middle East
- Africa
๐ป Implementation Guidelines
JavaScript Examples
Date Formatting
const date = new Date('2025-11-05T14:30:00Z');
// US Format - Short
const usShort = new Intl.DateTimeFormat('en-US').format(date);
console.log(usShort);
// โ "11/5/2025"
// US Format - Long
const usLong = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
console.log(usLong);
// โ "November 5, 2025"
// UK Format
const ukFormat = new Intl.DateTimeFormat('en-GB', {
year: 'numeric',
month: 'short',
day: 'numeric'
}).format(date);
console.log(ukFormat);
// โ "5 Nov 2025"
// German Format
const deFormat = new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
console.log(deFormat);
// โ "5. November 2025"
// Japanese Format
const jpFormat = new Intl.DateTimeFormat('ja-JP', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
console.log(jpFormat);
// โ "2025ๅนด11ๆ5ๆฅ"
// ISO 8601 (unambiguous, good for APIs)
console.log(date.toISOString());
// โ "2025-11-05T14:30:00.000Z"
Time Formatting
const date = new Date('2025-11-05T14:30:00Z');
// US - 12-hour format with AM/PM
const usTime = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short',
timeZone: 'America/New_York'
}).format(date);
console.log(usTime);
// โ "9:30 AM EST"
// UK - 24-hour format
const ukTime = new Intl.DateTimeFormat('en-GB', {
hour: '2-digit',
minute: '2-digit',
timeZone: 'Europe/London'
}).format(date);
console.log(ukTime);
// โ "14:30"
// With seconds
const timeWithSeconds = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true
}).format(date);
console.log(timeWithSeconds);
// โ "9:30:00 AM"
// Force 24-hour format
const force24h = new Intl.DateTimeFormat('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: false
}).format(date);
console.log(force24h);
// โ "09:30"
Combined Date & Time Formatting
const date = new Date('2025-11-05T14:30:00Z');
// Full date and time - US
const usFull = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short',
timeZone: 'America/Los_Angeles'
}).format(date);
console.log(usFull);
// โ "November 5, 2025 at 6:30 AM PST"
// Full date and time - German
const deFull = new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Europe/Berlin'
}).format(date);
console.log(deFull);
// โ "5. November 2025, 15:30"
// Relative time formatting (modern browsers)
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day')); // โ "yesterday"
console.log(rtf.format(2, 'week')); // โ "in 2 weeks"
console.log(rtf.format(-3, 'month')); // โ "3 months ago"
Python Examples
Using Babel for Date/Time Formatting
from datetime import datetime
from babel.dates import format_date, format_time, format_datetime
import pytz
dt = datetime(2025, 11, 5, 14, 30, 0, tzinfo=pytz.UTC)
# Date formatting
us_date = format_date(dt, format='long', locale='en_US')
print(us_date) # โ "November 5, 2025"
uk_date = format_date(dt, format='long', locale='en_GB')
print(uk_date) # โ "5 November 2025"
de_date = format_date(dt, format='long', locale='de_DE')
print(de_date) # โ "5. November 2025"
jp_date = format_date(dt, format='long', locale='ja_JP')
print(jp_date) # โ "2025ๅนด11ๆ5ๆฅ"
# Time formatting
us_time = format_time(dt, format='short', locale='en_US')
print(us_time) # โ "2:30 PM"
uk_time = format_time(dt, format='short', locale='en_GB')
print(uk_time) # โ "14:30"
# Combined date and time
us_full = format_datetime(dt, format='full', locale='en_US')
print(us_full)
# โ "Wednesday, November 5, 2025 at 2:30:00 PM GMT"
# Custom format patterns
custom = format_datetime(dt, "EEE, MMM d, yyyy 'at' h:mm a", locale='en_US')
print(custom) # โ "Wed, Nov 5, 2025 at 2:30 PM"
Java Examples
Using java.time for Date/Time Formatting
import java.time.*;
import java.time.format.*;
import java.util.Locale;
ZonedDateTime dt = ZonedDateTime.of(
2025, 11, 5, 14, 30, 0, 0,
ZoneId.of("UTC")
);
// US Format
DateTimeFormatter usFormatter = DateTimeFormatter.ofLocalizedDate(
FormatStyle.LONG
).withLocale(Locale.US);
System.out.println(dt.format(usFormatter));
// โ "November 5, 2025"
// German Format
DateTimeFormatter deFormatter = DateTimeFormatter.ofLocalizedDate(
FormatStyle.LONG
).withLocale(Locale.GERMANY);
System.out.println(dt.format(deFormatter));
// โ "5. November 2025"
// Time with US 12-hour format
DateTimeFormatter usTimeFormatter = DateTimeFormatter.ofLocalizedTime(
FormatStyle.SHORT
).withLocale(Locale.US);
System.out.println(dt.format(usTimeFormatter));
// โ "2:30 PM"
// Custom pattern
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern(
"EEE, MMM d, yyyy 'at' h:mm a",
Locale.US
);
System.out.println(dt.format(customFormatter));
// โ "Wed, Nov 5, 2025 at 2:30 PM"
๐ Common Date/Time Format Patterns
When you need custom formats, most libraries use similar pattern strings based on Unicode LDML.
Pattern Reference
Symbol | Meaning | Example |
|---|---|---|
| Year | 2025 |
| Month (numeric) | 11 / 11 |
| Month (text) | Nov / November |
| Day of month | 5 / 05 |
| Day of week | Wed / Wednesday |
| Hour (12-hour) | 2 / 02 |
| Hour (24-hour) | 14 / 14 |
| Minute | 30 / 30 |
| Second | 0 / 00 |
| AM/PM marker | PM |
| Time zone | PST / Pacific Standard Time |
Example: "MMM d, yyyy 'at' h:mm a" โ "Nov 5, 2025 at 2:30 PM"
โ ๏ธ Common Pitfalls & Solutions
โ Pitfall 1: Ambiguous Numeric Dates
Problem: Dates like 03/04/2025 are ambiguous.
โ
Solution: Use long format with month names ("March 4, 2025" or "4 March 2025") or ISO 8601 (2025-03-04) for unambiguous display.
โ Pitfall 2: Hard-coded Date Formats
// โ Wrong: Hard-coded format
const dateStr = `${month}/${day}/${year}`; // US-only!
// โ
Right: Locale-aware formatting
const dateStr = new Intl.DateTimeFormat(userLocale, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
}).format(date);
โ Pitfall 3: Ignoring User's Time Zone
Problem: Displaying "Meeting at 3:00 PM" without specifying the time zone confuses remote users.
โ
Solution: Always include the time zone name when displaying times: "3:00 PM EST" or convert to user's local time.
๐ฏ Best Practices Checklist
Practice | Priority |
|---|---|
โ Use locale-aware date/time formatting libraries | CRITICAL |
โ Store dates in UTC (see Time Zones topic) | CRITICAL |
โ Use month names or ISO 8601 to avoid ambiguity | HIGH |
โ Always display time zone when showing times | HIGH |
โ Respect user's 12-hour vs 24-hour preference | MEDIUM |
โ Test with multiple locales (US, UK, German, Japanese) | HIGH |
โ Use relative times where appropriate ("2 hours ago") | MEDIUM |
โ Provide date pickers that respect locale formats | HIGH |
๐ Additional Resources
- ISO 8601: International date/time standard
- Unicode LDML: Locale Data Markup Language for date patterns
- Intl.DateTimeFormat (JavaScript): MDN documentation
- Babel (Python): Date and time formatting
- java.time (Java): Modern date-time API
- Moment.js / Luxon: Popular JavaScript date libraries
Next Topic: Language & Region Identifiers โ
๐ท๏ธ | Language & Region Identifiers
๐ฏ Learning Objectives
- Understand locale identifiers and their structure
- Master BCP 47 language tags and IETF standards
- Distinguish between language, region, and script
- Implement proper locale detection and fallback
- Handle special cases and edge scenarios
๐ What is a Locale?
A locale is a set of parameters that defines the user's language, region, and cultural preferences. It determines how your application formats numbers, dates, currency, and displays text.
Locale Components
Language
The primary language being used (e.g., English, Spanish, Japanese)
en, es, ja, zh
Region/Territory
The country or region affecting formats and conventions
US, GB, CN, BR
Script (Optional)
The writing system used for the language
Latn, Cyrl, Arab, Hans
Variant (Rare)
Specific dialectal or orthographic variations
valencia, pinyin
๐ค BCP 47 Language Tags
BCP 47 (Best Current Practice 47) is the IETF standard for language tags. It defines how to construct identifiers that specify language, region, script, and variants.
BCP 47 Tag Structure
language-Script-REGION-variant
Example: zh-Hans-CN = Chinese (Simplified script) as used in China
Component | Format | Standard | Example |
|---|---|---|---|
Language | 2-3 lowercase letters | ISO 639 |
|
Script | 4 letters, title case | ISO 15924 |
|
Region | 2 uppercase letters or 3 digits | ISO 3166-1 |
|
Variant | 5-8 alphanumeric | IANA registry |
|
Common BCP 47 Examples
BCP 47 Tag | Description | Use Case |
|---|---|---|
| English (United States) | MM/DD/YYYY, $ before amount |
| English (United Kingdom) | DD/MM/YYYY, ยฃ before amount |
| Spanish (Spain) | Uses โฌ and European Spanish |
| Spanish (Mexico) | Uses $ and Mexican Spanish |
| Chinese (Simplified, China) | Simplified characters, Mainland |
| Chinese (Traditional, Taiwan) | Traditional characters, Taiwan |
| Arabic (Saudi Arabia) | RTL, Arabic numerals, Saudi riyal |
| Portuguese (Brazil) | Brazilian Portuguese, R$ currency |
| Portuguese (Portugal) | European Portuguese, โฌ currency |
| French (Canada) | Canadian French, $ currency |
โ Why Both Language AND Region Matter
Same language, different formats:
en-USvsen-GB: "color" vs "colour", $ vs ยฃ, MM/DD vs DD/MMes-ESvses-MX: โฌ vs $, "ordenador" vs "computadora"fr-FRvsfr-CA: โฌ vs $, some vocabulary differencespt-BRvspt-PT: Significant spelling and vocabulary differences
๐ Locale Detection & Fallback
How do you determine a user's locale? There are multiple strategies, and you should use them in order of priority.
Locale Detection Priority
- User Preference (Explicit Setting): Highest priority โ user has explicitly selected their locale in settings
- URL Parameter:
?lang=en-GBor/en-gb/pageโ useful for switching without login - Cookie/Session: Previously saved preference from this device
- Browser Accept-Language Header:
Accept-Language: en-US,en;q=0.9,es;q=0.8 - IP Geolocation: Infer from user's location (least reliable, privacy concerns)
- Default Fallback: Your application's default locale (usually
en-US)
Locale Detection Code Examples
JavaScript (Browser)
// Get browser's locale preferences
const userLocales = navigator.languages || [navigator.language];
console.log(userLocales);
// โ ["en-US", "en", "es"]
// Get primary locale
const primaryLocale = navigator.language;
console.log(primaryLocale);
// โ "en-US"
// Detect and use best available locale
function getBestLocale(supportedLocales, userPreferences) {
// Try exact match first
for (const userLocale of userPreferences) {
if (supportedLocales.includes(userLocale)) {
return userLocale;
}
}
// Try language-only match (en-GB โ en-US)
for (const userLocale of userPreferences) {
const language = userLocale.split('-')[0];
const match = supportedLocales.find(l => l.startsWith(language));
if (match) return match;
}
// Fallback to default
return supportedLocales[0];
}
const supported = ['en-US', 'es-ES', 'fr-FR', 'de-DE'];
const userPrefs = ['en-GB', 'en', 'es'];
const bestLocale = getBestLocale(supported, userPrefs);
console.log(bestLocale); // โ "en-US" (language match)
Python (Server-side)
from flask import request
from babel import Locale, negotiate_locale
# Supported locales in your application
SUPPORTED_LOCALES = ['en_US', 'es_ES', 'fr_FR', 'de_DE', 'ja_JP']
DEFAULT_LOCALE = 'en_US'
def get_user_locale():
# 1. Check user's explicit preference (from database/session)
user_pref = session.get('locale')
if user_pref and user_pref in SUPPORTED_LOCALES:
return user_pref
# 2. Check URL parameter
url_locale = request.args.get('lang')
if url_locale and url_locale in SUPPORTED_LOCALES:
return url_locale
# 3. Negotiate from Accept-Language header
header_locales = request.accept_languages
best_match = negotiate_locale(
[str(l) for l in header_locales],
SUPPORTED_LOCALES,
sep='_'
)
if best_match:
return best_match
# 4. Fallback to default
return DEFAULT_LOCALE
# Usage
locale = get_user_locale()
print(f"Using locale: {locale}")
โ ๏ธ Locale Fallback Chain
Always implement a fallback chain. If you don't have zh-Hant-HK (Traditional Chinese, Hong Kong), try falling back to:
zh-Hant-HK(exact match) โ not availablezh-Hant(language + script) โ try thiszh(language only) โ then thisen(default language) โ final fallback
๐ป Working with Locales in Code
JavaScript - Parsing and Validating Locales
// Check if locale is valid
function isValidLocale(locale) {
try {
Intl.NumberFormat(locale);
return true;
} catch (e) {
return false;
}
}
console.log(isValidLocale('en-US')); // โ true
console.log(isValidLocale('invalid')); // โ false
// Get canonical locale
const canonical = Intl.getCanonicalLocales('EN-us')[0];
console.log(canonical); // โ "en-US" (normalized)
// Parse locale components
function parseLocale(tag) {
const parts = tag.split('-');
return {
language: parts[0]?.toLowerCase(),
script: parts[1]?.length === 4 ? parts[1] : undefined,
region: parts.find(p => p.length === 2)?.toUpperCase(),
};
}
console.log(parseLocale('zh-Hans-CN'));
// โ { language: 'zh', script: 'Hans', region: 'CN' }
// Using Intl.Locale (modern browsers)
const locale = new Intl.Locale('zh-Hans-CN');
console.log(locale.language); // โ "zh"
console.log(locale.script); // โ "Hans"
console.log(locale.region); // โ "CN"
console.log(locale.baseName); // โ "zh-Hans-CN"
Python - Working with Babel Locales
from babel import Locale, UnknownLocaleError
# Parse locale
try:
locale = Locale.parse('zh_Hans_CN', sep='_')
print(f"Language: {locale.language}") # โ zh
print(f"Script: {locale.script}") # โ Hans
print(f"Territory: {locale.territory}") # โ CN
print(f"Display name: {locale.display_name}") # โ Chinese (Simplified, China)
except UnknownLocaleError:
print("Invalid locale")
# Get English name for locale
locale = Locale.parse('fr_CA')
print(locale.get_display_name('en')) # โ "French (Canada)"
print(locale.get_display_name('fr')) # โ "franรงais (Canada)"
# List all available locales
from babel.localedata import list as list_locales
all_locales = list_locales()
print(f"Available locales: {len(all_locales)}")
# โ Available locales: 700+
# Locale negotiation
from babel import negotiate_locale
supported = ['en_US', 'es_ES', 'fr_FR']
user_prefs = ['de_DE', 'en_GB', 'en']
best = negotiate_locale(user_prefs, supported, sep='_')
print(best) # โ "en_US" (language fallback from en)
๐ Special Cases & Edge Scenarios
๐ค Script Matters for Some Languages
Chinese has two writing systems:
zh-Hansโ Simplified Chinese (Mainland China, Singapore)zh-Hantโ Traditional Chinese (Taiwan, Hong Kong, Macau)
Using just zh is ambiguous and can lead to displaying the wrong script!
๐ Language Without Region
Sometimes you have only en without a region. What should you do?
- Option 1: Use a sensible default (e.g.,
enโen-US) - Option 2: Use language-only formatting (may not be culturally appropriate)
- Option 3: Detect region from IP/browser and complete the locale
๐ Format Separators: Underscore vs Hyphen
Different systems use different separators:
- BCP 47 / IETF / JavaScript:
en-US(hyphen) - POSIX / Python / Java:
en_US(underscore)
Be prepared to convert between formats: en-US โ en_US
โ ๏ธ Don't Use Locale for Authorization
Never assume that locale = "de-DE" means the user is in Germany or should see Germany-specific content. Users can set any locale regardless of location. Use separate mechanisms for:
- Locale: Formatting preferences (how to display data)
- Location/Region: What content/features to show (geo-restrictions, pricing)
๐ฏ Best Practices Checklist
Practice | Priority |
|---|---|
โ Use BCP 47 format for language tags (en-US, not en_US in APIs) | CRITICAL |
โ Always include both language AND region (en-US, not just en) | HIGH |
โ Implement locale fallback chain (zh-Hant-HK โ zh-Hant โ zh โ en) | CRITICAL |
โ Let users explicitly choose their locale (don't just auto-detect) | HIGH |
โ Validate locale codes before using them | HIGH |
โ Store user's locale preference in profile/session | MEDIUM |
โ Use script subtag for Chinese (zh-Hans vs zh-Hant) | CRITICAL |
โ Don't confuse locale with user location/authorization | CRITICAL |
โ Test locale detection with various browser/header configurations | HIGH |
๐ Additional Resources
- BCP 47: RFC 5646 - Tags for Identifying Languages
- IANA Language Subtag Registry: Official registry of language codes
- ISO 639: Language codes standard
- ISO 3166-1: Country/region codes standard
- ISO 15924: Script codes standard
- Unicode CLDR: Common Locale Data Repository
- Intl.Locale (JavaScript): MDN documentation
- Babel (Python): Locale handling library
Next Topic: Start Day of the Week โ
๐ | Start Day of the Week
๐ฏ Learning Objectives
- Understand global variations in week start days
- Handle calendar displays for different cultures
- Implement locale-aware week calculations
- Work with ISO week dates and week numbering
- Handle calendar systems beyond Gregorian
๐ The Week Start Day Challenge
What day does the week start on? The answer depends on where you are in the world! While it might seem like a simple question, different cultures have different conventions, and getting this wrong in a calendar interface can be confusing for users.
Week Start Day by Region
๐บ๐ธ Sunday Start
Countries: United States, Canada, Australia, Philippines, Japan, South Korea, Mexico, Brazil, Israel (partially)
Standard: Traditional in Americas and parts of Asia
๐ฌ๐ง Monday Start
Countries: Most of Europe, China, Russia, India, South Africa, most of Africa, Latin America (some)
Standard: ISO 8601 international standard
๐ธ๐ฆ Saturday Start
Countries: Saudi Arabia, UAE, Egypt, and other Middle Eastern countries
Standard: Common in Islamic calendar contexts
Locale | Country | Week Starts | Weekend Days |
|---|---|---|---|
| United States | Sunday | Saturday, Sunday |
| United Kingdom | Monday | Saturday, Sunday |
| Germany | Monday | Saturday, Sunday |
| Saudi Arabia | Saturday | Friday, Saturday |
| Israel | Sunday | Friday, Saturday |
| Japan | Sunday | Saturday, Sunday |
| China | Monday | Saturday, Sunday |
| Brazil | Sunday | Saturday, Sunday |
โ ISO 8601 Standard
The international standard ISO 8601 defines Monday as the first day of the week. However, many countries (particularly in the Americas and parts of Asia) traditionally use Sunday. Your application should respect the user's locale preference, not enforce a standard.
๐ป Implementation Guidelines
Detecting Week Start Day
JavaScript Example
// Using Intl.Locale (modern browsers, Node.js 12+)
const locale = new Intl.Locale('en-US');
const weekInfo = locale.weekInfo || locale.getWeekInfo?.();
console.log(weekInfo?.firstDay); // โ 7 (Sunday, 1=Monday, 7=Sunday)
// Different locales
const locales = ['en-US', 'en-GB', 'de-DE', 'ar-SA'];
locales.forEach(loc => {
const l = new Intl.Locale(loc);
const info = l.weekInfo || l.getWeekInfo?.();
console.log(`${loc}: Week starts on day ${info?.firstDay}`);
});
// โ en-US: Week starts on day 7 (Sunday)
// โ en-GB: Week starts on day 1 (Monday)
// โ de-DE: Week starts on day 1 (Monday)
// โ ar-SA: Week starts on day 6 (Saturday)
// Fallback for older browsers (manual mapping)
const weekStartByLocale = {
'en-US': 0, // Sunday
'en-GB': 1, // Monday
'de-DE': 1,
'fr-FR': 1,
'ar-SA': 6, // Saturday
'he-IL': 0, // Sunday
'ja-JP': 0,
'zh-CN': 1
};
function getWeekStartDay(locale) {
// Try modern API first
try {
const l = new Intl.Locale(locale);
const info = l.weekInfo || l.getWeekInfo?.();
if (info?.firstDay) {
// Convert 1-7 (Mon-Sun) to 0-6 (Sun-Sat) for JavaScript Date
return info.firstDay === 7 ? 0 : info.firstDay;
}
} catch (e) {}
// Fallback to lookup table
return weekStartByLocale[locale] ?? 0; // Default to Sunday
}
console.log(getWeekStartDay('en-US')); // โ 0 (Sunday)
console.log(getWeekStartDay('en-GB')); // โ 1 (Monday)
Python Example
from babel import Locale
import calendar
# Using Babel
locale = Locale.parse('en_US')
week_start = locale.first_week_day
print(f"Week starts on day: {week_start}") # โ 6 (Sunday in 0-6 where 0=Monday)
# Different locales
locales = ['en_US', 'en_GB', 'de_DE', 'ar_SA']
day_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for loc_code in locales:
loc = Locale.parse(loc_code)
start_day = loc.first_week_day
print(f"{loc_code}: Week starts on {day_names[start_day]}")
# โ en_US: Week starts on Sun
# โ en_GB: Week starts on Mon
# โ de_DE: Week starts on Mon
# โ ar_SA: Week starts on Sat
# Using Python's calendar module (global setting)
# Set first weekday (0=Monday, 6=Sunday)
calendar.setfirstweekday(calendar.SUNDAY)
print(calendar.firstweekday()) # โ 6
# Get month calendar with custom first day
calendar.setfirstweekday(calendar.MONDAY)
cal = calendar.monthcalendar(2025, 11)
print(cal) # Week starts on Monday
Building Locale-Aware Calendar Displays
JavaScript Calendar Grid Example
function generateCalendarGrid(year, month, locale) {
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// Get week start for locale
const weekStart = getWeekStartDay(locale);
// Get day of week for first day of month
let firstDayOfWeek = firstDay.getDay();
// Adjust for locale's week start
firstDayOfWeek = (firstDayOfWeek - weekStart + 7) % 7;
// Build calendar grid
const grid = [];
let week = new Array(firstDayOfWeek).fill(null);
for (let day = 1; day <= lastDay.getDate(); day++) {
week.push(day);
if (week.length === 7) {
grid.push(week);
week = [];
}
}
// Fill remaining days
if (week.length > 0) {
while (week.length < 7) {
week.push(null);
}
grid.push(week);
}
return grid;
}
// Generate calendar for US (Sunday start)
const usCalendar = generateCalendarGrid(2025, 10, 'en-US');
console.log('US Calendar (November 2025, Sunday start):');
console.log(usCalendar);
// Generate calendar for UK (Monday start)
const ukCalendar = generateCalendarGrid(2025, 10, 'en-GB');
console.log('UK Calendar (November 2025, Monday start):');
console.log(ukCalendar);
// Get localized day names for header
function getWeekdayNames(locale, weekStart) {
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short' });
const names = [];
// Start from locale's first day
for (let i = 0; i < 7; i++) {
const day = new Date(2024, 0, weekStart + i); // Jan 2024 starts on Monday
names.push(formatter.format(day));
}
return names;
}
console.log('US weekdays:', getWeekdayNames('en-US', 0));
// โ ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
console.log('UK weekdays:', getWeekdayNames('en-GB', 1));
// โ ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
๐ Week Numbering Systems
Different regions also have different conventions for numbering weeks of the year. This affects business reporting, scheduling, and date calculations.
Week Numbering Systems
System | Description | Used In |
|---|---|---|
ISO 8601 | Week starts Monday. Week 1 contains first Thursday of year. | Europe, most of world |
US System | Week starts Sunday. Week 1 contains January 1st. | United States, Canada |
Middle Eastern | Week starts Saturday. Varies by country. | Saudi Arabia, UAE, Egypt |
JavaScript ISO Week Example
// Calculate ISO week number (week starts Monday, week 1 has first Thursday)
function getISOWeek(date) {
const target = new Date(date.valueOf());
const dayNum = (date.getDay() + 6) % 7;
target.setDate(target.getDate() - dayNum + 3);
const firstThursday = target.valueOf();
target.setMonth(0, 1);
if (target.getDay() !== 4) {
target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7);
}
return 1 + Math.ceil((firstThursday - target) / 604800000);
}
const date1 = new Date(2025, 0, 1); // January 1, 2025
console.log(`ISO Week: ${getISOWeek(date1)}`); // โ ISO Week: 1
const date2 = new Date(2025, 10, 5); // November 5, 2025
console.log(`ISO Week: ${getISOWeek(date2)}`); // โ ISO Week: 45
// Using Intl for week-year formatting (where supported)
const formatter = new Intl.DateTimeFormat('en-GB', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
});
console.log(formatter.format(date2));
// โ "Wednesday, 5 November 2025"
๐ Alternative Calendar Systems
While the Gregorian calendar is most widely used, many cultures use alternative calendar systems for religious, cultural, or official purposes.
Major Calendar Systems
Calendar | Used By | Key Features |
|---|---|---|
Gregorian | Worldwide standard | Solar, 12 months, year 2025 |
Islamic (Hijri) | Muslim communities | Lunar, 12 months, year 1447 (2025 CE) |
Hebrew | Jewish communities | Lunisolar, year 5786 (2025 CE) |
Chinese | Chinese, Vietnamese | Lunisolar, 12-13 months, zodiac years |
Japanese | Japan (official) | Era-based, Reiwa 7 (2025 CE) |
Persian | Iran, Afghanistan | Solar, year 1404 (2025 CE) |
Buddhist | Thailand, Sri Lanka | Solar, year 2569 (2025 CE) |
JavaScript Calendar System Example
// Using Intl.DateTimeFormat with different calendars
const date = new Date(2025, 10, 5); // November 5, 2025
// Gregorian (default)
const gregorian = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
calendar: 'gregory'
}).format(date);
console.log(`Gregorian: ${gregorian}`);
// โ "November 5, 2025"
// Islamic/Hijri calendar
const islamic = new Intl.DateTimeFormat('ar-SA', {
year: 'numeric',
month: 'long',
day: 'numeric',
calendar: 'islamic'
}).format(date);
console.log(`Islamic: ${islamic}`);
// โ "ุฌู
ุงุฏู ุงูุฃููู ูคุ ูกูคูคูง" (approx)
// Hebrew calendar
const hebrew = new Intl.DateTimeFormat('he-IL', {
year: 'numeric',
month: 'long',
day: 'numeric',
calendar: 'hebrew'
}).format(date);
console.log(`Hebrew: ${hebrew}`);
// โ "ืืณ ืึฐึผืึถืฉึฐืืึธื ืชืฉืคืดื" (approx)
// Japanese calendar (with era)
const japanese = new Intl.DateTimeFormat('ja-JP', {
year: 'numeric',
month: 'long',
day: 'numeric',
calendar: 'japanese',
era: 'long'
}).format(date);
console.log(`Japanese: ${japanese}`);
// โ "ไปคๅ7ๅนด11ๆ5ๆฅ"
// Chinese calendar
const chinese = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
calendar: 'chinese'
}).format(date);
console.log(`Chinese: ${chinese}`);
// โ Chinese calendar date
โ ๏ธ Important Note on Calendar Systems
Always store dates in Gregorian calendar internally (as UTC timestamps or ISO 8601 strings). Alternative calendars should only be used for display purposes. Converting between calendar systems for storage can lead to data corruption and synchronization issues.
๐ฏ Best Practices Checklist
Practice | Priority |
|---|---|
โ Detect and respect user's locale for week start day | CRITICAL |
โ Display calendar grids with correct week start | HIGH |
โ Store dates in Gregorian/UTC internally, convert for display only | CRITICAL |
โ Allow users to override calendar preferences in settings | MEDIUM |
โ Be aware of different weekend days (Fri-Sat vs Sat-Sun) | HIGH |
โ Use ISO 8601 for week numbering in international contexts | MEDIUM |
โ Test calendar displays with Sunday, Monday, and Saturday starts | HIGH |
โ Support alternative calendars for display in relevant locales | MEDIUM |
๐ Additional Resources
- ISO 8601: Date and time format standard (includes week numbering)
- Unicode CLDR: Locale-specific calendar data
- Intl.Locale.weekInfo: MDN documentation
- Temporal API (Proposed): Modern JavaScript date/time handling
- Babel (Python): Calendar and locale support
- ICU (International Components for Unicode): Comprehensive calendar support
Next Topic: Conclusion & Resources โ
๐ | Conclusion & Resources
๐ Congratulations!
You've completed the Internationalization Knowledge Center! You now have a solid foundation in building applications that serve users around the world with respect and accuracy. Let's recap what you've learned and explore where to go from here.
๐ What You've Learned
๐ Time Zones
- Store in UTC, display in local time
- Use IANA identifiers
- Handle DST transitions
- Never implement your own TZ logic
๐ฐ Numbers & Currency
- Locale-aware formatting
- Decimal/group separators vary
- Currency symbols and placement
- Store as numbers, format for display
๐ Date & Time
- Format patterns vary globally
- 12-hour vs 24-hour formats
- Avoid ambiguous date formats
- Use month names or ISO 8601
๐ท๏ธ Locale Identifiers
๐ Calendar Systems
- Week start varies by culture
- Sunday vs Monday vs Saturday
- Alternative calendar systems
- ISO 8601 week numbering
๐ Beyond the Basics: Additional i18n Topics
While we've covered essential formatting and display topics, there are additional areas of internationalization you should be aware of:
๐ Right-to-Left (RTL) Languages
Languages like Arabic, Hebrew, Persian, and Urdu are written right-to-left. Supporting RTL requires:
- HTML dir attribute:
<html dir="rtl"> - CSS logical properties: Use
margin-inline-startinstead ofmargin-left - Mirrored UI: Navigation, icons, and layouts flip horizontally
- Bidirectional text: Handle mixed LTR/RTL content properly
<!-- Example -->
<html dir="rtl" lang="ar">
<style>
.container {
margin-inline-start: 20px; /* Adapts to LTR/RTL */
padding-inline: 15px; /* Instead of padding-left/right */
}
</style>
โ๏ธ Text & String Handling
- UTF-8 Encoding: Always use UTF-8 for text storage and transmission
- String Concatenation: Never concatenate translated strings โ use message formats with placeholders
- Pluralization: Different languages have different plural rules (English: 2 forms, Arabic: 6 forms)
- Gender: Some languages require gender-aware translations
- String Length: Translations can be 30-50% longer โ design flexible UIs
// โ Wrong: Concatenation breaks translation
const message = "You have " + count + " items";
// โ
Right: Use message format with placeholders
const message = t('items.count', { count: count });
// โ English: "You have 5 items"
// โ German: "Sie haben 5 Artikel"
// โ Arabic: "ูุฏูู ูฅ ุนูุงุตุฑ"
๐ค Sorting & Collation
Alphabetical order varies by language and locale:
- German: รค, รถ, รผ have special sort positions
- Spanish: ch and ll were traditionally separate letters
- Chinese: Multiple sorting methods (pinyin, stroke count, radical)
- Case sensitivity varies by locale
// JavaScript locale-aware sorting
const names = ['รmer', 'Anna', 'Bjรถrn', 'รgir'];
// English sort
console.log(names.sort((a, b) =>
a.localeCompare(b, 'en')
));
// โ ['รgir', 'Anna', 'Bjรถrn', 'รmer']
// German sort (รค comes after a)
console.log(names.sort((a, b) =>
a.localeCompare(b, 'de')
));
// โ ['Anna', 'รgir', 'Bjรถrn', 'รmer']
๐จ Images, Icons & Symbols
- Cultural symbols: Gestures, colors, and icons have different meanings globally
- Text in images: Avoid embedding text in images โ use overlays or SVG
- Flags: Be cautious with flags โ regional sensitivities exist
- Emojis: Not all emojis render identically across platforms
โ๏ธ Legal & Privacy Considerations
- GDPR: European privacy regulations
- Data localization: Some countries require data to be stored locally
- Terms of service: Must be provided in local languages in some jurisdictions
- Accessibility: WCAG guidelines apply internationally
๐ ๏ธ Essential Tools & Libraries
JavaScript/TypeScript
Library | Purpose | Link |
|---|---|---|
Intl (Built-in) | Native i18n formatting APIs | MDN Web Docs |
i18next | Translation framework | i18next.com |
Luxon | Modern date/time handling | moment.github.io/luxon |
FormatJS | Comprehensive i18n toolkit | formatjs.io |
date-fns | Date utilities with i18n | date-fns.org |
Python
Library | Purpose | Install |
|---|---|---|
Babel | Comprehensive i18n library |
|
pytz | Time zone handling |
|
python-dateutil | Date parsing and manipulation |
|
gettext | Translation framework (built-in) | Standard library |
Java
Library/API | Purpose | Notes |
|---|---|---|
java.time | Modern date/time API (Java 8+) | Built-in, replaces Date/Calendar |
ICU4J | International Components for Unicode | Most comprehensive i18n support |
ResourceBundle | Translation management | Built-in |
Cross-Platform
- ICU (International Components for Unicode): Available for C, C++, Java โ industry standard
- Unicode CLDR: Common Locale Data Repository โ authoritative locale data
- IANA Time Zone Database: Maintained time zone information
๐ Learning Resources
๐ Documentation
- Unicode.org: Official Unicode standards
- ICU4X Documentation: Modern i18n implementation guide
- MDN Web Docs: Intl API reference
- W3C i18n Activity: Web internationalization standards
๐ Courses & Tutorials
- Coursera: Software Product Management Specialization
- Pluralsight: Internationalization courses
- freeCodeCamp: i18n tutorials
- YouTube: Conference talks on localization
๐ Blogs & Articles
- Phrase Blog: Localization industry insights
- Smartling Resources: i18n best practices
- Localization Institute: Professional resources
- Dev.to #i18n: Developer articles
๐ฅ Communities
- Stack Overflow: [internationalization] tag
- Reddit: r/i18n, r/localization
- Discord/Slack: i18n developer communities
- LinkedIn Groups: Localization professionals
๐ค How to Get Help
The L10n Team Is Here for You!
As you implement internationalization in your projects, remember that you're not alone. The Localization team is your partner in building globally-accessible products.
๐ง Email Support
Reach out to the L10n team with questions, code reviews, or guidance on i18n implementation.
๐ฌ Slack Channel
Join #i18n-support for quick questions, discussions, and updates on i18n best practices.
๐ Office Hours
Weekly drop-in sessions where you can get live help with your i18n challenges.
๐ซ Jira Tickets
Submit formal requests for locale data, translation reviews, or technical consultations.
โ When to Reach Out
- Before starting: Planning a new feature with international scope? Get L10n input early!
- During development: Stuck on a formatting issue? Need to validate an approach?
- Code review: Request L10n review before merging i18n-related code
- Testing phase: Need help testing with different locales?
- Production issues: Users reporting locale-specific bugs? We can help diagnose
๐ฏ Quick Reference: i18n Checklist
Use this checklist when implementing or reviewing internationalization in your code:
Category | Key Checks |
|---|---|
Time & Dates | โ Stored in UTC โ IANA time zone IDs used โ Locale-aware formatting โ Time zone displayed to users |
Numbers & Currency | โ Locale-aware formatting used โ Currency code specified โ Stored as numbers, not strings โ Decimal precision handled |
Locales | โ BCP 47 format used โ Fallback chain implemented โ User can select locale โ Locale validated before use |
Text & Strings | โ UTF-8 encoding throughout โ No string concatenation โ Placeholders for dynamic content โ Pluralization handled |
UI/UX | โ RTL layouts supported (if applicable) โ Flexible UI for text expansion โ Calendar respects week start โ No text embedded in images |
Testing | โ Tested with multiple locales โ Edge cases verified (DST, etc.) โ RTL tested (if applicable) โ L10n team reviewed |
๐ Final Thoughts
Internationalization is not just about technical implementation โ it's about respect. Respect for your users' languages, cultures, and conventions. Every time you properly format a date, handle a time zone correctly, or display a currency symbol in the right place, you're telling users: "We built this for you."
The techniques you've learned here will serve you throughout your career. As our products reach more users in more countries, your i18n expertise becomes increasingly valuable.
Remember: Start with i18n in mind, use the right libraries, test thoroughly, and don't hesitate to ask the L10n team for guidance. Together, we're building software that truly serves the world.
๐ You're Now i18n Ready!
Thank you for investing your time in learning internationalization.
We're excited to see the globally-accessible features you'll build!
Questions? Feedback on this guide?
Reach out to the L10n team โ we're always here to help! ๐ค