Skip to content

feat(i18n): add a basic zh-TW Traditional Chinese (Taiwan) locale#380

Open
PeterDaveHello wants to merge 1 commit into
Worklenz:mainfrom
PeterDaveHelloKitchen:zh-tw
Open

feat(i18n): add a basic zh-TW Traditional Chinese (Taiwan) locale#380
PeterDaveHello wants to merge 1 commit into
Worklenz:mainfrom
PeterDaveHelloKitchen:zh-tw

Conversation

@PeterDaveHello
Copy link
Copy Markdown

@PeterDaveHello PeterDaveHello commented May 18, 2026

🎉

Summary by CodeRabbit

  • New Features

    • Full Traditional Chinese (Taiwan) (zh_TW) localization added across the app: UI, onboarding, auth, project/task views, reporting, admin center, settings, and more.
    • Language selector and region settings now include Traditional Chinese (Taiwan) so users can choose it.
  • Chores

    • Backend language support updated to recognize and persist the new Traditional Chinese (Taiwan) option.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6a827dc-1548-4b1d-9669-2fed1d86f638

📥 Commits

Reviewing files that changed from the base of the PR and between b3b88cb and ba17863.

📒 Files selected for processing (94)
  • worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql
  • worklenz-backend/database/sql/1_tables.sql
  • worklenz-frontend/public/locales/alb/account-setup.json
  • worklenz-frontend/public/locales/de/account-setup.json
  • worklenz-frontend/public/locales/en/account-setup.json
  • worklenz-frontend/public/locales/es/account-setup.json
  • worklenz-frontend/public/locales/pt/account-setup.json
  • worklenz-frontend/public/locales/zh/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/404-page.json
  • worklenz-frontend/public/locales/zh_tw/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/overview.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/projects.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/teams.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/users.json
  • worklenz-frontend/public/locales/zh_tw/all-project-list.json
  • worklenz-frontend/public/locales/zh_tw/auth/auth-common.json
  • worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json
  • worklenz-frontend/public/locales/zh_tw/auth/login.json
  • worklenz-frontend/public/locales/zh_tw/auth/signup.json
  • worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json
  • worklenz-frontend/public/locales/zh_tw/common.json
  • worklenz-frontend/public/locales/zh_tw/create-first-project-form.json
  • worklenz-frontend/public/locales/zh_tw/create-first-tasks.json
  • worklenz-frontend/public/locales/zh_tw/home.json
  • worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json
  • worklenz-frontend/public/locales/zh_tw/kanban-board.json
  • worklenz-frontend/public/locales/zh_tw/license-expired.json
  • worklenz-frontend/public/locales/zh_tw/navbar.json
  • worklenz-frontend/public/locales/zh_tw/organization-name-form.json
  • worklenz-frontend/public/locales/zh_tw/phases-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-view-files.json
  • worklenz-frontend/public/locales/zh_tw/project-view-insights.json
  • worklenz-frontend/public/locales/zh_tw/project-view-members.json
  • worklenz-frontend/public/locales/zh_tw/project-view-updates.json
  • worklenz-frontend/public/locales/zh_tw/project-view.json
  • worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json
  • worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects.json
  • worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json
  • worklenz-frontend/public/locales/zh_tw/schedule.json
  • worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json
  • worklenz-frontend/public/locales/zh_tw/settings/appearance.json
  • worklenz-frontend/public/locales/zh_tw/settings/categories.json
  • worklenz-frontend/public/locales/zh_tw/settings/change-password.json
  • worklenz-frontend/public/locales/zh_tw/settings/clients.json
  • worklenz-frontend/public/locales/zh_tw/settings/job-titles.json
  • worklenz-frontend/public/locales/zh_tw/settings/labels.json
  • worklenz-frontend/public/locales/zh_tw/settings/language.json
  • worklenz-frontend/public/locales/zh_tw/settings/notifications.json
  • worklenz-frontend/public/locales/zh_tw/settings/profile.json
  • worklenz-frontend/public/locales/zh_tw/settings/project-templates.json
  • worklenz-frontend/public/locales/zh_tw/settings/sidebar.json
  • worklenz-frontend/public/locales/zh_tw/settings/task-templates.json
  • worklenz-frontend/public/locales/zh_tw/settings/team-members.json
  • worklenz-frontend/public/locales/zh_tw/settings/teams.json
  • worklenz-frontend/public/locales/zh_tw/survey.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json
  • worklenz-frontend/public/locales/zh_tw/task-list-filters.json
  • worklenz-frontend/public/locales/zh_tw/task-list-table.json
  • worklenz-frontend/public/locales/zh_tw/task-management.json
  • worklenz-frontend/public/locales/zh_tw/task-template-drawer.json
  • worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json
  • worklenz-frontend/public/locales/zh_tw/template-drawer.json
  • worklenz-frontend/public/locales/zh_tw/templateDrawer.json
  • worklenz-frontend/public/locales/zh_tw/time-report.json
  • worklenz-frontend/public/locales/zh_tw/unauthorized.json
  • worklenz-frontend/src/components/account-setup/members-step.tsx
  • worklenz-frontend/src/features/i18n/LanguageSelector.tsx
  • worklenz-frontend/src/features/i18n/localesSlice.ts
  • worklenz-frontend/src/i18n.ts
  • worklenz-frontend/src/pages/account-setup/account-setup.tsx
  • worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx
  • worklenz-frontend/src/utils/calculate-time-difference.ts
  • worklenz-frontend/src/utils/calculate-time-gap.ts
  • worklenz-frontend/src/utils/current-date-string.ts
  • worklenz-frontend/src/utils/dateUtils.ts
  • worklenz-frontend/src/utils/format-date-time-with-locale.ts
  • worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts
  • worklenz-frontend/src/utils/greetingString.ts
  • worklenz-frontend/src/utils/language-utils.ts
