Skip to main content

Overview

app-datepicker uses the native Intl.DateTimeFormat API for internationalization, providing automatic localization for over 100 locales without requiring additional language packs.
The component leverages the browser’s built-in internationalization capabilities, ensuring consistent formatting across your application.

Setting the Locale

Basic Locale Configuration

Set the locale property to any valid BCP 47 language tag:
// American English (default)
picker.locale = 'en-US';

// British English
picker.locale = 'en-GB';

// Traditional Chinese (Taiwan)
picker.locale = 'zh-TW';

// German (Germany)
picker.locale = 'de-DE';

// Japanese
picker.locale = 'ja-JP';

// Arabic (Saudi Arabia)
picker.locale = 'ar-SA';

Auto-Detection

If no locale is specified, the component automatically uses the browser’s locale:
// From src/mixins/date-picker-mixin.ts:20
// Default: DateTimeFormat().resolvedOptions().locale
import { html } from 'lit';

const template = html`
  <app-date-picker locale="fr-FR"></app-date-picker>
`;

Date Format Examples

The locale affects how dates are displayed throughout the component:
// American English
picker.locale = 'en-US';
// Selected: Mar 15, 2024
// Month/Year: March 2024
// Weekday: Mon, Tue, Wed...

Internal Formatters

The component creates multiple Intl.DateTimeFormat instances for different parts of the UI. Here’s how they’re configured:
From src/helpers/to-formatters.ts:6-42:
export function toFormatters(locale: string): Formatters {
  const dateFmt = DateTimeFormat(locale, {
    day: 'numeric',
    month: 'short',
    timeZone: 'UTC',
    weekday: 'short',
  });
  const dayFmt = DateTimeFormat(locale, { day: 'numeric', timeZone: 'UTC' });
  const fullDateFmt = DateTimeFormat(locale, {
    day: 'numeric',
    month: 'short',
    timeZone: 'UTC',
    year: 'numeric',
  });
  const longMonthYearFmt = DateTimeFormat(locale, {
    month: 'long',
    timeZone: 'UTC',
    year: 'numeric',
  });
  const longWeekdayFmt = DateTimeFormat(locale, { timeZone: 'UTC', weekday: 'long' });
  const longMonthFmt = DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' });
  const narrowWeekdayFmt = DateTimeFormat(locale, { timeZone: 'UTC', weekday: 'narrow' });
  const yearFmt = DateTimeFormat(locale, { timeZone: 'UTC', year: 'numeric' });

  return {
    dateFormat: getFormatter(dateFmt),
    dayFormat: getFormatter(dayFmt),
    fullDateFormat: getFormatter(fullDateFmt),
    locale,
    longMonthFormat: getFormatter(longMonthFmt),
    longMonthYearFormat: getFormatter(longMonthYearFmt),
    longWeekdayFormat: getFormatter(longWeekdayFmt),
    narrowWeekdayFormat: getFormatter(narrowWeekdayFmt),
    yearFormat: getFormatter(yearFmt),
  };
}

Formatter Types

The component uses these formatters internally:
FormatterPurposeExample (en-US)Example (ja-JP)
dateFormatCalendar day cellsMar 15, Fri3月15日(金)
dayFormatDay number only1515
fullDateFormatFull date with yearMar 15, 20242024年3月15日
longMonthYearFormatHeader displayMarch 20242024年3月
longWeekdayFormatAccessibility labelsFriday金曜日
narrowWeekdayFormatWeekday headersF
yearFormatYear grid20242024年

First Day of Week

Customize which day starts the week:
// Sunday (default: 0)
picker.firstDayOfWeek = 0;

// Monday (common in Europe)
picker.firstDayOfWeek = 1;

// Saturday (common in Middle East)
picker.firstDayOfWeek = 6;
// American convention
picker.firstDayOfWeek = 0;
// Week: Sun Mon Tue Wed Thu Fri Sat
The value is 0 for Sunday through 6 for Saturday, matching JavaScript’s Date.getDay() convention.

Customizing Labels

Override default UI labels for complete localization:
picker.nextMonthLabel = 'Nächster Monat';        // Default: 'Next month'
picker.previousMonthLabel = 'Vorheriger Monat';  // Default: 'Previous month'
picker.chooseMonthLabel = 'Monat wählen';        // Default: 'Choose month'
picker.chooseYearLabel = 'Jahr wählen';          // Default: 'Choose year'
From src/constants.ts:6-14:
export const labelChooseMonth = 'Choose month' as const;
export const labelChooseYear = 'Choose year' as const;
export const labelNextMonth = 'Next month' as const;
export const labelPreviousMonth = 'Previous month' as const;
export const labelSelectedDate = 'Selected date' as const;
export const labelSelectedYear = 'Selected year' as const;
export const labelShortWeek = 'Wk' as const;
export const labelToday = 'Today' as const;
export const labelToyear = 'Toyear' as const;
export const labelWeek = 'Week' as const;

Date Labels

