Locale and date formats
Show dates in your users' language, and replace a label Intl cannot produce.
Set locale to a BCP 47 tag and every date label is formatted by Intl.DateTimeFormat, with no bundle to install and no locale file to import. With no tag the chart prints its built-in English labels.
Show dates in another language
locale takes the tag your app already uses. Nothing else in the setup changes.
import { useState } from 'react';
import { ReactGanttChart, type Task } from '@jaeungkim/gantt-chart';
import '@jaeungkim/gantt-chart/style.css';
const initialTasks: Task[] = [
{ id: 'design', name: 'Design', parentId: null, sequence: '1',
startDate: '2026-09-01', endDate: '2026-09-12' },
{ id: 'build', name: 'Build', parentId: null, sequence: '2',
startDate: '2026-09-15', endDate: '2026-10-10' },
];
export function ProjectChart() {
const [tasks, setTasks] = useState(initialTasks);
return (
<ReactGanttChart
tasks={tasks}
onTasksChange={setTasks}
locale="ko-KR"
showTaskList
height={420}
/>
);
}The tag reaches every date the chart writes. Every formatter is built with timeZone: 'UTC', so a label never shifts into the viewer's time zone.
The task list needs no locale, because it renders task.name from your data.
An editable Start or End never reaches a formatter. It is a native <input type="date"> and stays YYYY-MM-DD whatever the tag. See Detail panel.
An empty string counts as no tag. A malformed tag such as "en_US" falls back to the built-in labels as well. Intl throws on it, and the chart catches that and warns once per tag per page load.
A well-formed but unknown tag such as "xx-YY" does not throw. Intl uses the runtime locale instead, so that typo produces no feedback.
Intl has no quarter field, so the chart assembles the quarter header itself. Q3 goes before a year string ending in a digit and after it otherwise, giving Q3 2025 and 2025년 Q3.
Replace a label with your own
formats is a record keyed by scale. Each scale takes any of the three label slots, and an omitted slot keeps the label from the layer below.
| Slot | Fills | Receives |
|---|---|---|
tick | one label per cell in the bottom header row | that cell's start date |
header | one label per group in the top header row | the group's start date |
tooltip | every other date: the hover card, the bar names a screen reader reads, the read-only Start and End in the detail panel, and the drag readout | one date at a time |
Each slot resolves on its own, from the first layer that fills it: formats[scale][slot], then locale, then the built-in English label. So formats.quarter.header beside locale="ko-KR" leaves tick and tooltip in Korean.
A fiscal year starting in April needs a quarter label Intl cannot produce.
import type { GanttFormatOverrides } from '@jaeungkim/gantt-chart';
// Module scope, so the object identity never changes between renders
export const formats: GanttFormatOverrides = {
quarter: {
header: (d) => {
// The Dayjs is in UTC mode, so d.month() is 0 to 11 in UTC
const fiscalMonth = (d.month() + 9) % 12;
const fiscalYear = d.month() >= 3 ? d.year() : d.year() - 1;
return `FY${fiscalYear} Q${Math.floor(fiscalMonth / 3) + 1}`;
},
},
};Pass it as formats={formats}. The header slot receives the group's start date, so this override always sees January, April, July or October.
The Dayjs handed to a formatter is in UTC mode, so d.format('HH:mm'), d.year(), d.month() and d.day() return UTC values. A formatter that converts to local time prints a label that disagrees with its own cell.
locale never reaches dayjs either, so d.format('MMMM') prints September for every tag.
Declare formats at module scope or in useMemo. A new object literal on every render rebuilds every formatter behind the labels, and the chart does not warn.
Neighbouring groups whose labels read alike merge into one wider cell, so an override returning one string for two groups merges them.
During a move or resize, each end of the dragged span is written into the tick cell it lands in, in a compact form of the tooltip slot. An override is not compacted, and prints as it returns.
Start the week on another day
firstDayOfWeek is a number from 0 for Sunday to 6 for Saturday. Setting it groups the week scale's top header row by week from that day, instead of by month.
The chart checks whether the prop is present, so firstDayOfWeek={0} turns grouping on. On every other scale it does nothing. There is no validation: the value goes into a modulo, so 7 behaves as 0 and -1 as 6.
A grouped week header still reads its override from formats.week.header, because the record is keyed by the scale on screen. formats.day.header has no effect there.
Shading is separate. workingWeekdays and holidays decide which days are shaded. See The timeline.
What stays in English
The three props reach dates only. Every other string the chart renders is hardcoded English.
| String | Where it appears |
|---|---|
Add task | the button under the task list |
Start, End, Duration, Progress, Depends on | the detail panel's field captions |
12h, 3d | the duration shorthand in the hover card and the panel |
Gantt chart, Task name, Timeline, Resize task list, Task details, Close task details | the accessible names of the chart, its two columns, the splitter, the panel and its close button |
summary, to, % complete | the connectives inside every bar's accessible name |
<scale> scale | the announcement after a keyboard scale change |
<name> deleted, <name> cannot be changed, <name> moved to 2 of 5 under <parent> | the announcements after a keyboard edit |
Only the dates inside those strings go through the localized tooltip formatter. The announcements themselves are in Keyboard and screen readers.
renderDetail replaces the panel's whole body, captions and close button included, so your app can translate that text. The panel's own accessible name stays English.
The chart renders no scale picker, so your app writes and translates its option text. See Imperative API.
Before you integrate
localeandformatschange how a date is displayed, never how one is parsed.startDateandendDateare read as UTC whatever the tag, as Task data describes.- Positions and labels are UTC, and no prop renders them in local time. Shift your own date strings before passing them in.
- Right-to-left layout is unsupported. Nothing sets or reads
dir, so a right-to-left app mirrors the chart itself. - Number and plural formatting are absent.
Intl.NumberFormat,Intl.RelativeTimeFormatandIntl.PluralRulesare never constructed, so progress is announced as a raw number followed by% complete.
Props and defaults: GanttProps. The built-in format string for each scale is in Scale and theme types.