✅ Files skipped from review due to trivial changes (56)
  • worklenz-frontend/public/locales/zh_tw/settings/notifications.json
  • worklenz-frontend/public/locales/zh_tw/project-view-members.json
  • worklenz-frontend/public/locales/zh_tw/schedule.json
  • worklenz-frontend/public/locales/zh_tw/settings/clients.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json
  • worklenz-frontend/public/locales/zh_tw/task-list-filters.json
  • worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json
  • worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json
  • worklenz-frontend/public/locales/zh_tw/settings/teams.json
  • worklenz-frontend/src/pages/account-setup/account-setup.tsx
  • worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json
  • worklenz-frontend/public/locales/pt/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/users.json
  • worklenz-frontend/public/locales/zh_tw/phases-drawer.json
  • worklenz-frontend/public/locales/zh_tw/template-drawer.json
  • worklenz-frontend/public/locales/zh_tw/task-template-drawer.json
  • worklenz-frontend/src/utils/calculate-time-difference.ts
  • worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json
  • worklenz-frontend/public/locales/zh_tw/auth/auth-common.json
  • worklenz-frontend/public/locales/zh_tw/settings/team-members.json
  • worklenz-frontend/public/locales/zh_tw/auth/signup.json
  • worklenz-frontend/public/locales/zh_tw/survey.json
  • worklenz-frontend/src/utils/format-date-time-with-locale.ts
  • worklenz-frontend/public/locales/zh_tw/templateDrawer.json
  • worklenz-frontend/public/locales/zh_tw/project-view-files.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json
  • worklenz-frontend/public/locales/zh_tw/settings/profile.json
  • worklenz-frontend/public/locales/zh_tw/organization-name-form.json
  • worklenz-frontend/src/features/i18n/LanguageSelector.tsx
  • worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json
  • worklenz-frontend/public/locales/zh_tw/project-view.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json
  • worklenz-frontend/public/locales/zh_tw/settings/appearance.json
  • worklenz-frontend/public/locales/zh_tw/settings/task-templates.json
  • worklenz-frontend/public/locales/zh/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json
  • worklenz-frontend/public/locales/zh_tw/common.json
  • worklenz-frontend/public/locales/alb/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json
  • worklenz-frontend/public/locales/de/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json
  • worklenz-frontend/public/locales/zh_tw/settings/project-templates.json
  • worklenz-frontend/public/locales/zh_tw/task-list-table.json
  • worklenz-frontend/public/locales/zh_tw/create-first-tasks.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json
  • worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json
  • worklenz-frontend/public/locales/zh_tw/404-page.json
  • worklenz-frontend/public/locales/zh_tw/auth/login.json
  • worklenz-frontend/public/locales/zh_tw/license-expired.json
  • worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/teams.json
  • worklenz-frontend/public/locales/zh_tw/kanban-board.json
🚧 Files skipped from review as they are similar to previous changes (36)
  • worklenz-frontend/public/locales/zh_tw/unauthorized.json
  • worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx
  • worklenz-frontend/public/locales/en/account-setup.json
  • worklenz-frontend/src/components/account-setup/members-step.tsx
  • worklenz-frontend/public/locales/zh_tw/project-view-updates.json
  • worklenz-frontend/src/utils/dateUtils.ts
  • worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql
  • worklenz-frontend/public/locales/zh_tw/admin-center/overview.json
  • worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts
  • worklenz-frontend/public/locales/zh_tw/settings/categories.json
  • worklenz-frontend/public/locales/zh_tw/project-drawer.json
  • worklenz-frontend/src/utils/calculate-time-gap.ts
  • worklenz-frontend/public/locales/zh_tw/settings/language.json
  • worklenz-frontend/src/i18n.ts
  • worklenz-frontend/public/locales/zh_tw/reporting-projects.json
  • worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json
  • worklenz-frontend/public/locales/zh_tw/create-first-project-form.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/projects.json
  • worklenz-frontend/src/utils/current-date-string.ts
  • worklenz-frontend/public/locales/zh_tw/time-report.json
  • worklenz-frontend/public/locales/zh_tw/settings/change-password.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json
  • worklenz-frontend/public/locales/zh_tw/home.json
  • worklenz-frontend/public/locales/zh_tw/settings/job-titles.json
  • worklenz-frontend/src/utils/greetingString.ts
  • worklenz-frontend/public/locales/zh_tw/task-management.json
  • worklenz-frontend/src/features/i18n/localesSlice.ts
  • worklenz-frontend/public/locales/zh_tw/all-project-list.json
  • worklenz-frontend/public/locales/zh_tw/navbar.json
  • worklenz-frontend/public/locales/es/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview.json
  • worklenz-frontend/public/locales/zh_tw/account-setup.json
  • worklenz-backend/database/sql/1_tables.sql
  • worklenz-frontend/public/locales/zh_tw/settings/labels.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json
  • worklenz-frontend/src/utils/language-utils.ts

📝 Walkthrough

Walkthrough

This pull request adds comprehensive Traditional Chinese (Taiwan) language support to the Worklenz application. The implementation spans the database schema, language infrastructure, UI components, date/time utilities, and includes 100+ new locale JSON files providing complete translations for all application flows.

Changes

Traditional Chinese Taiwan Language Support

