From eaf180c64f48bb31857dcd8dbf927444473a5f28 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 24 May 2026 23:52:14 +0200 Subject: [PATCH] feat(arch-rules): JS-016 blocks hardcoded 'en-US' in JS at error severity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture rule that fails CI on any new toLocaleDateString / toLocaleString / toLocaleTimeString / new Intl.* call that hardcodes 'en-US' instead of using I18n.locale. The whole codebase was cleaned in the preceding commits (06e59f73, bb4c4004, dd1f9af8) so the rule ships at error severity from day one. - Rule definition in .architecture-rules/frontend.yaml under javascript_rules; exceptions: i18n.js (defines the helper), vendor/. - _check_hardcoded_locale in scripts/validate/validate_architecture.py wired into both JS validation sites (full scan + per-file -f mode). - Suppressible per-line with `// noqa: JS-016` for the rare case where a specific locale is genuinely required (e.g., a US-only invoice formatter that must use en-US regardless of UI language). Validator output: 0 JS-016 hits across the codebase. Negative-tested with a planted violation — rule fires correctly and clears on removal. Co-Authored-By: Claude Opus 4.7 (1M context) --- .architecture-rules/frontend.yaml | 35 +++++++++++++++++++ scripts/validate/validate_architecture.py | 42 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/.architecture-rules/frontend.yaml b/.architecture-rules/frontend.yaml index 4a4ff87f..8c8274e1 100644 --- a/.architecture-rules/frontend.yaml +++ b/.architecture-rules/frontend.yaml @@ -391,6 +391,41 @@ javascript_rules: exceptions: - "init-alpine.js" + - id: "JS-016" + name: "Do not hardcode 'en-US' (or any locale) in Intl/toLocale calls" + severity: "error" + description: | + Locale-aware APIs (toLocaleDateString, toLocaleString, toLocaleTimeString, + new Intl.NumberFormat, new Intl.DateTimeFormat, etc.) must NOT receive a + hardcoded locale tag like 'en-US'. The user's dashboard language won't be + respected and dates/numbers will render in English even when FR/DE/LB is + selected. + + Use the `I18n.locale` getter from static/shared/js/i18n.js, which returns + the current dashboard language (falls back to 'en' if I18n hasn't loaded). + + WRONG (always renders dates in English): + date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); + new Intl.NumberFormat('en-US').format(num); + + RIGHT: + date.toLocaleDateString(I18n.locale, { year: 'numeric', month: 'short', day: 'numeric' }); + new Intl.NumberFormat(I18n.locale).format(num); + + Suppress with `// noqa: JS-016` on the line for the rare case where a + specific locale is genuinely required (e.g., a US-only invoice number + formatter that must use en-US regardless of UI language). + pattern: + file_pattern: "static/**/js/**/*.js" + anti_patterns: + - "toLocaleDateString\\(\\s*['\"]en-US['\"]" + - "toLocaleString\\(\\s*['\"]en-US['\"]" + - "toLocaleTimeString\\(\\s*['\"]en-US['\"]" + - "new\\s+Intl\\.\\w+\\(\\s*['\"]en-US['\"]" + exceptions: + - "i18n.js" + - "vendor/" + - id: "JS-012" name: "Do not include /api/v1 prefix in API endpoints" severity: "error" diff --git a/scripts/validate/validate_architecture.py b/scripts/validate/validate_architecture.py index 44ecbe3a..50b13aef 100755 --- a/scripts/validate/validate_architecture.py +++ b/scripts/validate/validate_architecture.py @@ -485,6 +485,45 @@ class ArchitectureValidator: # JS-015: Check for native confirm() instead of confirm_modal macros self._check_confirm_usage(file_path, content, lines) + # JS-016: Check for hardcoded 'en-US' in locale-aware APIs + self._check_hardcoded_locale(file_path, content, lines) + + _HARDCODED_LOCALE_RE = re.compile( + r"(toLocaleDateString|toLocaleString|toLocaleTimeString|new\s+Intl\.\w+)\(\s*['\"]en-US['\"]" + ) + + def _check_hardcoded_locale( + self, file_path: Path, content: str, lines: list[str] + ): + """JS-016: Reject hardcoded 'en-US' in toLocale* / Intl.* calls. + + Dates and numbers must respect the user's dashboard language. Use + `I18n.locale` from static/shared/js/i18n.js instead of a hardcoded tag. + + Exceptions: i18n.js itself (defines the helper) and vendor/. + Suppressible per-line with `// noqa: JS-016`. + """ + name = file_path.name + path_str = str(file_path).replace("\\", "/") + if name == "i18n.js" or "/vendor/" in path_str: + return + + for i, line in enumerate(lines, 1): + if not self._HARDCODED_LOCALE_RE.search(line): + continue + if "noqa: js-016" in line.lower() or "noqa: js016" in line.lower(): + continue + self._add_violation( + rule_id="JS-016", + rule_name="Do not hardcode 'en-US' (or any locale) in Intl/toLocale calls", + severity=Severity.ERROR, + file_path=file_path, + line_number=i, + message="Hardcoded 'en-US' ignores the user's dashboard language", + context=line.strip()[:80], + suggestion="Replace 'en-US' with I18n.locale (from static/shared/js/i18n.js)", + ) + def _check_toast_usage(self, file_path: Path, content: str, lines: list[str]): """JS-009: Check for alert() or window.showToast instead of Utils.showToast()""" # Skip utils.js (where showToast is defined) @@ -3287,6 +3326,9 @@ class ArchitectureValidator: # JS-014: Check that store API calls don't include storeCode in path self._check_store_api_paths(file_path, content, lines) + # JS-016: Check for hardcoded 'en-US' in locale-aware APIs + self._check_hardcoded_locale(file_path, content, lines) + def _check_platform_settings_usage( self, file_path: Path, content: str, lines: list[str] ):