Get Localized Month and Weekday Names in JavaScript
To list localized month or weekday names, format a reference date with Intl.DateTimeFormat(locale, { month }) (or weekday) across the range β no hard-coded English arrays. Part of Locale-Sensitive Date Comparison and Sorting.
Why this scenario is tricky
Getting localized month and weekday names is a task where the wrong instinct β keeping an array of names in your code β is exactly what causes bugs and blocks internationalization. A hard-coded ['January', 'February', β¦] is English-only, and worse, it tempts you to index into it with a zero-based month while some other code uses one-based, reintroducing off-by-one errors. The correct source of names is the platform's internationalization data, which knows every locale's month and weekday names, their abbreviations, and their grammatical forms, and stays current as that data is updated.
Intl.DateTimeFormat is that source: format a date with { month: 'long' } (or 'short', 'narrow') and it returns the month's name in the requested locale and width, and the same with { weekday: 'long' } for weekday names. The subtlety is that to enumerate all twelve months or seven weekdays you format a representative date for each, and you must pin the time zone so the representative date does not slip to an adjacent day in some zone β which is the one trap in an otherwise clean approach.
Hard-coded ['January', ...] arrays are English-only and drift from the locale's real names (some locales capitalize differently or use distinct standalone forms). Generating them from Intl gives correct names for any locale and any width.
Minimal working solution
To list the month names, format day 1 of each month with a { month: 'long', timeZone: 'UTC' } formatter: Array.from({ length: 12 }, (_, m) => fmt.format(new Date(Date.UTC(2024, m, 1)))) yields the localized names in order. Pinning timeZone: 'UTC' and constructing the representative dates with Date.UTC is essential β without it, midnight on the 1st could render as the last day of the previous month in a negative-offset zone, shifting a name. The same pattern with { weekday: 'short' } over seven consecutive days gives weekday names in whatever width you request.
Choosing the width ('long', 'short', 'narrow') matters because they serve different UI slots: full names for a dropdown, short for a compact calendar header, narrow for a dense grid. Note that 'narrow' names are not guaranteed unique β several weekdays may share a single letter in some locales β so use narrow only where space forces it and uniqueness is not required. Requesting the width you actually need from Intl is cleaner than truncating a long name yourself, which would break for locales whose abbreviations are not simple prefixes.
const fmt = new Intl.DateTimeFormat('fr-FR', { month: 'long', timeZone: 'UTC' });
const months = Array.from({ length: 12 }, (_, m) =>
fmt.format(new Date(Date.UTC(2024, m, 1)))); // ['janvier', 'fΓ©vrier', ...]
Full production version
A production helper takes the locale and the desired width and returns the ordered arrays of month and weekday names, caching the results because the names for a given locale and width never change within a session. Building them once per locale avoids repeatedly formatting the same representative dates, which matters if a calendar re-renders often. Because the names come from Intl, adding a language requires no code change β the same helper produces Japanese, Arabic, or Finnish names on request, and it respects locale conventions your own table would miss.
Two production details are worth handling. First, weekday order is itself locale-sensitive for display β some locales start the week on Sunday, others Monday or Saturday β so if you are building a calendar header, order the weekday names according to the locale's first day rather than always starting at Monday. Second, for right-to-left locales the names come out correctly but your layout must accommodate RTL; the name data is fine, the presentation is your responsibility. Keeping name generation in one Intl-backed helper means every locale-specific subtlety of the names is handled centrally, leaving only layout to the UI.
// Weekday names, starting Monday, in the 'short' width.
const fmt = new Intl.DateTimeFormat('de-DE', { weekday: 'short', timeZone: 'UTC' });
// 2024-01-01 is a Monday.
const days = Array.from({ length: 7 }, (_, i) => fmt.format(new Date(Date.UTC(2024, 0, 1 + i))));
Verification snippet
Common pitfalls
Frequently Asked Questions
How do I get month names in the user's language?
Format a reference date for each month with Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' }). Looping January through December yields the localized names, and switching 'long' to 'short' or 'narrow' changes the width.
How do I get weekday names starting on Monday?
Format seven consecutive days beginning from a date you know is a Monday (2024-01-01) with { weekday: 'short' }. Because you control the starting day, you can order the array Monday-first or Sunday-first as your UI needs.
How do I get localized month and weekday names in JavaScript?
Use Intl.DateTimeFormat with { month: 'long' } or { weekday: 'long' } (or 'short'/'narrow') and format a representative date for each. For months: Array.from({ length: 12 }, (_, m) => fmt.format(new Date(Date.UTC(2024, m, 1)))). Pin timeZone: 'UTC' and build the dates with Date.UTC so none slips to an adjacent day in a negative-offset zone. This returns the names in the requested locale and width from the platform's data.
Why not keep an array of month names in my code?
Because a hard-coded array is English-only and blocks internationalization, and indexing into it invites zero-based-versus-one-based month bugs. Intl.DateTimeFormat sources names from the platform's locale data, which covers every language's month and weekday names, their abbreviations, and grammatical forms, and stays current as the data updates β so the same code produces correct names in any locale with no table to maintain.
Why pin the time zone when generating month or weekday names?
Because you enumerate names by formatting a representative date for each month or weekday, and without a pinned zone, midnight on the 1st can render as the previous day in a negative-offset zone, shifting a name by one. Setting timeZone: 'UTC' and constructing the dates with Date.UTC keeps each representative date on the intended calendar day so the enumerated names are correct and stable.
Are narrow month or weekday names always unique?
No. Narrow names β a single letter for weekdays in many locales β can collide, so several weekdays may share the same character (for example T for Tuesday and Thursday in English). Use narrow only where space forces it and uniqueness is not required, such as a dense calendar grid where position disambiguates. For anything that must be unambiguous on its own, request the short or long width instead.
How do I order weekday names by the locale's first day of the week?
Generate the seven weekday names, then rotate the array so it starts on the locale's first day rather than always Monday. Some locales begin the week on Sunday, others Monday or Saturday, and a calendar header should follow that convention. Determine the first day from locale data or configuration and reorder the names accordingly, keeping the name generation itself in one Intl-backed helper.