Layer / File(s) Summary
Database schema — add zh_tw to LANGUAGE_TYPE enum
worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql, worklenz-backend/database/sql/1_tables.sql
PostgreSQL enum LANGUAGE_TYPE gains a new zh_tw value positioned after zh_cn, allowing the backend to recognize and persist the new language choice.
Language infrastructure — enum, detection, and normalization
worklenz-frontend/src/features/i18n/localesSlice.ts, worklenz-frontend/src/i18n.ts, worklenz-frontend/src/utils/language-utils.ts
Language enum adds ZH_TW member and normalization helpers map Traditional Chinese locale aliases (zh-tw, zh-hant, etc.) to the canonical zh_tw value. Default language detection now uses this normalization to properly detect Taiwanese Chinese variants.
Language selection UI — update component dropdowns
worklenz-frontend/src/features/i18n/LanguageSelector.tsx, worklenz-frontend/src/components/account-setup/members-step.tsx, worklenz-frontend/src/pages/account-setup/account-setup.tsx, worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx
Language selection dropdowns in account setup, member invitation, settings, and global language selector are extended to include zh_tw as a selectable option.
Date and time utilities — add zh_tw locale support
worklenz-frontend/src/utils/calculate-time-difference.ts, worklenz-frontend/src/utils/calculate-time-gap.ts, worklenz-frontend/src/utils/current-date-string.ts, worklenz-frontend/src/utils/dateUtils.ts, worklenz-frontend/src/utils/format-date-time-with-locale.ts, worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts, worklenz-frontend/src/utils/greetingString.ts
Date/time formatting functions import zhTW locales from date-fns and dayjs libraries and extend conditional logic to map zh_tw language code to the appropriate locale objects.
Localization files — Traditional Chinese Taiwan UI translations
worklenz-frontend/public/locales/zh_tw/*, worklenz-frontend/public/locales/{alb,de,en,es,pt,zh}/account-setup.json
100+ new locale JSON files added for zh_tw locale with complete Traditional Chinese (Taiwan) translations across all application UI areas: account setup and authentication, admin center (configuration, billing, users, teams, projects), project and task management (drawer, list, kanban, member management, files, updates, insights), reporting (overview, projects, members), settings (profile, notifications, clients, labels, categories, job titles, teams, team members, appearance, change password, language, account deletion, templates), and common UI strings (navbar, home, schedule, license expiration, error pages, survey). Language option labels are also added to existing locale files for multiple languages (Albanian, German, English, Spanish, Portuguese, Simplified Chinese).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit bounces through Taiwan's locale,
With Traditional characters and greetings so grand,
From database enums to dropdowns so tall,
Date-time formats now understand this land! 🇹🇼

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 18, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (4)
worklenz-frontend/src/features/i18n/LanguageSelector.tsx (1)

24-32: ⚡ Quick win

Tighten languageLabels typing to the locale union.

Using Record<string, string> loses exhaustiveness checks; missing keys can silently render undefined in the button.

💡 Proposed change
-  const languageLabels: Record<string, string> = {
+  const languageLabels: Record<ILanguageType, string> = {
     en: 'En',
     es: 'Es',
     pt: 'Pt',
     alb: 'Sq',
     de: 'de',
     zh_cn: 'zh_cn',
     zh_tw: 'zh_tw',
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/features/i18n/LanguageSelector.tsx` around lines 24 -
32, The languageLabels constant currently uses Record<string,string>, losing
exhaustiveness; update languageLabels (the constant named languageLabels in
LanguageSelector.tsx) to be typed against the actual locale union (or derive its
type from a Locale union) so TypeScript enforces all locales are present—e.g.,
replace Record<string,string> with a type that references your Locale union or
declare languageLabels as a const and assert it equals Record<Locale,string>;
then fix any missing keys (en, es, pt, alb, de, zh_cn, zh_tw) or update the
union to match the object.
worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts (2)

51-51: ⚡ Quick win

Refactor nested ternary for better maintainability and add missing locale support.

The nested ternary chain is difficult to read and maintain. Additionally, other supported languages (alb, de, zh_cn) are not explicitly handled and will incorrectly fall back to Portuguese formatting.

♻️ Proposed refactor using object lookup
     // Fallback to date-fns formatting for UTC or when no timezone
     const localeString = getLanguageFromLocalStorage();
-    const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt;
+    const localeMap: Record<string, Locale> = {
+      en: enUS,
+      es: es,
+      pt: pt,
+      zh_tw: zhTW
+    };
+    const locale = localeMap[localeString] || enUS;
     return format(date, 'MMM d, yyyy, h:mm:ss a', { locale });

Note: You'll also need to import the Locale type from date-fns and consider adding entries for alb, de, and zh_cn if those locales are available in date-fns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts` at line
51, Replace the nested ternary that sets the locale constant with a clear object
lookup: create a mapping object keyed by localeString and map to the
corresponding date-fns locale imports, add explicit entries for "alb", "de", and
"zh_cn" (or their available equivalents), import the Locale type from date-fns
and type the mapping, then set const locale = localeMap[localeString] ?? enUS so
unsupported values default to enUS; update references to localeString and locale
in the function to use this lookup.

41-42: ⚡ Quick win

Incomplete locale mapping for Intl.DateTimeFormat.

The localeMap only includes 4 languages (en, es, pt, zh_tw) but the codebase supports additional languages (alb, de, zh_cn). Users with those language preferences will fall back to en-US, which may not provide appropriate date/time formatting.

📝 Proposed fix to add missing locale mappings
       const localeMap = {
         'en': 'en-US',
         'es': 'es-ES',
         'pt': 'pt-PT',
-        'zh_tw': 'zh-TW'
+        'zh_tw': 'zh-TW',
+        'alb': 'sq-AL',
+        'de': 'de-DE',
+        'zh_cn': 'zh-CN'
       };

Note: Verify these BCP 47 locale tags match the intended regional formats for each language.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts` around
lines 41 - 42, The localeMap in format-date-time-with-user-timezone.ts is
missing mappings for supported languages causing a fallback to en-US; add
mappings for the missing keys ('alb', 'de', 'zh_cn') to appropriate BCP 47 tags
(for example map 'alb' -> 'sq-AL', 'de' -> 'de-DE', 'zh_cn' -> 'zh-CN') within
the existing localeMap object so Intl.DateTimeFormat uses the correct regional
formats.
worklenz-frontend/src/utils/format-date-time-with-locale.ts (1)

10-10: ⚡ Quick win

Refactor nested ternary for better maintainability and add missing locale support.

The nested ternary chain is becoming difficult to read and maintain. Additionally, other supported languages (alb, de, zh_cn) are not explicitly handled and will incorrectly fall back to Portuguese formatting.

♻️ Proposed refactor using object lookup
 export const formatDateTimeWithLocale = (dateString: string): string => {
   if (!dateString) return '';
 
   const date = new Date(dateString);
   const localeString = getLanguageFromLocalStorage();
-  const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt;
+  const localeMap: Record<string, Locale> = {
+    en: enUS,
+    es: es,
+    pt: pt,
+    zh_tw: zhTW
+  };
+  const locale = localeMap[localeString] || enUS;
   return format(date, 'MMM d, yyyy, h:mm:ss a', { locale });
 };

Note: You'll also need to import the Locale type from date-fns and consider adding entries for alb, de, and zh_cn if those locales are available in date-fns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/utils/format-date-time-with-locale.ts` at line 10,
Replace the nested ternary that sets locale from localeString with a clear
lookup map: create a const (e.g., localeMap) that maps keys like 'en', 'es',
'zh_tw', 'alb', 'de', 'zh_cn' to their corresponding date-fns locale objects
(enUS, es, zhTW, etc.), import any missing locale objects and the Locale type
from date-fns, then set locale = localeMap[localeString] ?? pt to keep the
existing default; this makes the selection readable, adds explicit support for
alb/de/zh_cn (importing those locales if available), and ensures proper typing
for locale and localeString.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json`:
- Line 8: cardBodyText02 currently contains a hardcoded countdown ("1 個月 19 天")
which will become incorrect; replace it with a generic/translatable message or
an interpolation placeholder (e.g., "{{trialRemaining}}" or a phrase like
"您的試用方案即將到期") and ensure the code that reads cardBodyText02 uses the
corresponding interpolation variable (or a computed message) so the UI displays
dynamic trial remaining time instead of a fixed string.

In `@worklenz-frontend/public/locales/zh_tw/admin-center/overview.json`:
- Line 6: The translation for the key "contactNumber" is action-oriented
("新增聯絡電話"); change its value to a neutral field label like "聯絡電話" by updating
the "contactNumber" entry in overview.json so it reads: "contactNumber": "聯絡電話".

In `@worklenz-frontend/public/locales/zh_tw/create-first-tasks.json`:
- Line 3: Replace the misspelled JSON key "inputLable" with the correct
"inputLabel" across all affected locale files; specifically update
create-first-tasks.json and invite-initial-team-members.json for the listed
locales (en, es, ko, pt, zh, zh_tw) so the key matches the already-correct usage
in create-first-project-form.json and in locales like de and alb, ensuring you
do a project-wide search for "inputLable" and rename every occurrence to
"inputLabel" to keep keys consistent across translations.

In `@worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json`:
- Line 3: The translation file contains a misspelled key "inputLable" that will
not match lookups expecting "inputLabel"; rename the JSON key from "inputLable"
to "inputLabel" in invite-initial-team-members.json so the component (which
looks up "inputLabel") can resolve the zh_tw string correctly, and run a quick
search for any other usages of "inputLable" to update them if necessary.

In `@worklenz-frontend/public/locales/zh_tw/reporting-members.json`:
- Line 11: Rename the inconsistent translation key "EndDateInputPlaceholder" to
match camelCase "endDateInputPlaceholder" so it aligns with the existing
"startDateInputPlaceholder" usage; update the key name in reporting-members.json
and ensure any code or other locale files that reference
"EndDateInputPlaceholder" are updated to the new "endDateInputPlaceholder"
identifier (look for occurrences of EndDateInputPlaceholder and
startDateInputPlaceholder to ensure consistency).

In `@worklenz-frontend/public/locales/zh_tw/settings/profile.json`:
- Around line 10-11: The locale strings for profileJoinedText and
profileLastUpdatedText are missing interpolation placeholders while the code
calls t('profileJoinedText', { date: ... }) (and similarly for
profileLastUpdatedText), so update those keys (and all other locale files) to
include the date placeholder matching the project's i18n interpolation pattern
(e.g., use "於{date}加入" and "最後更新於{date}" if the project uses {date}
interpolation), ensuring the t(...) calls (the i18n function) will substitute
the passed date correctly; verify and use the exact placeholder syntax your i18n
setup requires before updating other language files.

In `@worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json`:
- Around line 84-85: The keys "selectedFiles" and "maxFilesError" use
single-brace placeholders ({count}) which are inconsistent with the file's
double-brace i18n convention; update the placeholder syntax in the
"selectedFiles" and "maxFilesError" values to use double braces ({{count}}) so
they match other entries like those using {{user}} and {{time}}.

In `@worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json`:
- Around line 36-39: The i18next pluralization format mismatch: locale keys like
"TASKS_SELECTED" and "DELETE_TASKS_CONFIRM" use legacy "_plural" suffixes but
i18next v23 expects CLDR suffixes; fix by either enabling v3 compatibility in
the i18n init or converting keys. Update the i18n initialization (i18n.ts) to
include compatibilityJSON: 'v3' to accept "_plural" keys, or rename each legacy
key pair (e.g. TASKS_SELECTED and TASKS_SELECTED_plural, DELETE_TASKS_CONFIRM
and DELETE_TASKS_CONFIRM_plural) to CLDR-style keys (e.g. TASKS_SELECTED_one /
TASKS_SELECTED_other) across all locale JSON files and ensure callers pass count
so i18next selects the correct form. Ensure the chosen approach is applied
consistently for all locales.

In `@worklenz-frontend/public/locales/zh_tw/templateDrawer.json`:
- Around line 1-23: The locale file templateDrawer.json breaks the project's
kebab-case convention and duplicates template-drawer.json with different keys
(e.g., "bugTracking", "softwareDevelopment", "description"); consolidate by
merging the needed keys into a single kebab-case file named template-drawer.json
(or remove templateDrawer.json if unused), ensure the merged file contains both
the task-template UI strings and project category types under a single
consistent namespace, delete the redundant templateDrawer.json across all
language directories, and run a project-wide search for any references to
templateDrawer.json to update them to template-drawer.json or adjust
import/usage accordingly.

In `@worklenz-frontend/src/i18n.ts`:
- Around line 6-11: normalizeDetectedLanguage currently returns the original
input for non-zh aliases which can leave mixed-case or dash-form locales (e.g.,
"EN", "en-US") unnormalized; update the function so it returns the already
computed normalizedLanguage in all cases while still mapping zh-tw variants to
'zh_tw' (i.e., in normalizeDetectedLanguage use
zhTwAliases.includes(normalizedLanguage) ? 'zh_tw' : normalizedLanguage),
ensuring all returned locale keys are lowercase/canonical.

In `@worklenz-frontend/src/utils/calculate-time-difference.ts`:
- Line 17: The locale fallback in calculate-time-difference.ts incorrectly
defaults every unsupported locale to Portuguese by assigning pt to the locale
variable; update the fallback so unknown localeString values default to a
sensible fallback (e.g., enUS) or use a lookup map of supported locales (e.g.,
map keys for 'en','es','zh_tw' -> enUS,es,zhTW) and return map[localeString] ??
enUS instead of falling back to pt; locate the assignment to the locale constant
and replace the ternary chain with a mapping or nullish-coalescing fallback to
ensure non-mapped languages use the correct default.

In `@worklenz-frontend/src/utils/dateUtils.ts`:
- Around line 23-25: The locale map used by getLocaleFromLanguage is missing the
'zh_cn' key so stored canonical values like "zh_cn" fall back to English; update
the mapping object (the one that currently contains 'zh' and 'zh_tw') to include
'zh_cn': 'zh-cn' (and ensure keys are in the same casing/format expected by
getLocaleFromLanguage) so getLocaleFromLanguage resolves Simplified Chinese to
the correct locale.

---

Nitpick comments:
In `@worklenz-frontend/src/features/i18n/LanguageSelector.tsx`:
- Around line 24-32: The languageLabels constant currently uses
Record<string,string>, losing exhaustiveness; update languageLabels (the
constant named languageLabels in LanguageSelector.tsx) to be typed against the
actual locale union (or derive its type from a Locale union) so TypeScript
enforces all locales are present—e.g., replace Record<string,string> with a type
that references your Locale union or declare languageLabels as a const and
assert it equals Record<Locale,string>; then fix any missing keys (en, es, pt,
alb, de, zh_cn, zh_tw) or update the union to match the object.

In `@worklenz-frontend/src/utils/format-date-time-with-locale.ts`:
- Line 10: Replace the nested ternary that sets locale from localeString with a
clear lookup map: create a const (e.g., localeMap) that maps keys like 'en',
'es', 'zh_tw', 'alb', 'de', 'zh_cn' to their corresponding date-fns locale
objects (enUS, es, zhTW, etc.), import any missing locale objects and the Locale
type from date-fns, then set locale = localeMap[localeString] ?? pt to keep the
existing default; this makes the selection readable, adds explicit support for
alb/de/zh_cn (importing those locales if available), and ensures proper typing
for locale and localeString.

In `@worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts`:
- Line 51: Replace the nested ternary that sets the locale constant with a clear
object lookup: create a mapping object keyed by localeString and map to the
corresponding date-fns locale imports, add explicit entries for "alb", "de", and
"zh_cn" (or their available equivalents), import the Locale type from date-fns
and type the mapping, then set const locale = localeMap[localeString] ?? enUS so
unsupported values default to enUS; update references to localeString and locale
in the function to use this lookup.
- Around line 41-42: The localeMap in format-date-time-with-user-timezone.ts is
missing mappings for supported languages causing a fallback to en-US; add
mappings for the missing keys ('alb', 'de', 'zh_cn') to appropriate BCP 47 tags
(for example map 'alb' -> 'sq-AL', 'de' -> 'de-DE', 'zh_cn' -> 'zh-CN') within
the existing localeMap object so Intl.DateTimeFormat uses the correct regional
formats.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d4ad7ff-2235-47c3-af23-0c734315f398

📥 Commits

Reviewing files that changed from the base of the PR and between c4c3268 and b3b88cb.

📒 Files selected for processing (94)
  • worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql
  • worklenz-backend/database/sql/1_tables.sql
  • worklenz-frontend/public/locales/alb/account-setup.json
  • worklenz-frontend/public/locales/de/account-setup.json
  • worklenz-frontend/public/locales/en/account-setup.json
  • worklenz-frontend/public/locales/es/account-setup.json
  • worklenz-frontend/public/locales/pt/account-setup.json
  • worklenz-frontend/public/locales/zh/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/404-page.json
  • worklenz-frontend/public/locales/zh_tw/account-setup.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/overview.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/projects.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/teams.json
  • worklenz-frontend/public/locales/zh_tw/admin-center/users.json
  • worklenz-frontend/public/locales/zh_tw/all-project-list.json
  • worklenz-frontend/public/locales/zh_tw/auth/auth-common.json
  • worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json
  • worklenz-frontend/public/locales/zh_tw/auth/login.json
  • worklenz-frontend/public/locales/zh_tw/auth/signup.json
  • worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json
  • worklenz-frontend/public/locales/zh_tw/common.json
  • worklenz-frontend/public/locales/zh_tw/create-first-project-form.json
  • worklenz-frontend/public/locales/zh_tw/create-first-tasks.json
  • worklenz-frontend/public/locales/zh_tw/home.json
  • worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json
  • worklenz-frontend/public/locales/zh_tw/kanban-board.json
  • worklenz-frontend/public/locales/zh_tw/license-expired.json
  • worklenz-frontend/public/locales/zh_tw/navbar.json
  • worklenz-frontend/public/locales/zh_tw/organization-name-form.json
  • worklenz-frontend/public/locales/zh_tw/phases-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-view-files.json
  • worklenz-frontend/public/locales/zh_tw/project-view-insights.json
  • worklenz-frontend/public/locales/zh_tw/project-view-members.json
  • worklenz-frontend/public/locales/zh_tw/project-view-updates.json
  • worklenz-frontend/public/locales/zh_tw/project-view.json
  • worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json
  • worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json
  • worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-members.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-overview.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json
  • worklenz-frontend/public/locales/zh_tw/reporting-projects.json
  • worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json
  • worklenz-frontend/public/locales/zh_tw/schedule.json
  • worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json
  • worklenz-frontend/public/locales/zh_tw/settings/appearance.json
  • worklenz-frontend/public/locales/zh_tw/settings/categories.json
  • worklenz-frontend/public/locales/zh_tw/settings/change-password.json
  • worklenz-frontend/public/locales/zh_tw/settings/clients.json
  • worklenz-frontend/public/locales/zh_tw/settings/job-titles.json
  • worklenz-frontend/public/locales/zh_tw/settings/labels.json
  • worklenz-frontend/public/locales/zh_tw/settings/language.json
  • worklenz-frontend/public/locales/zh_tw/settings/notifications.json
  • worklenz-frontend/public/locales/zh_tw/settings/profile.json
  • worklenz-frontend/public/locales/zh_tw/settings/project-templates.json
  • worklenz-frontend/public/locales/zh_tw/settings/sidebar.json
  • worklenz-frontend/public/locales/zh_tw/settings/task-templates.json
  • worklenz-frontend/public/locales/zh_tw/settings/team-members.json
  • worklenz-frontend/public/locales/zh_tw/settings/teams.json
  • worklenz-frontend/public/locales/zh_tw/survey.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json
  • worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json
  • worklenz-frontend/public/locales/zh_tw/task-list-filters.json
  • worklenz-frontend/public/locales/zh_tw/task-list-table.json
  • worklenz-frontend/public/locales/zh_tw/task-management.json
  • worklenz-frontend/public/locales/zh_tw/task-template-drawer.json
  • worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json
  • worklenz-frontend/public/locales/zh_tw/template-drawer.json
  • worklenz-frontend/public/locales/zh_tw/templateDrawer.json
  • worklenz-frontend/public/locales/zh_tw/time-report.json
  • worklenz-frontend/public/locales/zh_tw/unauthorized.json
  • worklenz-frontend/src/components/account-setup/members-step.tsx
  • worklenz-frontend/src/features/i18n/LanguageSelector.tsx
  • worklenz-frontend/src/features/i18n/localesSlice.ts
  • worklenz-frontend/src/i18n.ts
  • worklenz-frontend/src/pages/account-setup/account-setup.tsx
  • worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx
  • worklenz-frontend/src/utils/calculate-time-difference.ts
  • worklenz-frontend/src/utils/calculate-time-gap.ts
  • worklenz-frontend/src/utils/current-date-string.ts
  • worklenz-frontend/src/utils/dateUtils.ts
  • worklenz-frontend/src/utils/format-date-time-with-locale.ts
  • worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts
  • worklenz-frontend/src/utils/greetingString.ts
  • worklenz-frontend/src/utils/language-utils.ts

"currentPlanDetails": "目前方案詳細資料",
"upgradePlan": "升級方案",
"cardBodyText01": "免費試用",
"cardBodyText02": "(您的試用方案將在 1 個月 19 天後到期)",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove hardcoded trial countdown text.

cardBodyText02 hardcodes “1 個月 19 天”, which will drift and show incorrect billing/trial info. Use a generic message or interpolation placeholder instead.

Proposed fix
-  "cardBodyText02": "(您的試用方案將在 1 個月 19 天後到期)",
+  "cardBodyText02": "(您的試用方案即將到期)",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"cardBodyText02": "您的試用方案將在 1 個月 19 天後到期",
"cardBodyText02": "您的試用方案即將到期",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json` at
line 8, cardBodyText02 currently contains a hardcoded countdown ("1 個月 19 天")
which will become incorrect; replace it with a generic/translatable message or
an interpolation placeholder (e.g., "{{trialRemaining}}" or a phrase like
"您的試用方案即將到期") and ensure the code that reads cardBodyText02 uses the
corresponding interpolation variable (or a computed message) so the UI displays
dynamic trial remaining time instead of a fixed string.

"name": "組織名稱",
"owner": "組織擁有者",
"admins": "組織管理員",
"contactNumber": "新增聯絡電話",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

contactNumber translation is action-oriented instead of a field label.

Line 6 currently reads as “新增聯絡電話” (“Add contact number”), which mismatches a noun key and may confuse users. Use a neutral label like “聯絡電話” instead.

Suggested fix
-  "contactNumber": "新增聯絡電話",
+  "contactNumber": "聯絡電話",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"contactNumber": "新增聯絡電話",
"contactNumber": "聯絡電話",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/admin-center/overview.json` at line 6,
The translation for the key "contactNumber" is action-oriented ("新增聯絡電話");
change its value to a neutral field label like "聯絡電話" by updating the
"contactNumber" entry in overview.json so it reads: "contactNumber": "聯絡電話".

@@ -0,0 +1,7 @@
{
"formTitle": "建立第一個任務",
"inputLable": "輸入幾項您準備在此專案中進行的任務",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for inputLable vs inputLabel usage across locale files and code

echo "=== Checking locale files for 'inputLable' ==="
rg -n '"inputLable"' --glob 'public/locales/**/*.json'

