日付と時刻フィールドのローカライズされたラベルを表示する方法
Intl.DisplayNamesを使用してフィールドラベルを取得し、Intl.DateTimeFormatを使用して任意の言語で月名と曜日名を取得します。
はじめに
日付と時刻の入力フォームを構築する際、各フィールドを説明するラベルが必要です。日付ピッカーには「月」、「年」、「日」などのラベルが必要です。時刻ピッカーには「時」や「分」などのラベルが必要です。これらのラベルはユーザーの言語で表示される必要があります。
これらのラベルを英語でハードコーディングすることは、国際的なアプリケーションでは機能しません。フランス語のユーザーは「Mois」や「Année」を期待し、スペイン語のユーザーは「Mes」や「Año」を探します。任意の言語でこれらのラベルを自動的に提供するシステムが必要です。
JavaScriptはこのための2つの補完的なAPIを提供しています。Intl.DisplayNames APIは「月」や「年」などのフィールドラベルを提供します。Intl.DateTimeFormat APIは、月名や曜日名などのフィールドの実際の値を提供します。
日付と時刻フィールドラベルの理解
日付と時刻のインターフェースには2種類のラベルが必要です。フィールドラベルは各入力にどのようなデータが入るかを説明します。フィールド値はドロップダウンやピッカーに表示される実際のデータです。
フィールドラベルには「年」、「月」、「日」、「時」、「分」、「秒」などの単語が含まれます。これらはフォームフィールド自体にラベルを付けます。
フィールド値には「1月」や「2月」などの月名、「月曜日」や「火曜日」などの曜日名、「午前」や「午後」などの期間ラベルが含まれます。これらはドロップダウンや選択リストに入力されます。
完全な日付フォームには両方のタイプのラベルが必要です。
<label>Month</label>
<select>
<option>January</option>
<option>February</option>
<option>March</option>
<!-- more months -->
</select>
<label>Year</label>
<input type="number" />
「月」と「1月」の両方をローカライズする必要がありますが、異なるアプローチが必要です。
Intl.DisplayNamesでフィールドラベルを取得する
Intl.DisplayNamesコンストラクタにtype: "dateTimeField"を指定すると、日付と時刻のコンポーネントのローカライズされたラベルが返されます。
const labels = new Intl.DisplayNames('en-US', { type: 'dateTimeField' });
console.log(labels.of('year'));
// "year"
console.log(labels.of('month'));
// "month"
console.log(labels.of('day'));
// "day"
console.log(labels.of('hour'));
// "hour"
console.log(labels.of('minute'));
// "minute"
console.log(labels.of('second'));
// "second"
of()メソッドは、フィールドコードを受け取り、そのローカライズされたラベルを返します。ラベルは指定されたロケールの規則に従います。
ロケールを変更することで、任意の言語でラベルを取得できます。
// Spanish labels
const esLabels = new Intl.DisplayNames('es-ES', { type: 'dateTimeField' });
console.log(esLabels.of('year'));
// "año"
console.log(esLabels.of('month'));
// "mes"
console.log(esLabels.of('day'));
// "día"
console.log(esLabels.of('hour'));
// "hora"
console.log(esLabels.of('minute'));
// "minuto"
// French labels
const frLabels = new Intl.DisplayNames('fr-FR', { type: 'dateTimeField' });
console.log(frLabels.of('year'));
// "année"
console.log(frLabels.of('month'));
// "mois"
console.log(frLabels.of('day'));
// "jour"
console.log(frLabels.of('hour'));
// "heure"
console.log(frLabels.of('minute'));
// "minute"
// Japanese labels
const jaLabels = new Intl.DisplayNames('ja-JP', { type: 'dateTimeField' });
console.log(jaLabels.of('year'));
// "年"
console.log(jaLabels.of('month'));
// "月"
console.log(jaLabels.of('day'));
// "日"
console.log(jaLabels.of('hour'));
// "時"
console.log(jaLabels.of('minute'));
// "分"
各ロケールは、独自の言語と文字体系でラベルを提供します。
利用可能な日付と時刻のフィールドコード
Intl.DisplayNames APIは、以下のフィールドコードをサポートしています。
const labels = new Intl.DisplayNames('en-US', { type: 'dateTimeField' });
console.log(labels.of('era'));
// "era"
console.log(labels.of('year'));
// "year"
console.log(labels.of('quarter'));
// "quarter"
console.log(labels.of('month'));
// "month"
console.log(labels.of('weekOfYear'));
// "week"
console.log(labels.of('weekday'));
// "day of the week"
console.log(labels.of('day'));
// "day"
console.log(labels.of('dayPeriod'));
// "AM/PM"
console.log(labels.of('hour'));
// "hour"
console.log(labels.of('minute'));
// "minute"
console.log(labels.of('second'));
// "second"
console.log(labels.of('timeZoneName'));
// "time zone"
これらのコードを使用して、インターフェースに必要な任意の日付または時刻コンポーネントのラベルを取得できます。
ローカライズされた月名を取得する
Intl.DateTimeFormat APIは、ドロップダウンや選択リストに入力するための月名を提供します。monthオプションを"long"に設定してフォーマッタを作成し、各月を表す日付をフォーマットします。
function getMonthNames(locale) {
const formatter = new Intl.DateTimeFormat(locale, {
month: 'long',
timeZone: 'UTC'
});
const months = [];
for (let month = 0; month < 12; month++) {
const date = new Date(Date.UTC(2000, month, 1));
months.push(formatter.format(date));
}
return months;
}
console.log(getMonthNames('en-US'));
// ["January", "February", "March", "April", "May", "June",
// "July", "August", "September", "October", "November", "December"]
この関数は、年の各月の日付を作成し、それらをフォーマットして月名を抽出します。timeZone: 'UTC'を設定することで、タイムゾーン間で一貫した結果が保証されます。
任意の言語で月名を取得できます。
console.log(getMonthNames('es-ES'));
// ["enero", "febrero", "marzo", "abril", "mayo", "junio",
// "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]
console.log(getMonthNames('fr-FR'));
// ["janvier", "février", "mars", "avril", "mai", "juin",
// "juillet", "août", "septembre", "octobre", "novembre", "décembre"]
console.log(getMonthNames('ja-JP'));
// ["1月", "2月", "3月", "4月", "5月", "6月",
// "7月", "8月", "9月", "10月", "11月", "12月"]
各ロケールは、独自の規則に従って月名をフォーマットします。
月名の長さを制御する
monthオプションは、月名の長さを制御するさまざまな値を受け入れます。
"long"値は、完全な月名を返します。
const longFormatter = new Intl.DateTimeFormat('en-US', {
month: 'long',
timeZone: 'UTC'
});
const date = new Date(Date.UTC(2000, 0, 1));
console.log(longFormatter.format(date));
// "January"
"short"値は、省略された月名を返します。
const shortFormatter = new Intl.DateTimeFormat('en-US', {
month: 'short',
timeZone: 'UTC'
});
console.log(shortFormatter.format(date));
// "Jan"
"narrow"値は、可能な限り最短の月名(通常は1文字)を返します。
const narrowFormatter = new Intl.DateTimeFormat('en-US', {
month: 'narrow',
timeZone: 'UTC'
});
console.log(narrowFormatter.format(date));
// "J"
"narrow"は慎重に使用してください。複数の月が同じ文字を共有する場合があります。英語では、January、June、Julyはすべて「J」を生成します。
ローカライズされた曜日名を取得する
同じパターンを使用して曜日名を取得します。weekdayオプションを設定して、フォーマットを制御します。
function getWeekdayNames(locale, format = 'long') {
const formatter = new Intl.DateTimeFormat(locale, {
weekday: format,
timeZone: 'UTC'
});
const weekdays = [];
// Start from a Sunday (January 2, 2000 was a Sunday)
for (let day = 0; day < 7; day++) {
const date = new Date(Date.UTC(2000, 0, 2 + day));
weekdays.push(formatter.format(date));
}
return weekdays;
}
console.log(getWeekdayNames('en-US'));
// ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
console.log(getWeekdayNames('en-US', 'short'));
// ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
console.log(getWeekdayNames('en-US', 'narrow'));
// ["S", "M", "T", "W", "T", "F", "S"]
任意の言語で曜日名を取得できます。
console.log(getWeekdayNames('es-ES'));
// ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"]
console.log(getWeekdayNames('fr-FR'));
// ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
console.log(getWeekdayNames('ja-JP'));
// ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]
ロケールによって、週の開始日が異なる場合があります。この関数は常に日曜日から土曜日までを順番に返します。
ローカライズされた時間帯ラベルを取得する
時間帯ラベルは、12時間制で使用されるAMおよびPMの表示です。formatToParts()を使用して、これらのラベルを抽出します。
function getPeriodLabels(locale) {
const formatter = new Intl.DateTimeFormat(locale, {
hour: 'numeric',
hour12: true,
timeZone: 'UTC'
});
const amDate = new Date(Date.UTC(2000, 0, 1, 0, 0, 0));
const pmDate = new Date(Date.UTC(2000, 0, 1, 12, 0, 0));
const amParts = formatter.formatToParts(amDate);
const pmParts = formatter.formatToParts(pmDate);
const am = amParts.find(part => part.type === 'dayPeriod').value;
const pm = pmParts.find(part => part.type === 'dayPeriod').value;
return { am, pm };
}
console.log(getPeriodLabels('en-US'));
// { am: "AM", pm: "PM" }
console.log(getPeriodLabels('es-ES'));
// { am: "a. m.", pm: "p. m." }
console.log(getPeriodLabels('fr-FR'));
// { am: "AM", pm: "PM" }
console.log(getPeriodLabels('ja-JP'));
// { am: "午前", pm: "午後" }
formatToParts()メソッドは、フォーマットされた時刻の各部分を表すオブジェクトの配列を返します。type: "dayPeriod"を持つオブジェクトには、AMまたはPMのラベルが含まれます。
一部のロケールはデフォルトで24時間制を使用し、時間帯ラベルを含みません。hour12: trueオプションを使用して、12時間制を強制できます。
完全にローカライズされた日付フォームを構築する
これらすべてのテクニックを組み合わせて、完全にローカライズされた日付入力フォームを作成します。
function createDateForm(locale) {
const fieldLabels = new Intl.DisplayNames(locale, { type: 'dateTimeField' });
const monthNames = getMonthNames(locale);
const currentYear = new Date().getFullYear();
return {
monthLabel: fieldLabels.of('month'),
months: monthNames.map((name, index) => ({
value: index + 1,
label: name
})),
dayLabel: fieldLabels.of('day'),
yearLabel: fieldLabels.of('year'),
yearPlaceholder: currentYear
};
}
// Helper function from previous example
function getMonthNames(locale) {
const formatter = new Intl.DateTimeFormat(locale, {
month: 'long',
timeZone: 'UTC'
});
const months = [];
for (let month = 0; month < 12; month++) {
const date = new Date(Date.UTC(2000, month, 1));
months.push(formatter.format(date));
}
return months;
}
const enForm = createDateForm('en-US');
console.log(enForm.monthLabel);
// "month"
console.log(enForm.months[0]);
// { value: 1, label: "January" }
console.log(enForm.dayLabel);
// "day"
console.log(enForm.yearLabel);
// "year"
const esForm = createDateForm('es-ES');
console.log(esForm.monthLabel);
// "mes"
console.log(esForm.months[0]);
// { value: 1, label: "enero" }
console.log(esForm.dayLabel);
// "día"
console.log(esForm.yearLabel);
// "año"
この構造は、HTMLでローカライズされた日付フォームをレンダリングするために必要なすべてを提供します。
function renderDateForm(locale) {
const form = createDateForm(locale);
return `
<div class="date-form">
<div class="form-field">
<label>${form.monthLabel}</label>
<select name="month">
${form.months.map(month =>
`<option value="${month.value}">${month.label}</option>`
).join('')}
</select>
</div>
<div class="form-field">
<label>${form.dayLabel}</label>
<input type="number" name="day" min="1" max="31" />
</div>
<div class="form-field">
<label>${form.yearLabel}</label>
<input type="number" name="year" placeholder="${form.yearPlaceholder}" />
</div>
</div>
`;
}
console.log(renderDateForm('en-US'));
// Renders form with English labels and month names
console.log(renderDateForm('fr-FR'));
// Renders form with French labels and month names
フォームは、指定した任意のロケールに自動的に適応します。
ローカライズされた時刻フォームを構築する
同じアプローチを適用して、時、分、時間帯セレクターを備えた時刻入力フォームを作成します。
function createTimeForm(locale) {
const fieldLabels = new Intl.DisplayNames(locale, { type: 'dateTimeField' });
const periods = getPeriodLabels(locale);
const hours = [];
for (let hour = 1; hour <= 12; hour++) {
hours.push({ value: hour, label: hour.toString() });
}
const minutes = [];
for (let minute = 0; minute < 60; minute += 5) {
minutes.push({
value: minute,
label: minute.toString().padStart(2, '0')
});
}
return {
hourLabel: fieldLabels.of('hour'),
hours: hours,
minuteLabel: fieldLabels.of('minute'),
minutes: minutes,
periodLabel: fieldLabels.of('dayPeriod'),
periods: [
{ value: 'am', label: periods.am },
{ value: 'pm', label: periods.pm }
]
};
}
// Helper function from previous example
function getPeriodLabels(locale) {
const formatter = new Intl.DateTimeFormat(locale, {
hour: 'numeric',
hour12: true,
timeZone: 'UTC'
});
const amDate = new Date(Date.UTC(2000, 0, 1, 0, 0, 0));
const pmDate = new Date(Date.UTC(2000, 0, 1, 12, 0, 0));
const amParts = formatter.formatToParts(amDate);
const pmParts = formatter.formatToParts(pmDate);
const am = amParts.find(part => part.type === 'dayPeriod').value;
const pm = pmParts.find(part => part.type === 'dayPeriod').value;
return { am, pm };
}
const enTime = createTimeForm('en-US');
console.log(enTime.hourLabel);
// "hour"
console.log(enTime.minuteLabel);
// "minute"
console.log(enTime.periodLabel);
// "AM/PM"
console.log(enTime.periods);
// [{ value: "am", label: "AM" }, { value: "pm", label: "PM" }]
const jaTime = createTimeForm('ja-JP');
console.log(jaTime.hourLabel);
// "時"
console.log(jaTime.minuteLabel);
// "分"
console.log(jaTime.periodLabel);
// "午前/午後"
console.log(jaTime.periods);
// [{ value: "am", label: "午前" }, { value: "pm", label: "午後" }]
これにより、ローカライズされた時刻ピッカーをレンダリングするために必要なすべてのデータが提供されます。
日付と時刻のフィールドラベルを使用するタイミング
日付と時刻のフィールドラベルは、複数のタイプのインターフェースに表示されます。
カスタム日付・時刻ピッカー
日付ピッカー、カレンダーウィジェット、または時刻セレクターを構築する際は、ローカライズされたラベルを使用してください。
const locale = navigator.language;
const labels = new Intl.DisplayNames(locale, { type: 'dateTimeField' });
const datePicker = {
yearLabel: labels.of('year'),
monthLabel: labels.of('month'),
dayLabel: labels.of('day')
};
フォームフィールドラベル
日付と時刻データの標準フォーム入力にラベルを適用します。
const labels = new Intl.DisplayNames('en-US', { type: 'dateTimeField' });
document.querySelector('#birthdate-month-label').textContent =
labels.of('month');
document.querySelector('#birthdate-year-label').textContent =
labels.of('year');
アクセシビリティラベル
スクリーンリーダーや支援技術向けに、ローカライズされたARIAラベルを提供します。
const locale = navigator.language;
const labels = new Intl.DisplayNames(locale, { type: 'dateTimeField' });
const input = document.querySelector('#date-input');
input.setAttribute('aria-label', labels.of('year'));
データテーブルヘッダー
日付と時刻のコンポーネントを表示するテーブルの列にラベルを付けます。
const labels = new Intl.DisplayNames('en-US', { type: 'dateTimeField' });
const table = `
<table>
<thead>
<tr>
<th>${labels.of('year')}</th>
<th>${labels.of('month')}</th>
<th>${labels.of('day')}</th>
</tr>
</thead>
</table>
`;
ブラウザサポート
Intl.DisplayNamesとtype: "dateTimeField"を使用したAPIは、2022年3月以降、主要ブラウザでサポートされています。
ChromeとEdgeはバージョン99からサポートしています。Firefoxはバージョン99からサポートしています。Safariはバージョン15.4からサポートしています。
使用する前に、機能が利用可能かどうかを確認できます。
function supportsDateTimeFieldLabels() {
try {
const labels = new Intl.DisplayNames('en', { type: 'dateTimeField' });
labels.of('year');
return true;
} catch (error) {
return false;
}
}
if (supportsDateTimeFieldLabels()) {
const labels = new Intl.DisplayNames('en-US', { type: 'dateTimeField' });
console.log(labels.of('month'));
} else {
console.log('month'); // Fallback to English
}
古いブラウザの場合は、フォールバックラベルを提供する必要があります。一般的なフィールド用のシンプルなマッピングオブジェクトを作成してください。
const fallbackLabels = {
en: {
year: 'year',
month: 'month',
day: 'day',
hour: 'hour',
minute: 'minute',
second: 'second'
},
es: {
year: 'año',
month: 'mes',
day: 'día',
hour: 'hora',
minute: 'minuto',
second: 'segundo'
},
fr: {
year: 'année',
month: 'mois',
day: 'jour',
hour: 'heure',
minute: 'minute',
second: 'seconde'
}
};
function getFieldLabel(field, locale) {
if (supportsDateTimeFieldLabels()) {
const labels = new Intl.DisplayNames(locale, { type: 'dateTimeField' });
return labels.of(field);
}
const language = locale.split('-')[0];
return fallbackLabels[language]?.[field] || fallbackLabels.en[field];
}
console.log(getFieldLabel('month', 'es-ES'));
// "mes" (from API if supported, from fallback otherwise)
これにより、ネイティブローカライゼーションが利用可能な場合はそれを活用しながら、すべてのブラウザでフォームが機能することが保証されます。
月名と曜日名を取得するためのIntl.DateTimeFormat APIは、Internet Explorer 11およびすべてのモダンブラウザまで遡る、より広範なサポートがあります。ほとんどの場合、機能検出なしで使用できます。