picker.todayLabel = 'Aujourd\'hui';      // Default: 'Today'
picker.selectedDateLabel = 'Date sélectionnée';  // Default: 'Selected date'
picker.selectedYearLabel = 'Année sélectionnée'; // Default: 'Selected year'

Week Number Labels

picker.weekLabel = 'Semaine';           // Default: 'Week'
picker.shortWeekLabel = 'Sem';          // Default: 'Wk'
picker.weekNumberTemplate = 'Semaine %s'; // Default: 'Week %s'

Complete Localization Example

Here’s a fully localized French date picker:
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import 'app-datepicker/dist/date-picker/app-date-picker.js';

@customElement('french-date-picker')
export class FrenchDatePicker extends LitElement {
  render() {
    return html`
      <app-date-picker
        locale="fr-FR"
        .firstDayOfWeek=${1}
        .showWeekNumber=${true}
        nextMonthLabel="Mois suivant"
        previousMonthLabel="Mois précédent"
        chooseMonthLabel="Choisir le mois"
        chooseYearLabel="Choisir l'année"
        todayLabel="Aujourd'hui"
        selectedDateLabel="Date sélectionnée"
        selectedYearLabel="Année sélectionnée"
        weekLabel="Semaine"
        shortWeekLabel="Sem"
        weekNumberTemplate="Semaine %s"
      ></app-date-picker>
    `;
  }
}

Localization with Dialog

The dialog component includes additional labels for action buttons:
import { html } from 'lit';

const germanDialog = html`
  <app-date-picker-dialog
    locale="de-DE"
    confirmLabel="Bestätigen"
    dismissLabel="Abbrechen"
    resetLabel="Zurücksetzen"
    chooseMonthLabel="Monat wählen"
    chooseYearLabel="Jahr wählen"
  ></app-date-picker-dialog>
`;

Localization with Input

The input component includes a clear button label:
import { html } from 'lit';

const spanishInput = html`
  <app-date-picker-input
    locale="es-ES"
    label="Seleccionar fecha"
    clearLabel="Limpiar"
  ></app-date-picker-input>
`;
From src/date-picker-input/constants.ts:3:
export const appDatePickerInputClearLabel = 'Clear' as const;

Dynamic Locale Switching

Change locale dynamically at runtime:
import { LitElement, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';

@customElement('multi-locale-picker')
export class MultiLocalePicker extends LitElement {
  @state()
  private locale = 'en-US';

  private locales = [
    { code: 'en-US', name: 'English (US)' },
    { code: 'zh-TW', name: '繁體中文' },
    { code: 'ja-JP', name: '日本語' },
    { code: 'de-DE', name: 'Deutsch' },
  ];

  render() {
    return html`
      <select @change=${this.handleLocaleChange}>
        ${this.locales.map(l => html`
          <option value=${l.code} ?selected=${l.code === this.locale}>
            ${l.name}
          </option>
        `)}
      </select>

      <app-date-picker
        .locale=${this.locale}
        .firstDayOfWeek=${this.getFirstDayOfWeek()}
      ></app-date-picker>
    `;
  }

  private handleLocaleChange(e: Event) {
    this.locale = (e.target as HTMLSelectElement).value;
  }

  private getFirstDayOfWeek(): number {
    // Monday for most European locales
    if (['de-DE', 'fr-FR', 'it-IT'].includes(this.locale)) {
      return 1;
    }
    // Sunday for US and others
    return 0;
  }
}

Testing Localization

Here’s an example from the test suite showing locale behavior:
From src/__tests__/date-picker/app-date-picker.test.ts:109-141:
it.each<{
  $_locale: string,
  $_yearMonthLabel: string;
  locale: string | undefined,
}>([
  {
    $_locale: Intl.DateTimeFormat().resolvedOptions().locale,
    $_yearMonthLabel: 'February 2020',
    locale: undefined,
  },
  {
    $_locale: 'zh-TW',
    $_yearMonthLabel: '2020年2月',
    locale: 'zh-TW',
  },
])(\`renders (locale=$locale)\`, async ({
  $_locale,
  $_yearMonthLabel,
  locale,
}) => {
  const testValue = '2020-02-02';
  const el = await fixture<AppDatePicker>(
    html\`<app-date-picker .locale=\${locale as never} min="1970-01-01" value=\${testValue}></app-date-picker>\`
  );

  const selectedYearMonth = el.query<HTMLParagraphElement>(
    elementSelectors.selectedYearMonth
  );

  expect(el.locale).toBe($_locale);
  expect(el.value).toBe(testValue);
  expect(selectedYearMonth).toHaveTextContent($_yearMonthLabel);
});

Best Practices

Always specify locale explicitly for consistent behavior across different browsers and user settings.
Match firstDayOfWeek to locale conventions for the best user experience (e.g., Monday for European locales).
Changing the locale after component initialization will re-render the calendar. Avoid frequent locale changes for better performance.

Next Steps

Styling

Customize the visual appearance of the date picker

Advanced Features

Configure week numbers, disabled dates, and more