From 74bd87012ceed41e59aa2530d62456b98dfa595a Mon Sep 17 00:00:00 2001 From: Tomas Vondracek Date: Fri, 9 Oct 2026 10:29:43 +0200 Subject: [PATCH] fix(admin): show dates in the backend locale Dates in the admin UI ignored the backend locale. Relative labels such as "7 days ago" and the Vue listing dates are formatted by Luxon, which was never given a locale and used the browser's. Their tooltips are rendered by the localdate filter, which without a locale falls back to the current content locale. Admin locales that are not content locales, like cs, got English. - locale.js sets Luxon's default locale from , converting Bolt codes like pt_BR to BCP 47. An invalid locale would make Luxon throw on every date it formats, so those keep the browser's locale. - uses app.request.locale, the locale the backend is translated into, instead of the user's saved locale, and outputs it as a valid language tag (pt-BR). - The relative_datetime macro and value-only date fields (media edit page) pass app.request.locale to localdate. A locale passed by the caller still takes precedence. --- assets/js/bolt.js | 1 + assets/js/locale.js | 16 ++++++++++++++++ templates/_base/layout.html.twig | 2 +- templates/_macro/_macro.html.twig | 2 +- templates/_partials/fields/date.html.twig | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 assets/js/locale.js diff --git a/assets/js/bolt.js b/assets/js/bolt.js index 11750ce904..b6f4fed7d5 100644 --- a/assets/js/bolt.js +++ b/assets/js/bolt.js @@ -7,6 +7,7 @@ Axios.defaults.headers.common = { accept: 'application/vnd.api+json', }; +import './locale'; import './jquery'; import './codemirror'; import './filters'; diff --git a/assets/js/locale.js b/assets/js/locale.js new file mode 100644 index 0000000000..ee4855768f --- /dev/null +++ b/assets/js/locale.js @@ -0,0 +1,16 @@ +import { Settings } from 'luxon'; + +/* + ** Make Luxon format dates (incl. relative ones like "7 days ago") in the locale of the Bolt admin UI, + ** instead of the browser's locale. Bolt uses locale codes like 'pt_BR', while Intl expects 'pt-BR'. + ** An invalid locale would make Luxon throw on every date it formats, so those keep the browser's locale. + */ +const locale = document.documentElement.lang.replace(/_/g, '-'); + +if (locale) { + try { + Settings.defaultLocale = Intl.getCanonicalLocales(locale)[0]; + } catch { + // Keep the browser's locale + } +} diff --git a/templates/_base/layout.html.twig b/templates/_base/layout.html.twig index f9ccf7e2c2..c0863e6394 100644 --- a/templates/_base/layout.html.twig +++ b/templates/_base/layout.html.twig @@ -1,7 +1,7 @@ {# Check if aside has value and set hasAside variable #} {% set hasAside = block('aside')|trim is not empty ? true : false %} - + diff --git a/templates/_macro/_macro.html.twig b/templates/_macro/_macro.html.twig index 6eb1993271..defb00e1a8 100644 --- a/templates/_macro/_macro.html.twig +++ b/templates/_macro/_macro.html.twig @@ -94,7 +94,7 @@ {% macro relative_datetime(datetime) %}{% apply spaceless %} - + {{- datetime|date('c') -}} {% endapply %}{% endmacro %} diff --git a/templates/_partials/fields/date.html.twig b/templates/_partials/fields/date.html.twig index b77cdd758e..ed1f649812 100644 --- a/templates/_partials/fields/date.html.twig +++ b/templates/_partials/fields/date.html.twig @@ -41,7 +41,7 @@ :errormessage='{{ errormessage|json_encode }}' > {% else %} - {{ value|localdate(format|default(null), locale|default(null)) }} + {{ value|localdate(format|default(null), locale|default(app.request.locale|default(null))) }} ({{ macro.relative_datetime(value) }}) {% endif %}