¿Cómo mostrar etiquetas localizadas para campos de fecha y hora?
Utiliza Intl.DisplayNames para obtener etiquetas de campos e Intl.DateTimeFormat para obtener nombres de meses y días de la semana en cualquier idioma.
Introducción
Cuando construyes formularios de entrada de fecha y hora, necesitas etiquetas que describan cada campo. Un selector de fecha necesita etiquetas como "Mes", "Año" y "Día". Un selector de hora necesita etiquetas como "Hora" y "Minuto". Estas etiquetas deben aparecer en el idioma del usuario.
Codificar estas etiquetas en inglés no funciona para aplicaciones internacionales. Un usuario francés espera ver "Mois" y "Année", mientras que un usuario español busca "Mes" y "Año". Necesitas un sistema que proporcione estas etiquetas automáticamente en cualquier idioma.
JavaScript proporciona dos APIs complementarias para esto. La API Intl.DisplayNames te da etiquetas de campo como "Mes" y "Año". La API Intl.DateTimeFormat te da los valores reales para esos campos, como nombres de meses y nombres de días de la semana.
Entendiendo las etiquetas de campos de fecha y hora
Las interfaces de fecha y hora requieren dos tipos de etiquetas. Las etiquetas de campo describen qué tipo de datos va en cada entrada. Los valores de campo son los datos reales que aparecen en desplegables y selectores.
Las etiquetas de campo incluyen palabras como "Año", "Mes", "Día", "Hora", "Minuto" y "Segundo". Estas etiquetan los campos del formulario en sí.
Los valores de campo incluyen nombres de meses como "Enero" y "Febrero", nombres de días de la semana como "Lunes" y "Martes", y etiquetas de período como "AM" y "PM". Estos pueblan los desplegables y listas de selección.
Un formulario de fecha completo necesita ambos tipos de etiquetas.
<label>Mes</label>
<select>
<option>Enero</option>
<option>Febrero</option>
<option>Marzo</option>
<!-- más meses -->
</select>
<label>Año</label>
<input type="number" />
Tanto "Mes" como "Enero" necesitan localización, pero requieren enfoques diferentes.
Obtener etiquetas de campo con Intl.DisplayNames
El constructor Intl.DisplayNames con type: "dateTimeField" devuelve etiquetas localizadas para componentes de fecha y hora.
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"
El método of() toma un código de campo y devuelve su etiqueta localizada. La etiqueta coincide con las convenciones del locale especificado.
Puedes obtener etiquetas en cualquier idioma cambiando el locale.
// Etiquetas en español
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"
// Etiquetas en francés
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"
// Etiquetas en japonés
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'));
// "分"
Cada locale proporciona etiquetas en su propio idioma y escritura.
Códigos de campo de fecha y hora disponibles
La API Intl.DisplayNames admite estos códigos de campo.
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"
Utilice estos códigos para obtener etiquetas para cualquier componente de fecha u hora que necesite su interfaz.
Obtener nombres de meses localizados
La API Intl.DateTimeFormat proporciona nombres de meses para poblar menús desplegables y listas de selección. Cree un formateador con la opción month establecida en "long", luego formatee fechas que representen cada mes.
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"]
La función crea fechas para cada mes del año y las formatea para extraer el nombre del mes. Establecer timeZone: 'UTC' garantiza resultados consistentes en todas las zonas horarias.
Puede obtener nombres de meses en cualquier idioma.
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月"]
Cada configuración regional formatea los nombres de los meses según sus propias convenciones.
Control de la longitud del nombre del mes
La opción month acepta diferentes valores que controlan la longitud de los nombres de los meses.
El valor "long" devuelve los nombres completos de los meses.
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"
El valor "short" devuelve nombres de meses abreviados.
const shortFormatter = new Intl.DateTimeFormat('en-US', {
month: 'short',
timeZone: 'UTC'
});
console.log(shortFormatter.format(date));
// "Jan"
El valor "narrow" devuelve los nombres de meses más cortos posibles, típicamente una sola letra.
const narrowFormatter = new Intl.DateTimeFormat('en-US', {
month: 'narrow',
timeZone: 'UTC'
});
console.log(narrowFormatter.format(date));
// "J"
Utilice "narrow" con cuidado, ya que varios meses pueden compartir la misma letra. En inglés, enero (January), junio (June) y julio (July) producen todos "J".
Obtener nombres de días de la semana localizados
Utilice el mismo patrón para obtener nombres de días de la semana. Configure la opción weekday para controlar el formato.
function getWeekdayNames(locale, format = 'long') {
const formatter = new Intl.DateTimeFormat(locale, {
weekday: format,
timeZone: 'UTC'
});
const weekdays = [];
// Comienza desde un domingo (el 2 de enero de 2000 fue domingo)
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"]
Puede obtener nombres de días de la semana en cualquier idioma.
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'));
// ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]
Diferentes configuraciones regionales pueden comenzar la semana en diferentes días. Esta función siempre devuelve de domingo a sábado en orden.
Obtener etiquetas de período localizadas
Las etiquetas de período son los indicadores AM y PM utilizados con el formato de 12 horas. Utiliza formatToParts() para extraer estas etiquetas.
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: "午後" }
El método formatToParts() devuelve un array de objetos que representan cada parte del tiempo formateado. El objeto con type: "dayPeriod" contiene la etiqueta AM o PM.
Algunos locales utilizan el formato de 24 horas por defecto y no incluyen etiquetas de período. Puedes forzar el formato de 12 horas con la opción hour12: true.
Construir un formulario de fecha completamente localizado
Combina todas estas técnicas para crear un formulario de entrada de fecha totalmente localizado.
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
};
}
// Función auxiliar del ejemplo anterior
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"
Esta estructura proporciona todo lo necesario para renderizar un formulario de fecha localizado en 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'));
// Renderiza formulario con etiquetas y nombres de meses en inglés
console.log(renderDateForm('fr-FR'));
// Renderiza formulario con etiquetas y nombres de meses en francés
El formulario se adapta automáticamente a cualquier locale que especifiques.
Construir un formulario de hora localizado
Aplica el mismo enfoque para crear un formulario de entrada de hora con selectores de hora, minuto y período.
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 }
]
};
}
// Función auxiliar del ejemplo anterior
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: "午後" }]
Esto proporciona todos los datos necesarios para renderizar un selector de hora localizado.
Cuándo usar etiquetas de campos de fecha y hora
Las etiquetas de campos de fecha y hora aparecen en varios tipos de interfaces.
Selectores personalizados de fecha y hora
Utiliza etiquetas localizadas al construir selectores de fecha, widgets de calendario o selectores de hora.
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')
};
Etiquetas de campos de formulario
Aplica etiquetas a los campos de entrada estándar para datos de fecha y hora.
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');
Etiquetas de accesibilidad
Proporciona etiquetas ARIA localizadas para lectores de pantalla y tecnología de asistencia.
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'));
Encabezados de tablas de datos
Etiqueta las columnas en tablas que muestran componentes de fecha y hora.
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>
`;
Compatibilidad con navegadores
La API Intl.DisplayNames con type: "dateTimeField" es compatible desde marzo de 2022 en los principales navegadores.
Chrome y Edge la soportan desde la versión 99. Firefox la soporta desde la versión 99. Safari la soporta desde la versión 15.4.
Puedes verificar si la función está disponible antes de usarla.
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 a inglés
}
Para navegadores más antiguos, necesitas proporcionar etiquetas alternativas. Crea un objeto de mapeo simple para campos comunes.
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" (desde la API si es compatible, desde la alternativa en caso contrario)
Esto asegura que tus formularios funcionen en todos los navegadores mientras aprovechas la localización nativa cuando está disponible.
La API Intl.DateTimeFormat para obtener nombres de meses y días de la semana tiene un soporte más amplio, que se remonta a Internet Explorer 11 y todos los navegadores modernos. Puedes usarla sin detección de características en la mayoría de los casos.