echo -e "\n=== Checking locale files for 'inputLabel' ==="
rg -n '"inputLabel"' --glob 'public/locales/**/*.json'

echo -e "\n=== Checking TypeScript/JavaScript code usage ==="
rg -n "inputLable|inputLabel" --type ts --type tsx --type js --type jsx -g '!**/locales/**'

Repository: Worklenz/worklenz

Length of output: 551


🏁 Script executed:

#!/bin/bash

# First, check the locale files directory structure
echo "=== Locale files structure ==="
find worklenz-frontend/public/locales -name "*.json" -type f | head -20

echo -e "\n=== Searching for 'inputLable' (with typo) in all files ==="
rg -n "inputLable" --type json worklenz-frontend/public/locales/

echo -e "\n=== Searching for 'inputLabel' (correct) in all files ==="
rg -n "inputLabel" --type json worklenz-frontend/public/locales/

echo -e "\n=== Searching for usage in TypeScript/JavaScript code ==="
rg -n "inputLable|inputLabel" worklenz-frontend/src/ 2>/dev/null | head -30

Repository: Worklenz/worklenz

Length of output: 4219


Fix the typo in the key name across all affected locale files.

The key is misspelled "inputLable" but should be "inputLabel". This typo exists not only in the zh_tw file under review, but also in 11 other locale files across the same two JSON files (create-first-tasks.json and invite-initial-team-members.json) in Portuguese, Simplified Chinese, Traditional Chinese, English, Korean, and Spanish.

