I18n Challenges and Best Practices
- ๐ | Regional Time Zone Systems
- ๐ฐ | Number & Currency Formatting
- ๐ | Date & Time Formatting
- ๐ท๏ธ | Language & Region Identifiers
- ๐ | Start Day of the Week
๐ | 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 โ