Popular features/Dynamically switch locale
Loading live demo…from dhtmlxcode.com
Popular features
Dynamically switch locale
Switch the interface language while the page is running, and mirror the layout for right to left languages.
gantt.plugins({ tooltip: true });
gantt.config.date_format = "%Y-%m-%d";
gantt.config.auto_types = true;
gantt.config.open_tree_initially = true;
gantt.config.grid_resize = true;
gantt.config.work_time = true;
gantt.config.row_height = 34;
gantt.config.bar_height = 22;
/* ---------------------------------------------------------------------------
* LOCALES
*
* `code` follows the values used by the localization demo. English keeps the
* empty value from that list, while gantt.i18n receives "en" internally.
* ------------------------------------------------------------------------ */
let LOCALES = [
{ code: "af", label: "Afrikaans", flag: "🇿🇦" },
{ code: "sq", label: "Albanian", flag: "🇦🇱" },
{ code: "ar", label: "Arabic", flag: "🇸🇦", rtl: true },
{ code: "be", label: "Belarusian", flag: "🇧🇾" },
{ code: "bs", label: "Bosnian", flag: "🇧🇦" },
{ code: "bg", label: "Bulgarian", flag: "🇧🇬" },
{ code: "ca", label: "Catalan", flag: "🇪🇸" },
{ code: "cn", label: "Chinese (Simplified)", flag: "🇨🇳" },
{ code: "zh_hk", label: "Chinese (Hong Kong)", flag: "ðŸ‡ðŸ‡°" },
{ code: "zh_tw", label: "Chinese (Taiwan)", flag: "🇹🇼" },
{ code: "hr", label: "Croatian", flag: "ðŸ‡ðŸ‡·" },
{ code: "cs", label: "Czech", flag: "🇨🇿" },
{ code: "da", label: "Danish", flag: "🇩🇰" },
{ code: "nl", label: "Dutch", flag: "🇳🇱" },
{ code: "nl_be", label: "Dutch (Belgium)", flag: "🇧🇪" },
{ code: "", label: "English", flag: "🇬🇧" },
{ code: "et", label: "Estonian", flag: "🇪🇪" },
{ code: "fi", label: "Finnish", flag: "🇫🇮" },
{ code: "fr", label: "French (France)", flag: "🇫🇷" },
{ code: "fr_ca", label: "French (Canada)", flag: "🇨🇦" },
{ code: "de", label: "German (Germany)", flag: "🇩🇪" },
{ code: "de_at", label: "German (Austria)", flag: "🇦🇹" },
{ code: "el", label: "Greek", flag: "🇬🇷" },
{ code: "he", label: "Hebrew", flag: "🇮🇱", rtl: true },
{ code: "hu", label: "Hungarian", flag: "ðŸ‡ðŸ‡º" },
{ code: "id", label: "Indonesian", flag: "🇮🇩" },
{ code: "ga", label: "Irish", flag: "🇮🇪" },
{ code: "it", label: "Italian", flag: "🇮🇹" },
{ code: "jp", label: "Japanese", flag: "🇯🇵" },
{ code: "kr", label: "Korean", flag: "🇰🇷" },
{ code: "lv", label: "Latvian", flag: "🇱🇻" },
{ code: "lt", label: "Lithuanian", flag: "🇱🇹" },
{ code: "mk", label: "Macedonian", flag: "🇲🇰" },
{ code: "ms", label: "Malay", flag: "🇲🇾" },
{ code: "nb", label: "Norwegian (Bokmål)", flag: "🇳🇴" },
{ code: "no", label: "Norwegian (Nynorsk)", flag: "🇳🇴" },
{ code: "fa", label: "Persian", flag: "🇮🇷", rtl: true },
{ code: "pl", label: "Polish", flag: "🇵🇱" },
{ code: "pt", label: "Portuguese (Portugal)", flag: "🇵🇹" },
{ code: "pt_br", label: "Portuguese (Brazil)", flag: "🇧🇷" },
{ code: "ro", label: "Romanian", flag: "🇷🇴" },
{ code: "ru", label: "Russian", flag: "🇷🇺" },
{ code: "sk", label: "Slovak", flag: "🇸🇰" },
{ code: "si", label: "Slovenian", flag: "🇸🇮" },
{ code: "es", label: "Spanish (Spain)", flag: "🇪🇸" },
{ code: "es_mx", label: "Spanish (Mexico)", flag: "🇲🇽" },
{ code: "sv", label: "Swedish", flag: "🇸🇪" },
{ code: "th", label: "Thai", flag: "🇹ðŸ‡" },
{ code: "tr", label: "Turkish", flag: "🇹🇷" },
{ code: "ua", label: "Ukrainian", flag: "🇺🇦" },
{ code: "vi", label: "Vietnamese", flag: "🇻🇳" }
];
let localeByCode = {};
LOCALES.forEach(function (locale) {
localeByCode[locale.code] = locale;
});
let manualRtl = null; // null = follow locale; true/false = user override
/* ---------------------------------------------------------------------------
* DATASET
* ------------------------------------------------------------------------ */
let demoData = {
tasks: [
{ id: 1, text: "Website Relaunch", type: "project", open: true },
{ id: 11, text: "Kickoff & scope", parent: 1, start_date: "2026-07-06", duration: 3, progress: 0.6 },
{ id: 12, text: "UX & visual design", parent: 1, start_date: "2026-07-09", duration: 6, progress: 0.3 },
{ id: 13, text: "Front-end build", parent: 1, start_date: "2026-07-17", duration: 8, progress: 0 },
{ id: 14, text: "Content & SEO", parent: 1, start_date: "2026-07-09", duration: 5, progress: 0 },
{ id: 15, text: "QA & fixes", parent: 1, start_date: "2026-07-29", duration: 4, progress: 0 },
{ id: 16, text: "Launch prep", parent: 1, start_date: "2026-08-04", duration: 2, progress: 0 },
{ id: 17, text: "Go live", parent: 1, type: "milestone", start_date: "2026-08-06", progress: 0 },
{ id: 2, text: "Marketing & Launch Campaign", type: "project", open: true },
{ id: 21, text: "Campaign kickoff & brief", parent: 2, start_date: "2026-07-06", duration: 2, progress: 0.5 },
{ id: 22, text: "Brand messaging & assets", parent: 2, start_date: "2026-07-08", duration: 5, progress: 0.2 },
{ id: 23, text: "Landing page copy", parent: 2, start_date: "2026-07-08", duration: 4, progress: 0 },
{ id: 24, text: "Paid media plan", parent: 2, start_date: "2026-07-08", duration: 3, progress: 0 },
{ id: 25, text: "Social content calendar", parent: 2, start_date: "2026-07-13", duration: 4, progress: 0 },
{ id: 26, text: "Email sequence draft", parent: 2, start_date: "2026-07-17", duration: 3, progress: 0 },
{ id: 27, text: "Influencer outreach", parent: 2, start_date: "2026-07-22", duration: 4, progress: 0 },
{ id: 28, text: "Ad creative production", parent: 2, start_date: "2026-07-15", duration: 5, progress: 0 },
{ id: 29, text: "Landing page build", parent: 2, start_date: "2026-07-14", duration: 6, progress: 0 },
{ id: 30, text: "Analytics & tracking setup", parent: 2, start_date: "2026-07-08", duration: 2, progress: 0 },
{ id: 31, text: "Internal review & sign-off", parent: 2, start_date: "2026-07-10", duration: 2, progress: 0 },
{ id: 32, text: "Press release draft", parent: 2, start_date: "2026-07-14", duration: 3, progress: 0 },
{ id: 33, text: "Legal & compliance check", parent: 2, start_date: "2026-07-17", duration: 2, progress: 0 },
{ id: 34, text: "Channel scheduling", parent: 2, start_date: "2026-07-28", duration: 2, progress: 0 },
{ id: 35, text: "Final QA pass", parent: 2, start_date: "2026-07-30", duration: 3, progress: 0 },
{ id: 36, text: "Launch day coordination", parent: 2, start_date: "2026-08-04", duration: 1, progress: 0 },
{ id: 37, text: "Campaign live", parent: 2, type: "milestone", start_date: "2026-08-05", progress: 0 }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" }, { id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 11, target: 14, type: "0" }, { id: 4, source: 13, target: 15, type: "0" },
{ id: 5, source: 14, target: 15, type: "0" }, { id: 6, source: 15, target: 16, type: "0" },
{ id: 7, source: 16, target: 17, type: "0" },
{ id: 8, source: 21, target: 22, type: "0" }, { id: 9, source: 21, target: 23, type: "0" },
{ id: 10, source: 21, target: 24, type: "0" }, { id: 11, source: 21, target: 30, type: "0" },
{ id: 12, source: 22, target: 28, type: "0" }, { id: 13, source: 23, target: 29, type: "0" },
{ id: 14, source: 24, target: 25, type: "0" }, { id: 15, source: 25, target: 26, type: "0" },
{ id: 16, source: 26, target: 27, type: "0" }, { id: 17, source: 27, target: 34, type: "0" },
{ id: 18, source: 30, target: 31, type: "0" }, { id: 19, source: 31, target: 32, type: "0" },
{ id: 20, source: 32, target: 33, type: "0" }, { id: 21, source: 28, target: 35, type: "0" },
{ id: 22, source: 29, target: 35, type: "0" }, { id: 23, source: 34, target: 35, type: "0" },
{ id: 24, source: 33, target: 35, type: "0" }, { id: 25, source: 35, target: 36, type: "0" },
{ id: 26, source: 36, target: 37, type: "0" }
]
};
/* ---------------------------------------------------------------------------
* GRID (labels filled from the active locale dictionary)
* ------------------------------------------------------------------------ */
gantt.config.columns = [
{ name: "text", tree: true, width: 220, resize: true },
{ name: "start_date", align: "center", width: 110, resize: true, template: function (task) {
return task.type === gantt.config.types.project ? "" : gantt.templates.date_grid(task.start_date, task);
}},
{ name: "duration", align: "center", width: 70, resize: true, template: function (task) {
return task.type === gantt.config.types.milestone ? "" : task.duration;
}},
{ name: "add", width: 40, resize: true }
];
gantt.templates.timeline_cell_class = function (task, date) {
if (!gantt.isWorkTime({ date, task })) {
return "gantt_weekend";
}
};
gantt.config.scale_height = 50;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "day", step: 1, format: "%d %D" }
];
/* ---------------------------------------------------------------------------
* LOCALE SWITCHING
* ------------------------------------------------------------------------ */
function renderLocaleMenu() {
document.getElementById("locale_menu").innerHTML = LOCALES.map(function (locale) {
return "<div class='dhx_menu__item locale_item' data-code='" + locale.code + "'>" +
"<span class='flag'>" + locale.flag + "</span>" +
"<span>" + locale.label + "</span></div>";
}).join("");
document.querySelectorAll("#locale_menu .locale_item").forEach(function (el) {
el.addEventListener("click", function () { applyLocale(el.getAttribute("data-code")); DHX.dd.closeAll(); });
});
}
function applyLocale(code) {
let locale = localeByCode[code];
if (!locale) {
return;
}
let ganttLocale = code || "en";
gantt.i18n.setLocale(ganttLocale);
DHX.ui.setText("locale_flag", locale.flag);
DHX.ui.setText("locale_label", locale.label);
let rtl = manualRtl == null ? !!locale.rtl : manualRtl;
setDirection(rtl);
gantt.render();
}
/* ---------------------------------------------------------------------------
* RTL
* ------------------------------------------------------------------------ */
function setDirection(rtl) {
gantt.config.rtl = rtl;
DHX.toggle.setWrap(document.getElementById("rtl_switch").closest(".dhx_toggle"), rtl);
}
function toggleRtl() {
manualRtl = !(gantt.config.rtl);
setDirection(manualRtl);
gantt.render();
}
/* ---------------------------------------------------------------------------
* INIT
* ------------------------------------------------------------------------ */
DHX.dd.initClickOutside();
renderLocaleMenu();
gantt.init("gantt_here");
gantt.parse(demoData);<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Localization & RTL — dhtmlxGantt</title>
<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
<script src="../common/demo-controls/dhx_controls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body { display: flex; flex-direction: column; }
#gantt_here { width: 100%; flex: 1 1 auto; min-height: 0; }
.gantt_weekend { background: rgba(0,0,0,.035); }
.locale_menu {
min-width: 270px;
max-height: min(70vh, 520px);
overflow-y: auto;
}
.locale_item { display: flex; align-items: center; gap: 10px; }
.locale_item .flag,
.locale_button_flag {
flex: none;
width: 22px;
font-size: 17px;
line-height: 1;
text-align: center;
}
.dhx_header__center .dhx_toggle { height: 32px; }
</style>
</head>
<body>
<!-- -- Header --------------------------------------------------------------- -->
<div class="dhx_header">
<div class="dhx_header__center">
<div class="dhx_dd" id="dd_locale">
<button class="dhx_btn dhx_btn--surface" onclick="DHX.dd.toggle('dd_locale')">
<span class="locale_button_flag" id="locale_flag">🇬🇧</span>
<span id="locale_label">English</span>
<span class="dhx_caret"><svg class="dhx_ic dhx_ic--sm" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg></span>
</button>
<div class="dhx_menu locale_menu" id="locale_menu"></div>
</div>
<label class="dhx_toggle" onclick="toggleRtl()">
<span>RTL</span><span class="dhx_toggle__track" id="rtl_switch"></span>
</label>
</div>
</div>
<div id="gantt_here"></div>
</body>
</html>