The correct spelling "inputLabel" is already used in create-first-project-form.json and in locale files like German (de) and Albanian (alb) for the same files, confirming this is an inconsistency that needs fixing across the board.

Affected files (12 total):

  • create-first-tasks.json: en, es, ko, pt, zh, zh_tw
  • invite-initial-team-members.json: en, es, ko, pt, zh, zh_tw
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/create-first-tasks.json` at line 3,
Replace the misspelled JSON key "inputLable" with the correct "inputLabel"
across all affected locale files; specifically update create-first-tasks.json
and invite-initial-team-members.json for the listed locales (en, es, ko, pt, zh,
zh_tw) so the key matches the already-correct usage in
create-first-project-form.json and in locales like de and alb, ensuring you do a
project-wide search for "inputLable" and rename every occurrence to "inputLabel"
to keep keys consistent across translations.

@@ -0,0 +1,8 @@
{
"formTitle": "邀請團隊一同協作",
"inputLable": "以電子郵件邀請",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix misspelled translation key to avoid lookup miss.

Line 3 uses inputLable, which is likely a typo for inputLabel. If the component expects inputLabel, this string won’t resolve in zh_tw.

Proposed fix
-  "inputLable": "以電子郵件邀請",
+  "inputLabel": "以電子郵件邀請",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"inputLable": "以電子郵件邀請",
"inputLabel": "以電子郵件邀請",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json` at
line 3, The translation file contains a misspelled key "inputLable" that will
not match lookups expecting "inputLabel"; rename the JSON key from "inputLable"
to "inputLabel" in invite-initial-team-members.json so the component (which
looks up "inputLabel") can resolve the zh_tw string correctly, and run a quick
search for any other usages of "inputLable" to update them if necessary.

