From e3b3a77e9d417caeaa5ef61e81527e05a7d7d441 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 01:24:15 +0530 Subject: [PATCH 1/8] fix: resolve greeting timezone mismatch and add night greeting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The greeting text and displayed time were computed from different timezone sources — hour used browser local time while timeString used user profile timezone — causing mismatched greeting and display. Also adds a "night" bucket (0–4 AM) so late-night users no longer see "Good morning", and guards against invalid timezone strings crashing the component via a try/catch fallback to browser local time. Co-Authored-By: Claude Sonnet 4.6 --- .../core/components/home/user-greetings.tsx | 22 +++++++++++++++---- .../core/components/user/user-greetings.tsx | 22 +++++++++++++++---- packages/i18n/src/locales/cs/translations.ts | 1 + packages/i18n/src/locales/de/translations.ts | 1 + packages/i18n/src/locales/en/translations.ts | 1 + packages/i18n/src/locales/es/translations.ts | 1 + packages/i18n/src/locales/fr/translations.ts | 1 + packages/i18n/src/locales/id/translations.ts | 1 + packages/i18n/src/locales/it/translations.ts | 1 + packages/i18n/src/locales/ja/translations.ts | 1 + packages/i18n/src/locales/ko/translations.ts | 1 + packages/i18n/src/locales/pl/translations.ts | 1 + .../i18n/src/locales/pt-BR/translations.ts | 1 + packages/i18n/src/locales/ro/translations.ts | 1 + packages/i18n/src/locales/ru/translations.ts | 1 + packages/i18n/src/locales/sk/translations.ts | 1 + .../i18n/src/locales/tr-TR/translations.ts | 1 + packages/i18n/src/locales/ua/translations.ts | 1 + .../i18n/src/locales/vi-VN/translations.ts | 1 + .../i18n/src/locales/zh-CN/translations.ts | 1 + .../i18n/src/locales/zh-TW/translations.ts | 1 + 21 files changed, 55 insertions(+), 8 deletions(-) diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index 8d5d6cc1e86..1f70f985347 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -22,28 +22,42 @@ export function UserGreetingsView(props: IUserGreetingsView) { // store hooks const { t } = useTranslation(); + const userTimezone = (() => { + if (!user?.user_timezone) return undefined; + try { + Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); + return user.user_timezone; + } catch { + return undefined; + } + })(); + const hour = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, hour12: false, hour: "numeric", }).format(currentTime); const date = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, month: "short", day: "numeric", }).format(currentTime); const weekDay = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, weekday: "long", }).format(currentTime); const timeString = new Intl.DateTimeFormat("en-US", { - timeZone: user?.user_timezone, - hour12: false, // Use 24-hour format + timeZone: userTimezone, + hour12: false, hour: "2-digit", minute: "2-digit", }).format(currentTime); - const greeting = parseInt(hour, 10) < 12 ? "morning" : parseInt(hour, 10) < 18 ? "afternoon" : "evening"; + const hourNum = parseInt(hour, 10); + const greeting = hourNum < 5 ? "night" : hourNum < 12 ? "morning" : hourNum < 18 ? "afternoon" : "evening"; return (
@@ -51,7 +65,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name}
-
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙️"}
+
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
{weekDay}, {date} {timeString}
diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index ff00a23ca93..02475451648 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -22,28 +22,42 @@ export function UserGreetingsView(props: IUserGreetingsView) { // store hooks const { t } = useTranslation(); + const userTimezone = (() => { + if (!user?.user_timezone) return undefined; + try { + Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); + return user.user_timezone; + } catch { + return undefined; + } + })(); + const hour = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, hour12: false, hour: "numeric", }).format(currentTime); const date = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, month: "short", day: "numeric", }).format(currentTime); const weekDay = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, weekday: "long", }).format(currentTime); const timeString = new Intl.DateTimeFormat("en-US", { - timeZone: user?.user_timezone, - hour12: false, // Use 24-hour format + timeZone: userTimezone, + hour12: false, hour: "2-digit", minute: "2-digit", }).format(currentTime); - const greeting = parseInt(hour, 10) < 12 ? "morning" : parseInt(hour, 10) < 18 ? "afternoon" : "evening"; + const hourNum = parseInt(hour, 10); + const greeting = hourNum < 5 ? "night" : hourNum < 12 ? "morning" : hourNum < 18 ? "afternoon" : "evening"; return (
@@ -51,7 +65,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name}
-
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙️"}
+
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
{weekDay}, {date} {timeString}
diff --git a/packages/i18n/src/locales/cs/translations.ts b/packages/i18n/src/locales/cs/translations.ts index d2a818f3b89..7916b15e149 100644 --- a/packages/i18n/src/locales/cs/translations.ts +++ b/packages/i18n/src/locales/cs/translations.ts @@ -489,6 +489,7 @@ export default { morning: "ráno", afternoon: "odpoledne", evening: "večer", + night: "night", show_all: "Zobrazit vše", show_less: "Zobrazit méně", no_data_yet: "Zatím žádná data", diff --git a/packages/i18n/src/locales/de/translations.ts b/packages/i18n/src/locales/de/translations.ts index db4925f1f92..4d8189c53c9 100644 --- a/packages/i18n/src/locales/de/translations.ts +++ b/packages/i18n/src/locales/de/translations.ts @@ -502,6 +502,7 @@ export default { morning: "Morgen", afternoon: "Nachmittag", evening: "Abend", + night: "night", show_all: "Alle anzeigen", show_less: "Weniger anzeigen", no_data_yet: "Noch keine Daten", diff --git a/packages/i18n/src/locales/en/translations.ts b/packages/i18n/src/locales/en/translations.ts index 2885fd3f0a1..c35a5f8efb0 100644 --- a/packages/i18n/src/locales/en/translations.ts +++ b/packages/i18n/src/locales/en/translations.ts @@ -322,6 +322,7 @@ export default { morning: "morning", afternoon: "afternoon", evening: "evening", + night: "night", show_all: "Show all", show_less: "Show less", no_data_yet: "No Data yet", diff --git a/packages/i18n/src/locales/es/translations.ts b/packages/i18n/src/locales/es/translations.ts index 5ed015d06ca..9b81ca02d90 100644 --- a/packages/i18n/src/locales/es/translations.ts +++ b/packages/i18n/src/locales/es/translations.ts @@ -502,6 +502,7 @@ export default { morning: "días", afternoon: "tardes", evening: "noches", + night: "night", show_all: "Mostrar todo", show_less: "Mostrar menos", no_data_yet: "Aún no hay datos", diff --git a/packages/i18n/src/locales/fr/translations.ts b/packages/i18n/src/locales/fr/translations.ts index f7ace4c6914..f787ae7fe17 100644 --- a/packages/i18n/src/locales/fr/translations.ts +++ b/packages/i18n/src/locales/fr/translations.ts @@ -498,6 +498,7 @@ export default { morning: "matin", afternoon: "après-midi", evening: "soir", + night: "night", show_all: "Tout afficher", show_less: "Afficher moins", no_data_yet: "Pas encore de données", diff --git a/packages/i18n/src/locales/id/translations.ts b/packages/i18n/src/locales/id/translations.ts index f933b215e04..1674aad3332 100644 --- a/packages/i18n/src/locales/id/translations.ts +++ b/packages/i18n/src/locales/id/translations.ts @@ -494,6 +494,7 @@ export default { morning: "pagi", afternoon: "siang", evening: "malam", + night: "night", show_all: "Tampilkan semua", show_less: "Tampilkan lebih sedikit", no_data_yet: "Belum ada data", diff --git a/packages/i18n/src/locales/it/translations.ts b/packages/i18n/src/locales/it/translations.ts index a0cb1b5c54f..f4fe9e435e9 100644 --- a/packages/i18n/src/locales/it/translations.ts +++ b/packages/i18n/src/locales/it/translations.ts @@ -495,6 +495,7 @@ export default { morning: "Mattina", afternoon: "Pomeriggio", evening: "Sera", + night: "night", show_all: "Mostra tutto", show_less: "Mostra meno", no_data_yet: "Nessun dato disponibile", diff --git a/packages/i18n/src/locales/ja/translations.ts b/packages/i18n/src/locales/ja/translations.ts index 6d79979904e..ae63dd91395 100644 --- a/packages/i18n/src/locales/ja/translations.ts +++ b/packages/i18n/src/locales/ja/translations.ts @@ -488,6 +488,7 @@ export default { morning: "ございます", afternoon: "こんにちは", evening: "こんばんは", + night: "night", show_all: "すべて表示", show_less: "表示を減らす", no_data_yet: "まだデータがありません", diff --git a/packages/i18n/src/locales/ko/translations.ts b/packages/i18n/src/locales/ko/translations.ts index cf7e68d36a2..7cbd2e5f7c7 100644 --- a/packages/i18n/src/locales/ko/translations.ts +++ b/packages/i18n/src/locales/ko/translations.ts @@ -482,6 +482,7 @@ export default { morning: "아침", afternoon: "오후", evening: "저녁", + night: "night", show_all: "모두 보기", show_less: "간략히 보기", no_data_yet: "아직 데이터 없음", diff --git a/packages/i18n/src/locales/pl/translations.ts b/packages/i18n/src/locales/pl/translations.ts index 225859e9635..2722b1c94cf 100644 --- a/packages/i18n/src/locales/pl/translations.ts +++ b/packages/i18n/src/locales/pl/translations.ts @@ -488,6 +488,7 @@ export default { morning: "rano", afternoon: "po południu", evening: "wieczorem", + night: "night", show_all: "Pokaż wszystko", show_less: "Pokaż mniej", no_data_yet: "Brak danych", diff --git a/packages/i18n/src/locales/pt-BR/translations.ts b/packages/i18n/src/locales/pt-BR/translations.ts index 5282692a17b..27ca48ed5f3 100644 --- a/packages/i18n/src/locales/pt-BR/translations.ts +++ b/packages/i18n/src/locales/pt-BR/translations.ts @@ -498,6 +498,7 @@ export default { morning: "manhã", afternoon: "tarde", evening: "noite", + night: "night", show_all: "Mostrar tudo", show_less: "Mostrar menos", no_data_yet: "Nenhum dado ainda", diff --git a/packages/i18n/src/locales/ro/translations.ts b/packages/i18n/src/locales/ro/translations.ts index 09a3312b6c8..a94666aafc3 100644 --- a/packages/i18n/src/locales/ro/translations.ts +++ b/packages/i18n/src/locales/ro/translations.ts @@ -495,6 +495,7 @@ export default { morning: "dimineața", afternoon: "după-amiaza", evening: "seara", + night: "night", show_all: "Arată tot", show_less: "Arată mai puțin", no_data_yet: "Nicio dată încă", diff --git a/packages/i18n/src/locales/ru/translations.ts b/packages/i18n/src/locales/ru/translations.ts index 52024d06cd1..91b92ee872c 100644 --- a/packages/i18n/src/locales/ru/translations.ts +++ b/packages/i18n/src/locales/ru/translations.ts @@ -497,6 +497,7 @@ export default { morning: "утра", afternoon: "дня", evening: "вечера", + night: "night", show_all: "Показать все", show_less: "Свернуть", no_data_yet: "Нет данных", diff --git a/packages/i18n/src/locales/sk/translations.ts b/packages/i18n/src/locales/sk/translations.ts index 9a0c42e8e9e..d4d07346626 100644 --- a/packages/i18n/src/locales/sk/translations.ts +++ b/packages/i18n/src/locales/sk/translations.ts @@ -489,6 +489,7 @@ export default { morning: "ráno", afternoon: "popoludnie", evening: "večer", + night: "night", show_all: "Zobraziť všetko", show_less: "Zobraziť menej", no_data_yet: "Zatiaľ žiadne dáta", diff --git a/packages/i18n/src/locales/tr-TR/translations.ts b/packages/i18n/src/locales/tr-TR/translations.ts index 4e60e0cfbec..a25ac546bde 100644 --- a/packages/i18n/src/locales/tr-TR/translations.ts +++ b/packages/i18n/src/locales/tr-TR/translations.ts @@ -490,6 +490,7 @@ export default { morning: "sabah", afternoon: "öğleden sonra", evening: "akşam", + night: "night", show_all: "Tümünü göster", show_less: "Daha az göster", no_data_yet: "Henüz veri yok", diff --git a/packages/i18n/src/locales/ua/translations.ts b/packages/i18n/src/locales/ua/translations.ts index 0c949cf0567..796a909469f 100644 --- a/packages/i18n/src/locales/ua/translations.ts +++ b/packages/i18n/src/locales/ua/translations.ts @@ -497,6 +497,7 @@ export default { morning: "ранку", afternoon: "дня", evening: "вечора", + night: "night", show_all: "Показати все", show_less: "Показати менше", no_data_yet: "Поки що немає даних", diff --git a/packages/i18n/src/locales/vi-VN/translations.ts b/packages/i18n/src/locales/vi-VN/translations.ts index 0cc12c75ea0..5a1f6aa7817 100644 --- a/packages/i18n/src/locales/vi-VN/translations.ts +++ b/packages/i18n/src/locales/vi-VN/translations.ts @@ -495,6 +495,7 @@ export default { morning: "Buổi sáng", afternoon: "Buổi chiều", evening: "Buổi tối", + night: "night", show_all: "Hiển thị tất cả", show_less: "Hiển thị ít hơn", no_data_yet: "Chưa có dữ liệu", diff --git a/packages/i18n/src/locales/zh-CN/translations.ts b/packages/i18n/src/locales/zh-CN/translations.ts index 585ae13aa68..d750bed7267 100644 --- a/packages/i18n/src/locales/zh-CN/translations.ts +++ b/packages/i18n/src/locales/zh-CN/translations.ts @@ -477,6 +477,7 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", + night: "night", show_all: "显示全部", show_less: "显示更少", no_data_yet: "暂无数据", diff --git a/packages/i18n/src/locales/zh-TW/translations.ts b/packages/i18n/src/locales/zh-TW/translations.ts index 1fcf528fb1d..9d3cdb44c82 100644 --- a/packages/i18n/src/locales/zh-TW/translations.ts +++ b/packages/i18n/src/locales/zh-TW/translations.ts @@ -476,6 +476,7 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", + night: "night", show_all: "顯示全部", show_less: "顯示較少", no_data_yet: "尚無資料", From df7aea759b41f6c6381687f1c4a774680800e933 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 01:36:04 +0530 Subject: [PATCH 2/8] fix: use hourCycle h23 to prevent midnight returning 24 and add night translations Replace hour12: false with hourCycle: "h23" on hour and timeString formatters so midnight returns "0" instead of "24" in Chromium-based browsers (h24 cycle). This prevented the night greeting from firing at 00:00. Also adds proper localized translations for the night key across all 18 non-English locales instead of the English placeholder. Co-Authored-By: Claude Sonnet 4.6 --- apps/web/core/components/home/user-greetings.tsx | 4 ++-- apps/web/core/components/user/user-greetings.tsx | 4 ++-- packages/i18n/src/locales/cs/translations.ts | 2 +- packages/i18n/src/locales/de/translations.ts | 2 +- packages/i18n/src/locales/es/translations.ts | 2 +- packages/i18n/src/locales/fr/translations.ts | 2 +- packages/i18n/src/locales/id/translations.ts | 2 +- packages/i18n/src/locales/it/translations.ts | 2 +- packages/i18n/src/locales/ja/translations.ts | 2 +- packages/i18n/src/locales/ko/translations.ts | 2 +- packages/i18n/src/locales/pl/translations.ts | 2 +- packages/i18n/src/locales/pt-BR/translations.ts | 2 +- packages/i18n/src/locales/ro/translations.ts | 2 +- packages/i18n/src/locales/ru/translations.ts | 2 +- packages/i18n/src/locales/sk/translations.ts | 2 +- packages/i18n/src/locales/tr-TR/translations.ts | 2 +- packages/i18n/src/locales/ua/translations.ts | 2 +- packages/i18n/src/locales/vi-VN/translations.ts | 2 +- packages/i18n/src/locales/zh-CN/translations.ts | 2 +- packages/i18n/src/locales/zh-TW/translations.ts | 2 +- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index 1f70f985347..ff05a67d0e2 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -34,7 +34,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { const hour = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, - hour12: false, + hourCycle: "h23", hour: "numeric", }).format(currentTime); @@ -51,7 +51,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { const timeString = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, - hour12: false, + hourCycle: "h23", hour: "2-digit", minute: "2-digit", }).format(currentTime); diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index 02475451648..d58ad3ba802 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -34,7 +34,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { const hour = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, - hour12: false, + hourCycle: "h23", hour: "numeric", }).format(currentTime); @@ -51,7 +51,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { const timeString = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, - hour12: false, + hourCycle: "h23", hour: "2-digit", minute: "2-digit", }).format(currentTime); diff --git a/packages/i18n/src/locales/cs/translations.ts b/packages/i18n/src/locales/cs/translations.ts index 7916b15e149..1cd1f9c7896 100644 --- a/packages/i18n/src/locales/cs/translations.ts +++ b/packages/i18n/src/locales/cs/translations.ts @@ -489,7 +489,7 @@ export default { morning: "ráno", afternoon: "odpoledne", evening: "večer", - night: "night", + night: "noc", show_all: "Zobrazit vše", show_less: "Zobrazit méně", no_data_yet: "Zatím žádná data", diff --git a/packages/i18n/src/locales/de/translations.ts b/packages/i18n/src/locales/de/translations.ts index 4d8189c53c9..59c062a3f00 100644 --- a/packages/i18n/src/locales/de/translations.ts +++ b/packages/i18n/src/locales/de/translations.ts @@ -502,7 +502,7 @@ export default { morning: "Morgen", afternoon: "Nachmittag", evening: "Abend", - night: "night", + night: "Nacht", show_all: "Alle anzeigen", show_less: "Weniger anzeigen", no_data_yet: "Noch keine Daten", diff --git a/packages/i18n/src/locales/es/translations.ts b/packages/i18n/src/locales/es/translations.ts index 9b81ca02d90..92f4745e2c4 100644 --- a/packages/i18n/src/locales/es/translations.ts +++ b/packages/i18n/src/locales/es/translations.ts @@ -502,7 +502,7 @@ export default { morning: "días", afternoon: "tardes", evening: "noches", - night: "night", + night: "noche", show_all: "Mostrar todo", show_less: "Mostrar menos", no_data_yet: "Aún no hay datos", diff --git a/packages/i18n/src/locales/fr/translations.ts b/packages/i18n/src/locales/fr/translations.ts index f787ae7fe17..acd6cf74976 100644 --- a/packages/i18n/src/locales/fr/translations.ts +++ b/packages/i18n/src/locales/fr/translations.ts @@ -498,7 +498,7 @@ export default { morning: "matin", afternoon: "après-midi", evening: "soir", - night: "night", + night: "nuit", show_all: "Tout afficher", show_less: "Afficher moins", no_data_yet: "Pas encore de données", diff --git a/packages/i18n/src/locales/id/translations.ts b/packages/i18n/src/locales/id/translations.ts index 1674aad3332..bb2648beada 100644 --- a/packages/i18n/src/locales/id/translations.ts +++ b/packages/i18n/src/locales/id/translations.ts @@ -494,7 +494,7 @@ export default { morning: "pagi", afternoon: "siang", evening: "malam", - night: "night", + night: "dini hari", show_all: "Tampilkan semua", show_less: "Tampilkan lebih sedikit", no_data_yet: "Belum ada data", diff --git a/packages/i18n/src/locales/it/translations.ts b/packages/i18n/src/locales/it/translations.ts index f4fe9e435e9..3735813138c 100644 --- a/packages/i18n/src/locales/it/translations.ts +++ b/packages/i18n/src/locales/it/translations.ts @@ -495,7 +495,7 @@ export default { morning: "Mattina", afternoon: "Pomeriggio", evening: "Sera", - night: "night", + night: "notte", show_all: "Mostra tutto", show_less: "Mostra meno", no_data_yet: "Nessun dato disponibile", diff --git a/packages/i18n/src/locales/ja/translations.ts b/packages/i18n/src/locales/ja/translations.ts index ae63dd91395..24f17f7c17a 100644 --- a/packages/i18n/src/locales/ja/translations.ts +++ b/packages/i18n/src/locales/ja/translations.ts @@ -488,7 +488,7 @@ export default { morning: "ございます", afternoon: "こんにちは", evening: "こんばんは", - night: "night", + night: "夜", show_all: "すべて表示", show_less: "表示を減らす", no_data_yet: "まだデータがありません", diff --git a/packages/i18n/src/locales/ko/translations.ts b/packages/i18n/src/locales/ko/translations.ts index 7cbd2e5f7c7..ce82cc00365 100644 --- a/packages/i18n/src/locales/ko/translations.ts +++ b/packages/i18n/src/locales/ko/translations.ts @@ -482,7 +482,7 @@ export default { morning: "아침", afternoon: "오후", evening: "저녁", - night: "night", + night: "밤", show_all: "모두 보기", show_less: "간략히 보기", no_data_yet: "아직 데이터 없음", diff --git a/packages/i18n/src/locales/pl/translations.ts b/packages/i18n/src/locales/pl/translations.ts index 2722b1c94cf..c39d472cfd2 100644 --- a/packages/i18n/src/locales/pl/translations.ts +++ b/packages/i18n/src/locales/pl/translations.ts @@ -488,7 +488,7 @@ export default { morning: "rano", afternoon: "po południu", evening: "wieczorem", - night: "night", + night: "noc", show_all: "Pokaż wszystko", show_less: "Pokaż mniej", no_data_yet: "Brak danych", diff --git a/packages/i18n/src/locales/pt-BR/translations.ts b/packages/i18n/src/locales/pt-BR/translations.ts index 27ca48ed5f3..40597b19e10 100644 --- a/packages/i18n/src/locales/pt-BR/translations.ts +++ b/packages/i18n/src/locales/pt-BR/translations.ts @@ -498,7 +498,7 @@ export default { morning: "manhã", afternoon: "tarde", evening: "noite", - night: "night", + night: "noite", show_all: "Mostrar tudo", show_less: "Mostrar menos", no_data_yet: "Nenhum dado ainda", diff --git a/packages/i18n/src/locales/ro/translations.ts b/packages/i18n/src/locales/ro/translations.ts index a94666aafc3..64632b72e53 100644 --- a/packages/i18n/src/locales/ro/translations.ts +++ b/packages/i18n/src/locales/ro/translations.ts @@ -495,7 +495,7 @@ export default { morning: "dimineața", afternoon: "după-amiaza", evening: "seara", - night: "night", + night: "noapte", show_all: "Arată tot", show_less: "Arată mai puțin", no_data_yet: "Nicio dată încă", diff --git a/packages/i18n/src/locales/ru/translations.ts b/packages/i18n/src/locales/ru/translations.ts index 91b92ee872c..2090ad3d271 100644 --- a/packages/i18n/src/locales/ru/translations.ts +++ b/packages/i18n/src/locales/ru/translations.ts @@ -497,7 +497,7 @@ export default { morning: "утра", afternoon: "дня", evening: "вечера", - night: "night", + night: "ночи", show_all: "Показать все", show_less: "Свернуть", no_data_yet: "Нет данных", diff --git a/packages/i18n/src/locales/sk/translations.ts b/packages/i18n/src/locales/sk/translations.ts index d4d07346626..74d60f0e0b1 100644 --- a/packages/i18n/src/locales/sk/translations.ts +++ b/packages/i18n/src/locales/sk/translations.ts @@ -489,7 +489,7 @@ export default { morning: "ráno", afternoon: "popoludnie", evening: "večer", - night: "night", + night: "noc", show_all: "Zobraziť všetko", show_less: "Zobraziť menej", no_data_yet: "Zatiaľ žiadne dáta", diff --git a/packages/i18n/src/locales/tr-TR/translations.ts b/packages/i18n/src/locales/tr-TR/translations.ts index a25ac546bde..79437900842 100644 --- a/packages/i18n/src/locales/tr-TR/translations.ts +++ b/packages/i18n/src/locales/tr-TR/translations.ts @@ -490,7 +490,7 @@ export default { morning: "sabah", afternoon: "öğleden sonra", evening: "akşam", - night: "night", + night: "gece", show_all: "Tümünü göster", show_less: "Daha az göster", no_data_yet: "Henüz veri yok", diff --git a/packages/i18n/src/locales/ua/translations.ts b/packages/i18n/src/locales/ua/translations.ts index 796a909469f..3c1dfc828a6 100644 --- a/packages/i18n/src/locales/ua/translations.ts +++ b/packages/i18n/src/locales/ua/translations.ts @@ -497,7 +497,7 @@ export default { morning: "ранку", afternoon: "дня", evening: "вечора", - night: "night", + night: "ночі", show_all: "Показати все", show_less: "Показати менше", no_data_yet: "Поки що немає даних", diff --git a/packages/i18n/src/locales/vi-VN/translations.ts b/packages/i18n/src/locales/vi-VN/translations.ts index 5a1f6aa7817..d2a56f23a8b 100644 --- a/packages/i18n/src/locales/vi-VN/translations.ts +++ b/packages/i18n/src/locales/vi-VN/translations.ts @@ -495,7 +495,7 @@ export default { morning: "Buổi sáng", afternoon: "Buổi chiều", evening: "Buổi tối", - night: "night", + night: "Buổi đêm", show_all: "Hiển thị tất cả", show_less: "Hiển thị ít hơn", no_data_yet: "Chưa có dữ liệu", diff --git a/packages/i18n/src/locales/zh-CN/translations.ts b/packages/i18n/src/locales/zh-CN/translations.ts index d750bed7267..35c26afe1c3 100644 --- a/packages/i18n/src/locales/zh-CN/translations.ts +++ b/packages/i18n/src/locales/zh-CN/translations.ts @@ -477,7 +477,7 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", - night: "night", + night: "晚安", show_all: "显示全部", show_less: "显示更少", no_data_yet: "暂无数据", diff --git a/packages/i18n/src/locales/zh-TW/translations.ts b/packages/i18n/src/locales/zh-TW/translations.ts index 9d3cdb44c82..15d88143d83 100644 --- a/packages/i18n/src/locales/zh-TW/translations.ts +++ b/packages/i18n/src/locales/zh-TW/translations.ts @@ -476,7 +476,7 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", - night: "night", + night: "晚安", show_all: "顯示全部", show_less: "顯示較少", no_data_yet: "尚無資料", From 388502a1e8f91e83e42795d0ffc197cf5da1ea24 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 01:47:04 +0530 Subject: [PATCH 3/8] fix: memoize timezone validation and log invalid timezone warning Replaced IIFE with useMemo keyed on user_timezone so the validation runs once per timezone change rather than on every render tick. Also adds console.warn when an invalid timezone is detected so it is diagnosable rather than silently discarded. Fixed missing `new` keyword on Intl.DateTimeFormat validation call. Co-Authored-By: Claude Sonnet 4.6 --- apps/web/core/components/home/user-greetings.tsx | 10 ++++++---- apps/web/core/components/user/user-greetings.tsx | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index ff05a67d0e2..cec5c01aa79 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -5,6 +5,7 @@ */ // plane types +import { useMemo } from "react"; import { useTranslation } from "@plane/i18n"; import type { IUser } from "@plane/types"; // plane ui @@ -22,15 +23,16 @@ export function UserGreetingsView(props: IUserGreetingsView) { // store hooks const { t } = useTranslation(); - const userTimezone = (() => { + const userTimezone = useMemo(() => { if (!user?.user_timezone) return undefined; try { - Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); + new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); return user.user_timezone; - } catch { + } catch (e) { + console.warn(`[UserGreetingsView] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, e); return undefined; } - })(); + }, [user?.user_timezone]); const hour = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index d58ad3ba802..49b98ce3164 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -5,6 +5,7 @@ */ // plane types +import { useMemo } from "react"; import { useTranslation } from "@plane/i18n"; // hooks import type { IUser } from "@plane/types"; @@ -22,15 +23,16 @@ export function UserGreetingsView(props: IUserGreetingsView) { // store hooks const { t } = useTranslation(); - const userTimezone = (() => { + const userTimezone = useMemo(() => { if (!user?.user_timezone) return undefined; try { - Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); + new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); return user.user_timezone; - } catch { + } catch (e) { + console.warn(`[UserGreetingsView] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, e); return undefined; } - })(); + }, [user?.user_timezone]); const hour = new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, From 9e47a4308faf4efbcd92ea078130b65ddd4c30c6 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 01:56:15 +0530 Subject: [PATCH 4/8] fix: use phrase-level greeting keys to fix grammatical composition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces {t("good")} {t(greeting)}, {name} composition with a single t("greetings.", { first_name, last_name }) call. Fragment composition was grammatically incorrect in many locales — e.g. Russian "Доброго ночи" instead of "Доброй ночи", German "Guten Nacht" instead of "Gute Nacht", Czech "Dobrý noc" instead of "Dobrou noc". Adds greetings.{morning,afternoon,evening,night} nested keys with proper full-phrase translations across all 19 locale files. Co-Authored-By: Claude Sonnet 4.6 --- apps/web/core/components/home/user-greetings.tsx | 2 +- apps/web/core/components/user/user-greetings.tsx | 2 +- packages/i18n/src/locales/cs/translations.ts | 6 ++++++ packages/i18n/src/locales/de/translations.ts | 6 ++++++ packages/i18n/src/locales/en/translations.ts | 6 ++++++ packages/i18n/src/locales/es/translations.ts | 6 ++++++ packages/i18n/src/locales/fr/translations.ts | 6 ++++++ packages/i18n/src/locales/id/translations.ts | 6 ++++++ packages/i18n/src/locales/it/translations.ts | 6 ++++++ packages/i18n/src/locales/ja/translations.ts | 6 ++++++ packages/i18n/src/locales/ko/translations.ts | 6 ++++++ packages/i18n/src/locales/pl/translations.ts | 6 ++++++ packages/i18n/src/locales/pt-BR/translations.ts | 6 ++++++ packages/i18n/src/locales/ro/translations.ts | 6 ++++++ packages/i18n/src/locales/ru/translations.ts | 6 ++++++ packages/i18n/src/locales/sk/translations.ts | 6 ++++++ packages/i18n/src/locales/tr-TR/translations.ts | 6 ++++++ packages/i18n/src/locales/ua/translations.ts | 6 ++++++ packages/i18n/src/locales/vi-VN/translations.ts | 6 ++++++ packages/i18n/src/locales/zh-CN/translations.ts | 6 ++++++ packages/i18n/src/locales/zh-TW/translations.ts | 6 ++++++ 21 files changed, 116 insertions(+), 2 deletions(-) diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index cec5c01aa79..ab2710d1437 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -64,7 +64,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { return (

- {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name} + {t(`greetings.${greeting}`, { first_name: user?.first_name ?? "", last_name: user?.last_name ?? "" })}

{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index 49b98ce3164..f5996b3bd24 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -64,7 +64,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { return (

- {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name} + {t(`greetings.${greeting}`, { first_name: user?.first_name ?? "", last_name: user?.last_name ?? "" })}

{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
diff --git a/packages/i18n/src/locales/cs/translations.ts b/packages/i18n/src/locales/cs/translations.ts index 1cd1f9c7896..7c501c06790 100644 --- a/packages/i18n/src/locales/cs/translations.ts +++ b/packages/i18n/src/locales/cs/translations.ts @@ -490,6 +490,12 @@ export default { afternoon: "odpoledne", evening: "večer", night: "noc", + greetings: { + morning: "Dobré ráno, {first_name} {last_name}", + afternoon: "Dobré odpoledne, {first_name} {last_name}", + evening: "Dobrý večer, {first_name} {last_name}", + night: "Dobrou noc, {first_name} {last_name}", + }, show_all: "Zobrazit vše", show_less: "Zobrazit méně", no_data_yet: "Zatím žádná data", diff --git a/packages/i18n/src/locales/de/translations.ts b/packages/i18n/src/locales/de/translations.ts index 59c062a3f00..6c9acd1121e 100644 --- a/packages/i18n/src/locales/de/translations.ts +++ b/packages/i18n/src/locales/de/translations.ts @@ -503,6 +503,12 @@ export default { afternoon: "Nachmittag", evening: "Abend", night: "Nacht", + greetings: { + morning: "Guten Morgen, {first_name} {last_name}", + afternoon: "Guten Nachmittag, {first_name} {last_name}", + evening: "Guten Abend, {first_name} {last_name}", + night: "Gute Nacht, {first_name} {last_name}", + }, show_all: "Alle anzeigen", show_less: "Weniger anzeigen", no_data_yet: "Noch keine Daten", diff --git a/packages/i18n/src/locales/en/translations.ts b/packages/i18n/src/locales/en/translations.ts index c35a5f8efb0..86ff3ca70b0 100644 --- a/packages/i18n/src/locales/en/translations.ts +++ b/packages/i18n/src/locales/en/translations.ts @@ -323,6 +323,12 @@ export default { afternoon: "afternoon", evening: "evening", night: "night", + greetings: { + morning: "Good morning, {first_name} {last_name}", + afternoon: "Good afternoon, {first_name} {last_name}", + evening: "Good evening, {first_name} {last_name}", + night: "Good night, {first_name} {last_name}", + }, show_all: "Show all", show_less: "Show less", no_data_yet: "No Data yet", diff --git a/packages/i18n/src/locales/es/translations.ts b/packages/i18n/src/locales/es/translations.ts index 92f4745e2c4..09d11871ade 100644 --- a/packages/i18n/src/locales/es/translations.ts +++ b/packages/i18n/src/locales/es/translations.ts @@ -503,6 +503,12 @@ export default { afternoon: "tardes", evening: "noches", night: "noche", + greetings: { + morning: "Buenos días, {first_name} {last_name}", + afternoon: "Buenas tardes, {first_name} {last_name}", + evening: "Buenas noches, {first_name} {last_name}", + night: "Buenas noches, {first_name} {last_name}", + }, show_all: "Mostrar todo", show_less: "Mostrar menos", no_data_yet: "Aún no hay datos", diff --git a/packages/i18n/src/locales/fr/translations.ts b/packages/i18n/src/locales/fr/translations.ts index acd6cf74976..0e325d8fa58 100644 --- a/packages/i18n/src/locales/fr/translations.ts +++ b/packages/i18n/src/locales/fr/translations.ts @@ -499,6 +499,12 @@ export default { afternoon: "après-midi", evening: "soir", night: "nuit", + greetings: { + morning: "Bonjour, {first_name} {last_name}", + afternoon: "Bon après-midi, {first_name} {last_name}", + evening: "Bonsoir, {first_name} {last_name}", + night: "Bonne nuit, {first_name} {last_name}", + }, show_all: "Tout afficher", show_less: "Afficher moins", no_data_yet: "Pas encore de données", diff --git a/packages/i18n/src/locales/id/translations.ts b/packages/i18n/src/locales/id/translations.ts index bb2648beada..b123d373ea5 100644 --- a/packages/i18n/src/locales/id/translations.ts +++ b/packages/i18n/src/locales/id/translations.ts @@ -495,6 +495,12 @@ export default { afternoon: "siang", evening: "malam", night: "dini hari", + greetings: { + morning: "Selamat pagi, {first_name} {last_name}", + afternoon: "Selamat siang, {first_name} {last_name}", + evening: "Selamat malam, {first_name} {last_name}", + night: "Selamat malam, {first_name} {last_name}", + }, show_all: "Tampilkan semua", show_less: "Tampilkan lebih sedikit", no_data_yet: "Belum ada data", diff --git a/packages/i18n/src/locales/it/translations.ts b/packages/i18n/src/locales/it/translations.ts index 3735813138c..16f5d93a806 100644 --- a/packages/i18n/src/locales/it/translations.ts +++ b/packages/i18n/src/locales/it/translations.ts @@ -496,6 +496,12 @@ export default { afternoon: "Pomeriggio", evening: "Sera", night: "notte", + greetings: { + morning: "Buongiorno, {first_name} {last_name}", + afternoon: "Buon pomeriggio, {first_name} {last_name}", + evening: "Buonasera, {first_name} {last_name}", + night: "Buonanotte, {first_name} {last_name}", + }, show_all: "Mostra tutto", show_less: "Mostra meno", no_data_yet: "Nessun dato disponibile", diff --git a/packages/i18n/src/locales/ja/translations.ts b/packages/i18n/src/locales/ja/translations.ts index 24f17f7c17a..4141226ed82 100644 --- a/packages/i18n/src/locales/ja/translations.ts +++ b/packages/i18n/src/locales/ja/translations.ts @@ -489,6 +489,12 @@ export default { afternoon: "こんにちは", evening: "こんばんは", night: "夜", + greetings: { + morning: "おはようございます、{first_name} {last_name}", + afternoon: "こんにちは、{first_name} {last_name}", + evening: "こんばんは、{first_name} {last_name}", + night: "おやすみなさい、{first_name} {last_name}", + }, show_all: "すべて表示", show_less: "表示を減らす", no_data_yet: "まだデータがありません", diff --git a/packages/i18n/src/locales/ko/translations.ts b/packages/i18n/src/locales/ko/translations.ts index ce82cc00365..c7cb8c82ab1 100644 --- a/packages/i18n/src/locales/ko/translations.ts +++ b/packages/i18n/src/locales/ko/translations.ts @@ -483,6 +483,12 @@ export default { afternoon: "오후", evening: "저녁", night: "밤", + greetings: { + morning: "좋은 아침, {first_name} {last_name}", + afternoon: "좋은 오후, {first_name} {last_name}", + evening: "좋은 저녁, {first_name} {last_name}", + night: "좋은 밤, {first_name} {last_name}", + }, show_all: "모두 보기", show_less: "간략히 보기", no_data_yet: "아직 데이터 없음", diff --git a/packages/i18n/src/locales/pl/translations.ts b/packages/i18n/src/locales/pl/translations.ts index c39d472cfd2..154741626c5 100644 --- a/packages/i18n/src/locales/pl/translations.ts +++ b/packages/i18n/src/locales/pl/translations.ts @@ -489,6 +489,12 @@ export default { afternoon: "po południu", evening: "wieczorem", night: "noc", + greetings: { + morning: "Dzień dobry, {first_name} {last_name}", + afternoon: "Dzień dobry, {first_name} {last_name}", + evening: "Dobry wieczór, {first_name} {last_name}", + night: "Dobranoc, {first_name} {last_name}", + }, show_all: "Pokaż wszystko", show_less: "Pokaż mniej", no_data_yet: "Brak danych", diff --git a/packages/i18n/src/locales/pt-BR/translations.ts b/packages/i18n/src/locales/pt-BR/translations.ts index 40597b19e10..9f5184fddf1 100644 --- a/packages/i18n/src/locales/pt-BR/translations.ts +++ b/packages/i18n/src/locales/pt-BR/translations.ts @@ -499,6 +499,12 @@ export default { afternoon: "tarde", evening: "noite", night: "noite", + greetings: { + morning: "Bom dia, {first_name} {last_name}", + afternoon: "Boa tarde, {first_name} {last_name}", + evening: "Boa noite, {first_name} {last_name}", + night: "Boa noite, {first_name} {last_name}", + }, show_all: "Mostrar tudo", show_less: "Mostrar menos", no_data_yet: "Nenhum dado ainda", diff --git a/packages/i18n/src/locales/ro/translations.ts b/packages/i18n/src/locales/ro/translations.ts index 64632b72e53..ae473148477 100644 --- a/packages/i18n/src/locales/ro/translations.ts +++ b/packages/i18n/src/locales/ro/translations.ts @@ -496,6 +496,12 @@ export default { afternoon: "după-amiaza", evening: "seara", night: "noapte", + greetings: { + morning: "Bună dimineața, {first_name} {last_name}", + afternoon: "Bună ziua, {first_name} {last_name}", + evening: "Bună seara, {first_name} {last_name}", + night: "Noapte bună, {first_name} {last_name}", + }, show_all: "Arată tot", show_less: "Arată mai puțin", no_data_yet: "Nicio dată încă", diff --git a/packages/i18n/src/locales/ru/translations.ts b/packages/i18n/src/locales/ru/translations.ts index 2090ad3d271..1379e81119c 100644 --- a/packages/i18n/src/locales/ru/translations.ts +++ b/packages/i18n/src/locales/ru/translations.ts @@ -498,6 +498,12 @@ export default { afternoon: "дня", evening: "вечера", night: "ночи", + greetings: { + morning: "Доброе утро, {first_name} {last_name}", + afternoon: "Добрый день, {first_name} {last_name}", + evening: "Добрый вечер, {first_name} {last_name}", + night: "Доброй ночи, {first_name} {last_name}", + }, show_all: "Показать все", show_less: "Свернуть", no_data_yet: "Нет данных", diff --git a/packages/i18n/src/locales/sk/translations.ts b/packages/i18n/src/locales/sk/translations.ts index 74d60f0e0b1..d286c5b274f 100644 --- a/packages/i18n/src/locales/sk/translations.ts +++ b/packages/i18n/src/locales/sk/translations.ts @@ -490,6 +490,12 @@ export default { afternoon: "popoludnie", evening: "večer", night: "noc", + greetings: { + morning: "Dobré ráno, {first_name} {last_name}", + afternoon: "Dobré popoludnie, {first_name} {last_name}", + evening: "Dobrý večer, {first_name} {last_name}", + night: "Dobrú noc, {first_name} {last_name}", + }, show_all: "Zobraziť všetko", show_less: "Zobraziť menej", no_data_yet: "Zatiaľ žiadne dáta", diff --git a/packages/i18n/src/locales/tr-TR/translations.ts b/packages/i18n/src/locales/tr-TR/translations.ts index 79437900842..a5e19e91695 100644 --- a/packages/i18n/src/locales/tr-TR/translations.ts +++ b/packages/i18n/src/locales/tr-TR/translations.ts @@ -491,6 +491,12 @@ export default { afternoon: "öğleden sonra", evening: "akşam", night: "gece", + greetings: { + morning: "Günaydın, {first_name} {last_name}", + afternoon: "İyi günler, {first_name} {last_name}", + evening: "İyi akşamlar, {first_name} {last_name}", + night: "İyi geceler, {first_name} {last_name}", + }, show_all: "Tümünü göster", show_less: "Daha az göster", no_data_yet: "Henüz veri yok", diff --git a/packages/i18n/src/locales/ua/translations.ts b/packages/i18n/src/locales/ua/translations.ts index 3c1dfc828a6..7dc5200e0c8 100644 --- a/packages/i18n/src/locales/ua/translations.ts +++ b/packages/i18n/src/locales/ua/translations.ts @@ -498,6 +498,12 @@ export default { afternoon: "дня", evening: "вечора", night: "ночі", + greetings: { + morning: "Добрий ранок, {first_name} {last_name}", + afternoon: "Добрий день, {first_name} {last_name}", + evening: "Добрий вечір, {first_name} {last_name}", + night: "Доброї ночі, {first_name} {last_name}", + }, show_all: "Показати все", show_less: "Показати менше", no_data_yet: "Поки що немає даних", diff --git a/packages/i18n/src/locales/vi-VN/translations.ts b/packages/i18n/src/locales/vi-VN/translations.ts index d2a56f23a8b..61b22d502a3 100644 --- a/packages/i18n/src/locales/vi-VN/translations.ts +++ b/packages/i18n/src/locales/vi-VN/translations.ts @@ -496,6 +496,12 @@ export default { afternoon: "Buổi chiều", evening: "Buổi tối", night: "Buổi đêm", + greetings: { + morning: "Chào buổi sáng, {first_name} {last_name}", + afternoon: "Chào buổi chiều, {first_name} {last_name}", + evening: "Chào buổi tối, {first_name} {last_name}", + night: "Chúc ngủ ngon, {first_name} {last_name}", + }, show_all: "Hiển thị tất cả", show_less: "Hiển thị ít hơn", no_data_yet: "Chưa có dữ liệu", diff --git a/packages/i18n/src/locales/zh-CN/translations.ts b/packages/i18n/src/locales/zh-CN/translations.ts index 35c26afe1c3..460820b39ce 100644 --- a/packages/i18n/src/locales/zh-CN/translations.ts +++ b/packages/i18n/src/locales/zh-CN/translations.ts @@ -478,6 +478,12 @@ export default { afternoon: "下午", evening: "晚上", night: "晚安", + greetings: { + morning: "早上好,{first_name} {last_name}", + afternoon: "下午好,{first_name} {last_name}", + evening: "晚上好,{first_name} {last_name}", + night: "晚安,{first_name} {last_name}", + }, show_all: "显示全部", show_less: "显示更少", no_data_yet: "暂无数据", diff --git a/packages/i18n/src/locales/zh-TW/translations.ts b/packages/i18n/src/locales/zh-TW/translations.ts index 15d88143d83..82b6c0d7777 100644 --- a/packages/i18n/src/locales/zh-TW/translations.ts +++ b/packages/i18n/src/locales/zh-TW/translations.ts @@ -477,6 +477,12 @@ export default { afternoon: "下午", evening: "晚上", night: "晚安", + greetings: { + morning: "早安,{first_name} {last_name}", + afternoon: "午安,{first_name} {last_name}", + evening: "晚上好,{first_name} {last_name}", + night: "晚安,{first_name} {last_name}", + }, show_all: "顯示全部", show_less: "顯示較少", no_data_yet: "尚無資料", From bab77e64429f5c352e0bae244ff1c4c3a701e507 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 09:25:55 +0530 Subject: [PATCH 5/8] fix: remove night greeting bucket and extract shared useGreeting hook Per maintainer feedback, "Good night" is a farewell not a greeting. Simplifies to three buckets: morning (5-11), afternoon (12-16), evening (17-4). Extracts duplicated timezone validation, Intl formatting, and daypart logic into a shared useGreeting hook to eliminate future drift between the two identical components. Removes greetings.night key from all 19 locale files. Co-Authored-By: Claude Sonnet 4.6 --- .../core/components/home/user-greetings.tsx | 46 +------------- .../core/components/user/user-greetings.tsx | 48 +-------------- apps/web/core/hooks/use-greeting.ts | 60 +++++++++++++++++++ packages/i18n/src/locales/cs/translations.ts | 1 - packages/i18n/src/locales/de/translations.ts | 1 - packages/i18n/src/locales/en/translations.ts | 1 - packages/i18n/src/locales/es/translations.ts | 1 - packages/i18n/src/locales/fr/translations.ts | 1 - packages/i18n/src/locales/id/translations.ts | 1 - packages/i18n/src/locales/it/translations.ts | 1 - packages/i18n/src/locales/ja/translations.ts | 1 - packages/i18n/src/locales/ko/translations.ts | 1 - packages/i18n/src/locales/pl/translations.ts | 1 - .../i18n/src/locales/pt-BR/translations.ts | 1 - packages/i18n/src/locales/ro/translations.ts | 1 - packages/i18n/src/locales/ru/translations.ts | 1 - packages/i18n/src/locales/sk/translations.ts | 1 - .../i18n/src/locales/tr-TR/translations.ts | 1 - packages/i18n/src/locales/ua/translations.ts | 1 - .../i18n/src/locales/vi-VN/translations.ts | 1 - .../i18n/src/locales/zh-CN/translations.ts | 1 - .../i18n/src/locales/zh-TW/translations.ts | 1 - 22 files changed, 65 insertions(+), 108 deletions(-) create mode 100644 apps/web/core/hooks/use-greeting.ts diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index ab2710d1437..a5b1f6c07ea 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -5,12 +5,10 @@ */ // plane types -import { useMemo } from "react"; import { useTranslation } from "@plane/i18n"; import type { IUser } from "@plane/types"; -// plane ui // hooks -import { useCurrentTime } from "@/hooks/use-current-time"; +import { useGreeting } from "@/hooks/use-greeting"; export interface IUserGreetingsView { user: IUser; @@ -18,49 +16,9 @@ export interface IUserGreetingsView { export function UserGreetingsView(props: IUserGreetingsView) { const { user } = props; - // current time hook - const { currentTime } = useCurrentTime(); - // store hooks + const { greeting, timeString, weekDay, date } = useGreeting(user); const { t } = useTranslation(); - const userTimezone = useMemo(() => { - if (!user?.user_timezone) return undefined; - try { - new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); - return user.user_timezone; - } catch (e) { - console.warn(`[UserGreetingsView] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, e); - return undefined; - } - }, [user?.user_timezone]); - - const hour = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "numeric", - }).format(currentTime); - - const date = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - month: "short", - day: "numeric", - }).format(currentTime); - - const weekDay = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - weekday: "long", - }).format(currentTime); - - const timeString = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "2-digit", - minute: "2-digit", - }).format(currentTime); - - const hourNum = parseInt(hour, 10); - const greeting = hourNum < 5 ? "night" : hourNum < 12 ? "morning" : hourNum < 18 ? "afternoon" : "evening"; - return (

diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index f5996b3bd24..a5b1f6c07ea 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -5,12 +5,10 @@ */ // plane types -import { useMemo } from "react"; import { useTranslation } from "@plane/i18n"; -// hooks import type { IUser } from "@plane/types"; -import { useCurrentTime } from "@/hooks/use-current-time"; -// types +// hooks +import { useGreeting } from "@/hooks/use-greeting"; export interface IUserGreetingsView { user: IUser; @@ -18,49 +16,9 @@ export interface IUserGreetingsView { export function UserGreetingsView(props: IUserGreetingsView) { const { user } = props; - // current time hook - const { currentTime } = useCurrentTime(); - // store hooks + const { greeting, timeString, weekDay, date } = useGreeting(user); const { t } = useTranslation(); - const userTimezone = useMemo(() => { - if (!user?.user_timezone) return undefined; - try { - new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); - return user.user_timezone; - } catch (e) { - console.warn(`[UserGreetingsView] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, e); - return undefined; - } - }, [user?.user_timezone]); - - const hour = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "numeric", - }).format(currentTime); - - const date = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - month: "short", - day: "numeric", - }).format(currentTime); - - const weekDay = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - weekday: "long", - }).format(currentTime); - - const timeString = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "2-digit", - minute: "2-digit", - }).format(currentTime); - - const hourNum = parseInt(hour, 10); - const greeting = hourNum < 5 ? "night" : hourNum < 12 ? "morning" : hourNum < 18 ? "afternoon" : "evening"; - return (

diff --git a/apps/web/core/hooks/use-greeting.ts b/apps/web/core/hooks/use-greeting.ts new file mode 100644 index 00000000000..e0dff46ec14 --- /dev/null +++ b/apps/web/core/hooks/use-greeting.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useMemo } from "react"; +import type { IUser } from "@plane/types"; +import { useCurrentTime } from "@/hooks/use-current-time"; + +export type TGreeting = "morning" | "afternoon" | "evening"; + +export const useGreeting = (user: IUser) => { + const { currentTime } = useCurrentTime(); + + const userTimezone = useMemo(() => { + if (!user?.user_timezone) return undefined; + try { + new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); + return user.user_timezone; + } catch (e) { + console.warn( + `[useGreeting] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, + e + ); + return undefined; + } + }, [user?.user_timezone]); + + const hour = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, + hourCycle: "h23", + hour: "numeric", + }).format(currentTime); + + const date = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, + month: "short", + day: "numeric", + }).format(currentTime); + + const weekDay = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, + weekday: "long", + }).format(currentTime); + + const timeString = new Intl.DateTimeFormat("en-US", { + timeZone: userTimezone, + hourCycle: "h23", + hour: "2-digit", + minute: "2-digit", + }).format(currentTime); + + const hourNum = parseInt(hour, 10); + // 5–11: morning, 12–16: afternoon, 17–4: evening + const greeting: TGreeting = + hourNum >= 5 && hourNum < 12 ? "morning" : hourNum >= 12 && hourNum < 17 ? "afternoon" : "evening"; + + return { greeting, timeString, weekDay, date }; +}; diff --git a/packages/i18n/src/locales/cs/translations.ts b/packages/i18n/src/locales/cs/translations.ts index 7c501c06790..c13ad957a14 100644 --- a/packages/i18n/src/locales/cs/translations.ts +++ b/packages/i18n/src/locales/cs/translations.ts @@ -494,7 +494,6 @@ export default { morning: "Dobré ráno, {first_name} {last_name}", afternoon: "Dobré odpoledne, {first_name} {last_name}", evening: "Dobrý večer, {first_name} {last_name}", - night: "Dobrou noc, {first_name} {last_name}", }, show_all: "Zobrazit vše", show_less: "Zobrazit méně", diff --git a/packages/i18n/src/locales/de/translations.ts b/packages/i18n/src/locales/de/translations.ts index 6c9acd1121e..3decf23af0d 100644 --- a/packages/i18n/src/locales/de/translations.ts +++ b/packages/i18n/src/locales/de/translations.ts @@ -507,7 +507,6 @@ export default { morning: "Guten Morgen, {first_name} {last_name}", afternoon: "Guten Nachmittag, {first_name} {last_name}", evening: "Guten Abend, {first_name} {last_name}", - night: "Gute Nacht, {first_name} {last_name}", }, show_all: "Alle anzeigen", show_less: "Weniger anzeigen", diff --git a/packages/i18n/src/locales/en/translations.ts b/packages/i18n/src/locales/en/translations.ts index 86ff3ca70b0..f725f6f2a25 100644 --- a/packages/i18n/src/locales/en/translations.ts +++ b/packages/i18n/src/locales/en/translations.ts @@ -327,7 +327,6 @@ export default { morning: "Good morning, {first_name} {last_name}", afternoon: "Good afternoon, {first_name} {last_name}", evening: "Good evening, {first_name} {last_name}", - night: "Good night, {first_name} {last_name}", }, show_all: "Show all", show_less: "Show less", diff --git a/packages/i18n/src/locales/es/translations.ts b/packages/i18n/src/locales/es/translations.ts index 09d11871ade..faf739668eb 100644 --- a/packages/i18n/src/locales/es/translations.ts +++ b/packages/i18n/src/locales/es/translations.ts @@ -507,7 +507,6 @@ export default { morning: "Buenos días, {first_name} {last_name}", afternoon: "Buenas tardes, {first_name} {last_name}", evening: "Buenas noches, {first_name} {last_name}", - night: "Buenas noches, {first_name} {last_name}", }, show_all: "Mostrar todo", show_less: "Mostrar menos", diff --git a/packages/i18n/src/locales/fr/translations.ts b/packages/i18n/src/locales/fr/translations.ts index 0e325d8fa58..94d251779d8 100644 --- a/packages/i18n/src/locales/fr/translations.ts +++ b/packages/i18n/src/locales/fr/translations.ts @@ -503,7 +503,6 @@ export default { morning: "Bonjour, {first_name} {last_name}", afternoon: "Bon après-midi, {first_name} {last_name}", evening: "Bonsoir, {first_name} {last_name}", - night: "Bonne nuit, {first_name} {last_name}", }, show_all: "Tout afficher", show_less: "Afficher moins", diff --git a/packages/i18n/src/locales/id/translations.ts b/packages/i18n/src/locales/id/translations.ts index b123d373ea5..10665da67ff 100644 --- a/packages/i18n/src/locales/id/translations.ts +++ b/packages/i18n/src/locales/id/translations.ts @@ -499,7 +499,6 @@ export default { morning: "Selamat pagi, {first_name} {last_name}", afternoon: "Selamat siang, {first_name} {last_name}", evening: "Selamat malam, {first_name} {last_name}", - night: "Selamat malam, {first_name} {last_name}", }, show_all: "Tampilkan semua", show_less: "Tampilkan lebih sedikit", diff --git a/packages/i18n/src/locales/it/translations.ts b/packages/i18n/src/locales/it/translations.ts index 16f5d93a806..ddc62d140ca 100644 --- a/packages/i18n/src/locales/it/translations.ts +++ b/packages/i18n/src/locales/it/translations.ts @@ -500,7 +500,6 @@ export default { morning: "Buongiorno, {first_name} {last_name}", afternoon: "Buon pomeriggio, {first_name} {last_name}", evening: "Buonasera, {first_name} {last_name}", - night: "Buonanotte, {first_name} {last_name}", }, show_all: "Mostra tutto", show_less: "Mostra meno", diff --git a/packages/i18n/src/locales/ja/translations.ts b/packages/i18n/src/locales/ja/translations.ts index 4141226ed82..a9a91e63052 100644 --- a/packages/i18n/src/locales/ja/translations.ts +++ b/packages/i18n/src/locales/ja/translations.ts @@ -493,7 +493,6 @@ export default { morning: "おはようございます、{first_name} {last_name}", afternoon: "こんにちは、{first_name} {last_name}", evening: "こんばんは、{first_name} {last_name}", - night: "おやすみなさい、{first_name} {last_name}", }, show_all: "すべて表示", show_less: "表示を減らす", diff --git a/packages/i18n/src/locales/ko/translations.ts b/packages/i18n/src/locales/ko/translations.ts index c7cb8c82ab1..4d19d26c924 100644 --- a/packages/i18n/src/locales/ko/translations.ts +++ b/packages/i18n/src/locales/ko/translations.ts @@ -487,7 +487,6 @@ export default { morning: "좋은 아침, {first_name} {last_name}", afternoon: "좋은 오후, {first_name} {last_name}", evening: "좋은 저녁, {first_name} {last_name}", - night: "좋은 밤, {first_name} {last_name}", }, show_all: "모두 보기", show_less: "간략히 보기", diff --git a/packages/i18n/src/locales/pl/translations.ts b/packages/i18n/src/locales/pl/translations.ts index 154741626c5..dd3b0a64520 100644 --- a/packages/i18n/src/locales/pl/translations.ts +++ b/packages/i18n/src/locales/pl/translations.ts @@ -493,7 +493,6 @@ export default { morning: "Dzień dobry, {first_name} {last_name}", afternoon: "Dzień dobry, {first_name} {last_name}", evening: "Dobry wieczór, {first_name} {last_name}", - night: "Dobranoc, {first_name} {last_name}", }, show_all: "Pokaż wszystko", show_less: "Pokaż mniej", diff --git a/packages/i18n/src/locales/pt-BR/translations.ts b/packages/i18n/src/locales/pt-BR/translations.ts index 9f5184fddf1..a3e2dca39d0 100644 --- a/packages/i18n/src/locales/pt-BR/translations.ts +++ b/packages/i18n/src/locales/pt-BR/translations.ts @@ -503,7 +503,6 @@ export default { morning: "Bom dia, {first_name} {last_name}", afternoon: "Boa tarde, {first_name} {last_name}", evening: "Boa noite, {first_name} {last_name}", - night: "Boa noite, {first_name} {last_name}", }, show_all: "Mostrar tudo", show_less: "Mostrar menos", diff --git a/packages/i18n/src/locales/ro/translations.ts b/packages/i18n/src/locales/ro/translations.ts index ae473148477..e56dbcf092a 100644 --- a/packages/i18n/src/locales/ro/translations.ts +++ b/packages/i18n/src/locales/ro/translations.ts @@ -500,7 +500,6 @@ export default { morning: "Bună dimineața, {first_name} {last_name}", afternoon: "Bună ziua, {first_name} {last_name}", evening: "Bună seara, {first_name} {last_name}", - night: "Noapte bună, {first_name} {last_name}", }, show_all: "Arată tot", show_less: "Arată mai puțin", diff --git a/packages/i18n/src/locales/ru/translations.ts b/packages/i18n/src/locales/ru/translations.ts index 1379e81119c..f061ed86aff 100644 --- a/packages/i18n/src/locales/ru/translations.ts +++ b/packages/i18n/src/locales/ru/translations.ts @@ -502,7 +502,6 @@ export default { morning: "Доброе утро, {first_name} {last_name}", afternoon: "Добрый день, {first_name} {last_name}", evening: "Добрый вечер, {first_name} {last_name}", - night: "Доброй ночи, {first_name} {last_name}", }, show_all: "Показать все", show_less: "Свернуть", diff --git a/packages/i18n/src/locales/sk/translations.ts b/packages/i18n/src/locales/sk/translations.ts index d286c5b274f..7bb02333fe9 100644 --- a/packages/i18n/src/locales/sk/translations.ts +++ b/packages/i18n/src/locales/sk/translations.ts @@ -494,7 +494,6 @@ export default { morning: "Dobré ráno, {first_name} {last_name}", afternoon: "Dobré popoludnie, {first_name} {last_name}", evening: "Dobrý večer, {first_name} {last_name}", - night: "Dobrú noc, {first_name} {last_name}", }, show_all: "Zobraziť všetko", show_less: "Zobraziť menej", diff --git a/packages/i18n/src/locales/tr-TR/translations.ts b/packages/i18n/src/locales/tr-TR/translations.ts index a5e19e91695..348436f7b5c 100644 --- a/packages/i18n/src/locales/tr-TR/translations.ts +++ b/packages/i18n/src/locales/tr-TR/translations.ts @@ -495,7 +495,6 @@ export default { morning: "Günaydın, {first_name} {last_name}", afternoon: "İyi günler, {first_name} {last_name}", evening: "İyi akşamlar, {first_name} {last_name}", - night: "İyi geceler, {first_name} {last_name}", }, show_all: "Tümünü göster", show_less: "Daha az göster", diff --git a/packages/i18n/src/locales/ua/translations.ts b/packages/i18n/src/locales/ua/translations.ts index 7dc5200e0c8..9292218ae48 100644 --- a/packages/i18n/src/locales/ua/translations.ts +++ b/packages/i18n/src/locales/ua/translations.ts @@ -502,7 +502,6 @@ export default { morning: "Добрий ранок, {first_name} {last_name}", afternoon: "Добрий день, {first_name} {last_name}", evening: "Добрий вечір, {first_name} {last_name}", - night: "Доброї ночі, {first_name} {last_name}", }, show_all: "Показати все", show_less: "Показати менше", diff --git a/packages/i18n/src/locales/vi-VN/translations.ts b/packages/i18n/src/locales/vi-VN/translations.ts index 61b22d502a3..a0e3aa20f85 100644 --- a/packages/i18n/src/locales/vi-VN/translations.ts +++ b/packages/i18n/src/locales/vi-VN/translations.ts @@ -500,7 +500,6 @@ export default { morning: "Chào buổi sáng, {first_name} {last_name}", afternoon: "Chào buổi chiều, {first_name} {last_name}", evening: "Chào buổi tối, {first_name} {last_name}", - night: "Chúc ngủ ngon, {first_name} {last_name}", }, show_all: "Hiển thị tất cả", show_less: "Hiển thị ít hơn", diff --git a/packages/i18n/src/locales/zh-CN/translations.ts b/packages/i18n/src/locales/zh-CN/translations.ts index 460820b39ce..d0d774620ec 100644 --- a/packages/i18n/src/locales/zh-CN/translations.ts +++ b/packages/i18n/src/locales/zh-CN/translations.ts @@ -482,7 +482,6 @@ export default { morning: "早上好,{first_name} {last_name}", afternoon: "下午好,{first_name} {last_name}", evening: "晚上好,{first_name} {last_name}", - night: "晚安,{first_name} {last_name}", }, show_all: "显示全部", show_less: "显示更少", diff --git a/packages/i18n/src/locales/zh-TW/translations.ts b/packages/i18n/src/locales/zh-TW/translations.ts index 82b6c0d7777..1c494049363 100644 --- a/packages/i18n/src/locales/zh-TW/translations.ts +++ b/packages/i18n/src/locales/zh-TW/translations.ts @@ -481,7 +481,6 @@ export default { morning: "早安,{first_name} {last_name}", afternoon: "午安,{first_name} {last_name}", evening: "晚上好,{first_name} {last_name}", - night: "晚安,{first_name} {last_name}", }, show_all: "顯示全部", show_less: "顯示較少", From 93e15fcce8f7e110b74924addefd8610f5d8a8dd Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 09:32:51 +0530 Subject: [PATCH 6/8] perf(greetings): memoize Intl formatters and hide decorative emojis from screen readers - Memoize all four Intl.DateTimeFormat instances on userTimezone so formatter objects are only recreated when the timezone changes, not on every 60-second render tick - Add aria-hidden="true" to the decorative emoji
in both UserGreetingsView components so screen readers skip it Co-Authored-By: Claude Sonnet 4.6 --- .../core/components/home/user-greetings.tsx | 2 +- .../core/components/user/user-greetings.tsx | 2 +- apps/web/core/hooks/use-greeting.ts | 48 ++++++++++--------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/apps/web/core/components/home/user-greetings.tsx b/apps/web/core/components/home/user-greetings.tsx index a5b1f6c07ea..f48b82c63fd 100644 --- a/apps/web/core/components/home/user-greetings.tsx +++ b/apps/web/core/components/home/user-greetings.tsx @@ -25,7 +25,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { {t(`greetings.${greeting}`, { first_name: user?.first_name ?? "", last_name: user?.last_name ?? "" })}

-
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
+
{weekDay}, {date} {timeString}
diff --git a/apps/web/core/components/user/user-greetings.tsx b/apps/web/core/components/user/user-greetings.tsx index a5b1f6c07ea..f48b82c63fd 100644 --- a/apps/web/core/components/user/user-greetings.tsx +++ b/apps/web/core/components/user/user-greetings.tsx @@ -25,7 +25,7 @@ export function UserGreetingsView(props: IUserGreetingsView) { {t(`greetings.${greeting}`, { first_name: user?.first_name ?? "", last_name: user?.last_name ?? "" })}
-
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙"}
+
{weekDay}, {date} {timeString}
diff --git a/apps/web/core/hooks/use-greeting.ts b/apps/web/core/hooks/use-greeting.ts index e0dff46ec14..0f43e0b78d5 100644 --- a/apps/web/core/hooks/use-greeting.ts +++ b/apps/web/core/hooks/use-greeting.ts @@ -27,29 +27,31 @@ export const useGreeting = (user: IUser) => { } }, [user?.user_timezone]); - const hour = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "numeric", - }).format(currentTime); - - const date = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - month: "short", - day: "numeric", - }).format(currentTime); - - const weekDay = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - weekday: "long", - }).format(currentTime); - - const timeString = new Intl.DateTimeFormat("en-US", { - timeZone: userTimezone, - hourCycle: "h23", - hour: "2-digit", - minute: "2-digit", - }).format(currentTime); + const hourFormatter = useMemo( + () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, hourCycle: "h23", hour: "numeric" }), + [userTimezone] + ); + + const dateFormatter = useMemo( + () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, month: "short", day: "numeric" }), + [userTimezone] + ); + + const weekDayFormatter = useMemo( + () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, weekday: "long" }), + [userTimezone] + ); + + const timeStringFormatter = useMemo( + () => + new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, hourCycle: "h23", hour: "2-digit", minute: "2-digit" }), + [userTimezone] + ); + + const hour = hourFormatter.format(currentTime); + const date = dateFormatter.format(currentTime); + const weekDay = weekDayFormatter.format(currentTime); + const timeString = timeStringFormatter.format(currentTime); const hourNum = parseInt(hour, 10); // 5–11: morning, 12–16: afternoon, 17–4: evening From 84922cfeae54fe860ced080129d1fe94c9cc8990 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 09:34:09 +0530 Subject: [PATCH 7/8] i18n: remove unused flat night key from all 19 locale files The top-level night: key was never referenced in app code. Only the greetings.{morning,afternoon,evening} nested keys are used. Co-Authored-By: Claude Sonnet 4.6 --- packages/i18n/src/locales/cs/translations.ts | 1 - packages/i18n/src/locales/de/translations.ts | 1 - packages/i18n/src/locales/en/translations.ts | 1 - packages/i18n/src/locales/es/translations.ts | 1 - packages/i18n/src/locales/fr/translations.ts | 1 - packages/i18n/src/locales/id/translations.ts | 1 - packages/i18n/src/locales/it/translations.ts | 1 - packages/i18n/src/locales/ja/translations.ts | 1 - packages/i18n/src/locales/ko/translations.ts | 1 - packages/i18n/src/locales/pl/translations.ts | 1 - packages/i18n/src/locales/pt-BR/translations.ts | 1 - packages/i18n/src/locales/ro/translations.ts | 1 - packages/i18n/src/locales/ru/translations.ts | 1 - packages/i18n/src/locales/sk/translations.ts | 1 - packages/i18n/src/locales/tr-TR/translations.ts | 1 - packages/i18n/src/locales/ua/translations.ts | 1 - packages/i18n/src/locales/vi-VN/translations.ts | 1 - packages/i18n/src/locales/zh-CN/translations.ts | 1 - packages/i18n/src/locales/zh-TW/translations.ts | 1 - 19 files changed, 19 deletions(-) diff --git a/packages/i18n/src/locales/cs/translations.ts b/packages/i18n/src/locales/cs/translations.ts index c13ad957a14..fc9f9a281e1 100644 --- a/packages/i18n/src/locales/cs/translations.ts +++ b/packages/i18n/src/locales/cs/translations.ts @@ -489,7 +489,6 @@ export default { morning: "ráno", afternoon: "odpoledne", evening: "večer", - night: "noc", greetings: { morning: "Dobré ráno, {first_name} {last_name}", afternoon: "Dobré odpoledne, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/de/translations.ts b/packages/i18n/src/locales/de/translations.ts index 3decf23af0d..c39be25c285 100644 --- a/packages/i18n/src/locales/de/translations.ts +++ b/packages/i18n/src/locales/de/translations.ts @@ -502,7 +502,6 @@ export default { morning: "Morgen", afternoon: "Nachmittag", evening: "Abend", - night: "Nacht", greetings: { morning: "Guten Morgen, {first_name} {last_name}", afternoon: "Guten Nachmittag, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/en/translations.ts b/packages/i18n/src/locales/en/translations.ts index f725f6f2a25..a9f69e16371 100644 --- a/packages/i18n/src/locales/en/translations.ts +++ b/packages/i18n/src/locales/en/translations.ts @@ -322,7 +322,6 @@ export default { morning: "morning", afternoon: "afternoon", evening: "evening", - night: "night", greetings: { morning: "Good morning, {first_name} {last_name}", afternoon: "Good afternoon, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/es/translations.ts b/packages/i18n/src/locales/es/translations.ts index faf739668eb..6d847ef1f76 100644 --- a/packages/i18n/src/locales/es/translations.ts +++ b/packages/i18n/src/locales/es/translations.ts @@ -502,7 +502,6 @@ export default { morning: "días", afternoon: "tardes", evening: "noches", - night: "noche", greetings: { morning: "Buenos días, {first_name} {last_name}", afternoon: "Buenas tardes, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/fr/translations.ts b/packages/i18n/src/locales/fr/translations.ts index 94d251779d8..5790f92b050 100644 --- a/packages/i18n/src/locales/fr/translations.ts +++ b/packages/i18n/src/locales/fr/translations.ts @@ -498,7 +498,6 @@ export default { morning: "matin", afternoon: "après-midi", evening: "soir", - night: "nuit", greetings: { morning: "Bonjour, {first_name} {last_name}", afternoon: "Bon après-midi, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/id/translations.ts b/packages/i18n/src/locales/id/translations.ts index 10665da67ff..b8297af5aab 100644 --- a/packages/i18n/src/locales/id/translations.ts +++ b/packages/i18n/src/locales/id/translations.ts @@ -494,7 +494,6 @@ export default { morning: "pagi", afternoon: "siang", evening: "malam", - night: "dini hari", greetings: { morning: "Selamat pagi, {first_name} {last_name}", afternoon: "Selamat siang, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/it/translations.ts b/packages/i18n/src/locales/it/translations.ts index ddc62d140ca..c50a71f3d99 100644 --- a/packages/i18n/src/locales/it/translations.ts +++ b/packages/i18n/src/locales/it/translations.ts @@ -495,7 +495,6 @@ export default { morning: "Mattina", afternoon: "Pomeriggio", evening: "Sera", - night: "notte", greetings: { morning: "Buongiorno, {first_name} {last_name}", afternoon: "Buon pomeriggio, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/ja/translations.ts b/packages/i18n/src/locales/ja/translations.ts index a9a91e63052..1a95f971c2f 100644 --- a/packages/i18n/src/locales/ja/translations.ts +++ b/packages/i18n/src/locales/ja/translations.ts @@ -488,7 +488,6 @@ export default { morning: "ございます", afternoon: "こんにちは", evening: "こんばんは", - night: "夜", greetings: { morning: "おはようございます、{first_name} {last_name}", afternoon: "こんにちは、{first_name} {last_name}", diff --git a/packages/i18n/src/locales/ko/translations.ts b/packages/i18n/src/locales/ko/translations.ts index 4d19d26c924..943e92cf233 100644 --- a/packages/i18n/src/locales/ko/translations.ts +++ b/packages/i18n/src/locales/ko/translations.ts @@ -482,7 +482,6 @@ export default { morning: "아침", afternoon: "오후", evening: "저녁", - night: "밤", greetings: { morning: "좋은 아침, {first_name} {last_name}", afternoon: "좋은 오후, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/pl/translations.ts b/packages/i18n/src/locales/pl/translations.ts index dd3b0a64520..e4485a1b646 100644 --- a/packages/i18n/src/locales/pl/translations.ts +++ b/packages/i18n/src/locales/pl/translations.ts @@ -488,7 +488,6 @@ export default { morning: "rano", afternoon: "po południu", evening: "wieczorem", - night: "noc", greetings: { morning: "Dzień dobry, {first_name} {last_name}", afternoon: "Dzień dobry, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/pt-BR/translations.ts b/packages/i18n/src/locales/pt-BR/translations.ts index a3e2dca39d0..8230a00647a 100644 --- a/packages/i18n/src/locales/pt-BR/translations.ts +++ b/packages/i18n/src/locales/pt-BR/translations.ts @@ -498,7 +498,6 @@ export default { morning: "manhã", afternoon: "tarde", evening: "noite", - night: "noite", greetings: { morning: "Bom dia, {first_name} {last_name}", afternoon: "Boa tarde, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/ro/translations.ts b/packages/i18n/src/locales/ro/translations.ts index e56dbcf092a..a2e6028fb79 100644 --- a/packages/i18n/src/locales/ro/translations.ts +++ b/packages/i18n/src/locales/ro/translations.ts @@ -495,7 +495,6 @@ export default { morning: "dimineața", afternoon: "după-amiaza", evening: "seara", - night: "noapte", greetings: { morning: "Bună dimineața, {first_name} {last_name}", afternoon: "Bună ziua, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/ru/translations.ts b/packages/i18n/src/locales/ru/translations.ts index f061ed86aff..8836278187a 100644 --- a/packages/i18n/src/locales/ru/translations.ts +++ b/packages/i18n/src/locales/ru/translations.ts @@ -497,7 +497,6 @@ export default { morning: "утра", afternoon: "дня", evening: "вечера", - night: "ночи", greetings: { morning: "Доброе утро, {first_name} {last_name}", afternoon: "Добрый день, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/sk/translations.ts b/packages/i18n/src/locales/sk/translations.ts index 7bb02333fe9..7ed41885533 100644 --- a/packages/i18n/src/locales/sk/translations.ts +++ b/packages/i18n/src/locales/sk/translations.ts @@ -489,7 +489,6 @@ export default { morning: "ráno", afternoon: "popoludnie", evening: "večer", - night: "noc", greetings: { morning: "Dobré ráno, {first_name} {last_name}", afternoon: "Dobré popoludnie, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/tr-TR/translations.ts b/packages/i18n/src/locales/tr-TR/translations.ts index 348436f7b5c..a6145a3398e 100644 --- a/packages/i18n/src/locales/tr-TR/translations.ts +++ b/packages/i18n/src/locales/tr-TR/translations.ts @@ -490,7 +490,6 @@ export default { morning: "sabah", afternoon: "öğleden sonra", evening: "akşam", - night: "gece", greetings: { morning: "Günaydın, {first_name} {last_name}", afternoon: "İyi günler, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/ua/translations.ts b/packages/i18n/src/locales/ua/translations.ts index 9292218ae48..15edbd6bc5a 100644 --- a/packages/i18n/src/locales/ua/translations.ts +++ b/packages/i18n/src/locales/ua/translations.ts @@ -497,7 +497,6 @@ export default { morning: "ранку", afternoon: "дня", evening: "вечора", - night: "ночі", greetings: { morning: "Добрий ранок, {first_name} {last_name}", afternoon: "Добрий день, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/vi-VN/translations.ts b/packages/i18n/src/locales/vi-VN/translations.ts index a0e3aa20f85..1c7d1ffa282 100644 --- a/packages/i18n/src/locales/vi-VN/translations.ts +++ b/packages/i18n/src/locales/vi-VN/translations.ts @@ -495,7 +495,6 @@ export default { morning: "Buổi sáng", afternoon: "Buổi chiều", evening: "Buổi tối", - night: "Buổi đêm", greetings: { morning: "Chào buổi sáng, {first_name} {last_name}", afternoon: "Chào buổi chiều, {first_name} {last_name}", diff --git a/packages/i18n/src/locales/zh-CN/translations.ts b/packages/i18n/src/locales/zh-CN/translations.ts index d0d774620ec..f60e3d741cf 100644 --- a/packages/i18n/src/locales/zh-CN/translations.ts +++ b/packages/i18n/src/locales/zh-CN/translations.ts @@ -477,7 +477,6 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", - night: "晚安", greetings: { morning: "早上好,{first_name} {last_name}", afternoon: "下午好,{first_name} {last_name}", diff --git a/packages/i18n/src/locales/zh-TW/translations.ts b/packages/i18n/src/locales/zh-TW/translations.ts index 1c494049363..699f5c4751b 100644 --- a/packages/i18n/src/locales/zh-TW/translations.ts +++ b/packages/i18n/src/locales/zh-TW/translations.ts @@ -476,7 +476,6 @@ export default { morning: "早上", afternoon: "下午", evening: "晚上", - night: "晚安", greetings: { morning: "早安,{first_name} {last_name}", afternoon: "午安,{first_name} {last_name}", From fc1a396bf21f73c5ecab7ae1e68643cf271a24c1 Mon Sep 17 00:00:00 2001 From: Minit Date: Sat, 25 Apr 2026 18:03:04 +0530 Subject: [PATCH 8/8] fix(greeting): use browser local time instead of user-configured timezone Greeting should reflect where the user physically is, not the timezone they configured in Plane settings. Removed userTimezone from all Intl.DateTimeFormat calls so formatters fall back to browser locale. --- apps/web/core/hooks/use-greeting.ts | 33 ++++++++--------------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/apps/web/core/hooks/use-greeting.ts b/apps/web/core/hooks/use-greeting.ts index 0f43e0b78d5..51dcb9f54a0 100644 --- a/apps/web/core/hooks/use-greeting.ts +++ b/apps/web/core/hooks/use-greeting.ts @@ -10,42 +10,27 @@ import { useCurrentTime } from "@/hooks/use-current-time"; export type TGreeting = "morning" | "afternoon" | "evening"; -export const useGreeting = (user: IUser) => { +export const useGreeting = (_user: IUser) => { const { currentTime } = useCurrentTime(); - const userTimezone = useMemo(() => { - if (!user?.user_timezone) return undefined; - try { - new Intl.DateTimeFormat(undefined, { timeZone: user.user_timezone }); - return user.user_timezone; - } catch (e) { - console.warn( - `[useGreeting] Invalid user_timezone "${user.user_timezone}", falling back to browser timezone.`, - e - ); - return undefined; - } - }, [user?.user_timezone]); - const hourFormatter = useMemo( - () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, hourCycle: "h23", hour: "numeric" }), - [userTimezone] + () => new Intl.DateTimeFormat("en-US", { hourCycle: "h23", hour: "numeric" }), + [] ); const dateFormatter = useMemo( - () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, month: "short", day: "numeric" }), - [userTimezone] + () => new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric" }), + [] ); const weekDayFormatter = useMemo( - () => new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, weekday: "long" }), - [userTimezone] + () => new Intl.DateTimeFormat("en-US", { weekday: "long" }), + [] ); const timeStringFormatter = useMemo( - () => - new Intl.DateTimeFormat("en-US", { timeZone: userTimezone, hourCycle: "h23", hour: "2-digit", minute: "2-digit" }), - [userTimezone] + () => new Intl.DateTimeFormat("en-US", { hourCycle: "h23", hour: "2-digit", minute: "2-digit" }), + [] ); const hour = hourFormatter.format(currentTime);