"allTimeText": "所有時間",
"customRangeText": "自訂範圍",
"startDateInputPlaceholder": "開始日期",
"EndDateInputPlaceholder": "結束日期",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix inconsistent camelCase in key name.

The key EndDateInputPlaceholder starts with an uppercase 'E', while startDateInputPlaceholder on line 10 correctly uses lowercase. This inconsistency will cause the code to fail when looking up this translation key.

🐛 Proposed fix
-  "EndDateInputPlaceholder": "結束日期",
+  "endDateInputPlaceholder": "結束日期",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"EndDateInputPlaceholder": "結束日期",
"endDateInputPlaceholder": "結束日期",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/reporting-members.json` at line 11,
Rename the inconsistent translation key "EndDateInputPlaceholder" to match
camelCase "endDateInputPlaceholder" so it aligns with the existing
"startDateInputPlaceholder" usage; update the key name in reporting-members.json
and ensure any code or other locale files that reference
"EndDateInputPlaceholder" are updated to the new "endDateInputPlaceholder"
identifier (look for occurrences of EndDateInputPlaceholder and
startDateInputPlaceholder to ensure consistency).

Comment on lines +36 to +39
"TASKS_SELECTED": "已選取 {{count}} 項任務",
"TASKS_SELECTED_plural": "已選取 {{count}} 項任務",
"DELETE_TASKS_CONFIRM": "確定要刪除 {{count}} 項任務嗎?",
"DELETE_TASKS_CONFIRM_plural": "確定要刪除 {{count}} 項任務嗎?",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify plural config and current key style usage
rg -nP "compatibilityJSON|simplifyPluralSuffix|pluralSeparator" worklenz-frontend/src/i18n.ts
rg -nP "\"[A-Z_]+_plural\"\\s*:" worklenz-frontend/public/locales
rg -nP "\"[A-Z_]+_(one|other|zero|few|many|two)\"\\s*:" worklenz-frontend/public/locales

Repository: Worklenz/worklenz

Length of output: 1656


🏁 Script executed:

#!/bin/bash

# 1. Check package.json for i18next versions
echo "=== Package versions ==="
rg -A2 "\"i18next|react-i18next" worklenz-frontend/package.json

# 2. Search more broadly for i18next configuration
echo -e "\n=== i18next config in various files ==="
find worklenz-frontend/src -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) | head -20 | xargs rg -l "i18next" 2>/dev/null | head -5

# 3. Check for i18n initialization or config files
echo -e "\n=== i18n related files ==="
fd -e ts -e tsx -e js "i18n|i18next" worklenz-frontend/src --type f

# 4. Look for how plural keys are actually used in components
echo -e "\n=== Usage of TASKS_SELECTED key ==="
rg -r "TASKS_SELECTED" worklenz-frontend/src --type ts --type tsx -B2 -A2

Repository: Worklenz/worklenz

Length of output: 622


🏁 Script executed:

#!/bin/bash

# 5. Check the actual i18n.ts file if it exists
echo "=== Checking i18n.ts ==="
find worklenz-frontend -name "i18n.ts" -o -name "i18n.js" | head -1 | xargs cat -n

# 6. Look at the full task-table-bulk-actions.json to understand structure
echo -e "\n=== Full structure of the JSON file ==="
cat worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json | head -50

# 7. Check if there's any vite or webpack config that sets i18next options
echo -e "\n=== Checking build config for i18next settings ==="
find worklenz-frontend -maxdepth 2 -name "vite.config.*" -o -name "webpack.config.*" | xargs grep -l "i18next" 2>/dev/null || echo "No i18next config found in build files"

Repository: Worklenz/worklenz

Length of output: 2730


🏁 Script executed:

#!/bin/bash

# Search for usage of TASKS_SELECTED key in components
echo "=== Usage of TASKS_SELECTED in components ==="
rg "TASKS_SELECTED|DELETE_TASKS_CONFIRM" worklenz-frontend/src --type ts -B3 -A3 2>/dev/null || rg "TASKS_SELECTED|DELETE_TASKS_CONFIRM" worklenz-frontend/src -B3 -A3

# Check the English locale to compare structure
echo -e "\n=== English locale file ==="
cat worklenz-frontend/public/locales/en/tasks/task-table-bulk-actions.json | head -50

# Look for any i18next pluralization usage pattern in the codebase
echo -e "\n=== Check for useTranslation hook usage with pluralization ==="
rg "useTranslation|i18next.t\(" worklenz-frontend/src -A2 -B1 | grep -A3 "TASKS_SELECTED\|count"

Repository: Worklenz/worklenz

Length of output: 14816


🏁 Script executed:

#!/bin/bash

# Check i18next default pluralization behavior documentation
# For now, let's see if there are any other locale files using different plural formats
echo "=== Check all locales for plural format consistency ==="
find worklenz-frontend/public/locales -name "task-table-bulk-actions.json" -exec sh -c 'echo "=== {} ===" && head -45 "$1" | tail -10' _ {} \;

# Look for any error handling or fallback behavior in i18n initialization
echo -e "\n=== Check for any pluralization-related code in src ==="
rg "plural|Plural" worklenz-frontend/src --type ts

Repository: Worklenz/worklenz

Length of output: 7347


Configure i18next v23 for v3-style _plural suffix or update keys to CLDR format.

The codebase uses the legacy _plural suffix format (v3), but i18next v23 defaults to compatibilityJSON: "v4", which expects CLDR plural rules with suffixes like _one, _other, _few, etc. This mismatch causes pluralization fallback: when count > 1, i18next cannot find the CLDR plural form and falls back to the singular key.

For zh_tw, this goes unnoticed because singular and plural values are identical. However, for other locales (en, de, es, pt, ko), the singular and plural forms differ—meaning incorrect singular text displays for multiple items.

Fix by either:

  1. Adding compatibilityJSON: 'v3' to i18n.ts init config to support the _plural format, or
  2. Renaming all _plural keys to the CLDR _other suffix and updating the locale JSON files accordingly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json`
around lines 36 - 39, The i18next pluralization format mismatch: locale keys
like "TASKS_SELECTED" and "DELETE_TASKS_CONFIRM" use legacy "_plural" suffixes
but i18next v23 expects CLDR suffixes; fix by either enabling v3 compatibility
in the i18n init or converting keys. Update the i18n initialization (i18n.ts) to
include compatibilityJSON: 'v3' to accept "_plural" keys, or rename each legacy
key pair (e.g. TASKS_SELECTED and TASKS_SELECTED_plural, DELETE_TASKS_CONFIRM
and DELETE_TASKS_CONFIRM_plural) to CLDR-style keys (e.g. TASKS_SELECTED_one /
TASKS_SELECTED_other) across all locale JSON files and ensure callers pass count
so i18next selects the correct form. Ensure the chosen approach is applied
consistently for all locales.

Comment on lines +1 to +23
{
"bugTracking": "錯誤追蹤",
"construction": "營建",
"designCreative": "設計與創意",
"education": "教育",
"finance": "財務",
"hrRecruiting": "人資與徵才",
"informationTechnology": "資訊科技",
"legal": "法律",
"manufacturing": "製造",
"marketing": "行銷",
"nonprofit": "非營利組織",
"personalUse": "個人用途",
"salesCRM": "銷售與 CRM",
"serviceConsulting": "服務與顧問",
"softwareDevelopment": "軟體開發",
"description": "描述",
"phase": "階段",
"statuses": "狀態",
"priorities": "優先順序",
"labels": "標籤",
"tasks": "任務"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check where each namespace/file name is referenced
rg -nP "templateDrawer|template-drawer" --type=ts --type=tsx worklenz-frontend/src
rg -nP "ns\\s*:\\s*\\[|useTranslation\\(" --type=ts --type=tsx worklenz-frontend/src

Repository: Worklenz/worklenz

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Find actual locale files to check naming
fd "\.json$" worklenz-frontend/public/locales/ | grep -i template | head -20

Repository: Worklenz/worklenz

Length of output: 1377


🏁 Script executed:

#!/bin/bash
# Search for templateDrawer and template-drawer references in source files
rg "templateDrawer|template-drawer" worklenz-frontend/src --no-type-list 2>/dev/null | head -30

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check i18n configuration and namespace setup
fd "i18n|i18next|translation" worklenz-frontend/src -t f | head -20

Repository: Worklenz/worklenz

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Search for useTranslation calls and ns configuration patterns
rg "ns\s*:\s*['\"]|useTranslation\(\)" worklenz-frontend/src --no-type-list 2>/dev/null | head -30

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Read i18n configuration
cat -n worklenz-frontend/src/i18n.ts

Repository: Worklenz/worklenz

Length of output: 1460


🏁 Script executed:

#!/bin/bash
# Check if both files exist in zh_tw
ls -la worklenz-frontend/public/locales/zh_tw/ | grep -i template

Repository: Worklenz/worklenz

Length of output: 278


🏁 Script executed:

#!/bin/bash
# Compare the content of both files (using one language as example)
echo "=== template-drawer.json (en) ===" && \
cat worklenz-frontend/public/locales/en/template-drawer.json && \
echo -e "\n=== templateDrawer.json (en) ===" && \
cat worklenz-frontend/public/locales/en/templateDrawer.json

Repository: Worklenz/worklenz

Length of output: 1351


🏁 Script executed:

#!/bin/bash
# Search for indirect references - check for dynamic namespace loading or imports
rg "template.*drawer|drawer" worklenz-frontend/src --no-type-list -i 2>/dev/null | grep -v node_modules | head -40

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for actual useTranslation calls with namespace argument
rg "useTranslation\(\s*['\"]" worklenz-frontend/src --no-type-list 2>/dev/null | head -50

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for references to either namespace with quotes or variables
rg "(['\"]template[Dd]rawer['\"]|['\"]template-drawer['\"])" worklenz-frontend/src --no-type-list 2>/dev/null | head -50

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check if templateDrawer.json is referenced anywhere in config or component files
rg "templateDrawer" worklenz-frontend --no-type-list 2>/dev/null | grep -v ".json:" | head -30

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check for references in config/build files
rg "templateDrawer|template-drawer" worklenz-frontend --no-type-list -g "*.json" -g "*.ts" -g "*.tsx" -g "*.js" 2>/dev/null | grep -v "public/locales" | head -30

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check for any dynamic i18n namespace loading or registration
rg "loadNamespaces|addResourceBundle|getResource" worklenz-frontend/src --no-type-list 2>/dev/null

Repository: Worklenz/worklenz

Length of output: 43


🏁 Script executed:

#!/bin/bash
# List all namespace files to understand the full naming pattern
find worklenz-frontend/public/locales/en -name "*.json" -type f | sort

Repository: Worklenz/worklenz

Length of output: 4452


🏁 Script executed:

#!/bin/bash
# Check git history for these files
cd worklenz-frontend && git log --oneline -- "public/locales/en/templateDrawer.json" "public/locales/en/template-drawer.json" 2>/dev/null | head -20

Repository: Worklenz/worklenz

Length of output: 135


🏁 Script executed:

#!/bin/bash
# Check if there are any comments or TODOs related to these files in the codebase
rg "TODO|FIXME|deprecated|template.*drawer|drawer.*template" worklenz-frontend/src --no-type-list -i 2>/dev/null | head -20

Repository: Worklenz/worklenz

Length of output: 43


Remove or consolidate the duplicate and unused locale files with inconsistent naming.

The file templateDrawer.json violates the project's kebab-case naming convention (the only camelCase file among 70+ locale files). Both templateDrawer.json and template-drawer.json exist in all language directories with different content, yet neither is referenced anywhere in the source code. This creates confusion without providing any value:

  • template-drawer.json: task template editing UI
  • templateDrawer.json: project category types

Either consolidate these files into a single namespace with consistent naming, remove the unused files, or clarify their intended use with code references.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/public/locales/zh_tw/templateDrawer.json` around lines 1 -
23, The locale file templateDrawer.json breaks the project's kebab-case
convention and duplicates template-drawer.json with different keys (e.g.,
"bugTracking", "softwareDevelopment", "description"); consolidate by merging the
needed keys into a single kebab-case file named template-drawer.json (or remove
templateDrawer.json if unused), ensure the merged file contains both the
task-template UI strings and project category types under a single consistent
namespace, delete the redundant templateDrawer.json across all language
directories, and run a project-wide search for any references to
templateDrawer.json to update them to template-drawer.json or adjust
import/usage accordingly.

Comment thread worklenz-frontend/src/i18n.ts
const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
const localeString = getLanguageFromLocalStorage();
const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt;
const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Locale fallback incorrectly forces Portuguese for many supported languages.

The current ternary maps every non-en/es/zh_tw locale to pt, which produces incorrect relative-time text for other supported UI languages.

✅ Proposed fix
-import { enUS, es, pt, zhTW } from 'date-fns/locale';
+import { enUS, es, pt, de, zhCN, zhTW } from 'date-fns/locale';
...
-  const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt;
+  const localeMap = {
+    en: enUS,
+    es,
+    pt,
+    de,
+    zh_cn: zhCN,
+    zh_tw: zhTW,
+  } as const;
+  const locale = localeMap[localeString as keyof typeof localeMap] ?? enUS;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/utils/calculate-time-difference.ts` at line 17, The
locale fallback in calculate-time-difference.ts incorrectly defaults every
unsupported locale to Portuguese by assigning pt to the locale variable; update
the fallback so unknown localeString values default to a sensible fallback
(e.g., enUS) or use a lookup map of supported locales (e.g., map keys for
'en','es','zh_tw' -> enUS,es,zhTW) and return map[localeString] ?? enUS instead
of falling back to pt; locate the assignment to the locale constant and replace
the ternary chain with a mapping or nullish-coalescing fallback to ensure
non-mapped languages use the correct default.

Comment on lines +23 to 25
'zh': 'zh-cn',
'zh_tw': 'zh-tw'
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing zh_cn mapping causes Simplified Chinese to fall back to English.

getLocaleFromLanguage handles zh but not zh_cn; if stored language is canonical zh_cn, localized date formatting won’t use Chinese locale.

✅ Proposed fix
   const localeMap: Record<string, string> = {
     'en': 'en',
     'de': 'de',
     'es': 'es',
     'pt': 'pt',
     'alb': 'en', // Albanian not supported by dayjs, fallback to English
     'zh': 'zh-cn',
+    'zh_cn': 'zh-cn',
     'zh_tw': 'zh-tw'
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'zh': 'zh-cn',
'zh_tw': 'zh-tw'
};
const localeMap: Record<string, string> = {
'en': 'en',
'de': 'de',
'es': 'es',
'pt': 'pt',
'alb': 'en', // Albanian not supported by dayjs, fallback to English
'zh': 'zh-cn',
'zh_cn': 'zh-cn',
'zh_tw': 'zh-tw'
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worklenz-frontend/src/utils/dateUtils.ts` around lines 23 - 25, The locale
map used by getLocaleFromLanguage is missing the 'zh_cn' key so stored canonical
values like "zh_cn" fall back to English; update the mapping object (the one
that currently contains 'zh' and 'zh_tw') to include 'zh_cn': 'zh-cn' (and
ensure keys are in the same casing/format expected by getLocaleFromLanguage) so
getLocaleFromLanguage resolves Simplified Chinese to the correct locale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants