@harness-engineering/cli 1.6.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/personas/documentation-maintainer.yaml +3 -1
- package/dist/agents/personas/performance-guardian.yaml +23 -0
- package/dist/agents/personas/planner.yaml +27 -0
- package/dist/agents/personas/verifier.yaml +30 -0
- package/dist/agents/skills/claude-code/align-documentation/SKILL.md +13 -0
- package/dist/agents/skills/claude-code/cleanup-dead-code/SKILL.md +25 -1
- package/dist/agents/skills/claude-code/cleanup-dead-code/skill.yaml +5 -2
- package/dist/agents/skills/claude-code/detect-doc-drift/SKILL.md +12 -0
- package/dist/agents/skills/claude-code/enforce-architecture/SKILL.md +67 -1
- package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +5 -2
- package/dist/agents/skills/claude-code/harness-accessibility/SKILL.md +281 -0
- package/dist/agents/skills/claude-code/harness-accessibility/skill.yaml +51 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +119 -72
- package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +4 -2
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +76 -4
- package/dist/agents/skills/claude-code/harness-brainstorming/skill.yaml +2 -0
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +487 -234
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +15 -2
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/SKILL.md +226 -0
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +64 -0
- package/dist/agents/skills/claude-code/harness-dependency-health/SKILL.md +35 -6
- package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +1 -1
- package/dist/agents/skills/claude-code/harness-design/SKILL.md +265 -0
- package/dist/agents/skills/claude-code/harness-design/skill.yaml +53 -0
- package/dist/agents/skills/claude-code/harness-design-mobile/SKILL.md +336 -0
- package/dist/agents/skills/claude-code/harness-design-mobile/skill.yaml +49 -0
- package/dist/agents/skills/claude-code/harness-design-system/SKILL.md +282 -0
- package/dist/agents/skills/claude-code/harness-design-system/skill.yaml +50 -0
- package/dist/agents/skills/claude-code/harness-design-web/SKILL.md +360 -0
- package/dist/agents/skills/claude-code/harness-design-web/skill.yaml +52 -0
- package/dist/agents/skills/claude-code/harness-docs-pipeline/SKILL.md +460 -0
- package/dist/agents/skills/claude-code/harness-docs-pipeline/skill.yaml +69 -0
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +73 -8
- package/dist/agents/skills/claude-code/harness-execution/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-hotspot-detector/SKILL.md +32 -6
- package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +1 -1
- package/dist/agents/skills/claude-code/harness-i18n/SKILL.md +484 -0
- package/dist/agents/skills/claude-code/harness-i18n/skill.yaml +54 -0
- package/dist/agents/skills/claude-code/harness-i18n-process/SKILL.md +388 -0
- package/dist/agents/skills/claude-code/harness-i18n-process/skill.yaml +43 -0
- package/dist/agents/skills/claude-code/harness-i18n-workflow/SKILL.md +512 -0
- package/dist/agents/skills/claude-code/harness-i18n-workflow/skill.yaml +53 -0
- package/dist/agents/skills/claude-code/harness-impact-analysis/SKILL.md +51 -6
- package/dist/agents/skills/claude-code/harness-integrity/SKILL.md +35 -1
- package/dist/agents/skills/claude-code/harness-knowledge-mapper/SKILL.md +46 -5
- package/dist/agents/skills/claude-code/harness-knowledge-mapper/skill.yaml +1 -1
- package/dist/agents/skills/claude-code/harness-onboarding/SKILL.md +19 -1
- package/dist/agents/skills/claude-code/harness-perf/SKILL.md +37 -8
- package/dist/agents/skills/claude-code/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-perf-tdd/SKILL.md +17 -4
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +57 -3
- package/dist/agents/skills/claude-code/harness-planning/skill.yaml +2 -0
- package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +29 -9
- package/dist/agents/skills/claude-code/harness-roadmap/SKILL.md +562 -0
- package/dist/agents/skills/claude-code/harness-roadmap/skill.yaml +43 -0
- package/dist/agents/skills/claude-code/harness-security-review/SKILL.md +36 -2
- package/dist/agents/skills/claude-code/harness-security-review/skill.yaml +8 -6
- package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +1 -1
- package/dist/agents/skills/claude-code/harness-soundness-review/SKILL.md +1267 -0
- package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +48 -0
- package/dist/agents/skills/claude-code/harness-test-advisor/SKILL.md +35 -6
- package/dist/agents/skills/claude-code/harness-verification/SKILL.md +66 -0
- package/dist/agents/skills/claude-code/harness-verification/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-verify/SKILL.md +37 -0
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +15 -1
- package/dist/agents/skills/claude-code/validate-context-engineering/SKILL.md +12 -0
- package/dist/agents/skills/gemini-cli/harness-accessibility/SKILL.md +281 -0
- package/dist/agents/skills/gemini-cli/harness-accessibility/skill.yaml +51 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +119 -72
- package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +4 -2
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/SKILL.md +226 -0
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +64 -0
- package/dist/agents/skills/gemini-cli/harness-dependency-health/SKILL.md +35 -6
- package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +1 -1
- package/dist/agents/skills/gemini-cli/harness-design/SKILL.md +265 -0
- package/dist/agents/skills/gemini-cli/harness-design/skill.yaml +53 -0
- package/dist/agents/skills/gemini-cli/harness-design-mobile/SKILL.md +336 -0
- package/dist/agents/skills/gemini-cli/harness-design-mobile/skill.yaml +49 -0
- package/dist/agents/skills/gemini-cli/harness-design-system/SKILL.md +282 -0
- package/dist/agents/skills/gemini-cli/harness-design-system/skill.yaml +50 -0
- package/dist/agents/skills/gemini-cli/harness-design-web/SKILL.md +360 -0
- package/dist/agents/skills/gemini-cli/harness-design-web/skill.yaml +52 -0
- package/dist/agents/skills/gemini-cli/harness-docs-pipeline/SKILL.md +460 -0
- package/dist/agents/skills/gemini-cli/harness-docs-pipeline/skill.yaml +69 -0
- package/dist/agents/skills/gemini-cli/harness-hotspot-detector/SKILL.md +32 -6
- package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +1 -1
- package/dist/agents/skills/gemini-cli/harness-i18n/SKILL.md +484 -0
- package/dist/agents/skills/gemini-cli/harness-i18n/skill.yaml +54 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-process/SKILL.md +388 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-process/skill.yaml +43 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-workflow/SKILL.md +512 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-workflow/skill.yaml +53 -0
- package/dist/agents/skills/gemini-cli/harness-impact-analysis/SKILL.md +51 -6
- package/dist/agents/skills/gemini-cli/harness-knowledge-mapper/SKILL.md +46 -5
- package/dist/agents/skills/gemini-cli/harness-knowledge-mapper/skill.yaml +1 -1
- package/dist/agents/skills/gemini-cli/harness-perf/SKILL.md +37 -8
- package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-perf-tdd/SKILL.md +17 -4
- package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +29 -9
- package/dist/agents/skills/gemini-cli/harness-roadmap/SKILL.md +562 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap/skill.yaml +43 -0
- package/dist/agents/skills/gemini-cli/harness-security-review/skill.yaml +8 -6
- package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +1 -1
- package/dist/agents/skills/gemini-cli/harness-soundness-review/SKILL.md +1267 -0
- package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +48 -0
- package/dist/agents/skills/gemini-cli/harness-test-advisor/SKILL.md +35 -6
- package/dist/agents/skills/node_modules/.bin/vitest +2 -2
- package/dist/agents/skills/shared/design-knowledge/anti-patterns/color.yaml +106 -0
- package/dist/agents/skills/shared/design-knowledge/anti-patterns/layout.yaml +109 -0
- package/dist/agents/skills/shared/design-knowledge/anti-patterns/motion.yaml +109 -0
- package/dist/agents/skills/shared/design-knowledge/anti-patterns/typography.yaml +112 -0
- package/dist/agents/skills/shared/design-knowledge/industries/creative.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/ecommerce.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/emerging-tech.yaml +83 -0
- package/dist/agents/skills/shared/design-knowledge/industries/fintech.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/healthcare.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/lifestyle.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/saas.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/industries/services.yaml +80 -0
- package/dist/agents/skills/shared/design-knowledge/palettes/curated.yaml +234 -0
- package/dist/agents/skills/shared/design-knowledge/platform-rules/android.yaml +125 -0
- package/dist/agents/skills/shared/design-knowledge/platform-rules/flutter.yaml +144 -0
- package/dist/agents/skills/shared/design-knowledge/platform-rules/ios.yaml +106 -0
- package/dist/agents/skills/shared/design-knowledge/platform-rules/web.yaml +102 -0
- package/dist/agents/skills/shared/design-knowledge/typography/pairings.yaml +274 -0
- package/dist/agents/skills/shared/i18n-knowledge/accessibility/intersection.yaml +142 -0
- package/dist/agents/skills/shared/i18n-knowledge/anti-patterns/encoding.yaml +67 -0
- package/dist/agents/skills/shared/i18n-knowledge/anti-patterns/formatting.yaml +106 -0
- package/dist/agents/skills/shared/i18n-knowledge/anti-patterns/layout.yaml +80 -0
- package/dist/agents/skills/shared/i18n-knowledge/anti-patterns/pluralization.yaml +80 -0
- package/dist/agents/skills/shared/i18n-knowledge/anti-patterns/string-handling.yaml +106 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/android-resources.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/apple-strings.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/backend-patterns.yaml +50 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/flutter-intl.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/i18next.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/react-intl.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/frameworks/vue-i18n.yaml +47 -0
- package/dist/agents/skills/shared/i18n-knowledge/industries/ecommerce.yaml +66 -0
- package/dist/agents/skills/shared/i18n-knowledge/industries/fintech.yaml +66 -0
- package/dist/agents/skills/shared/i18n-knowledge/industries/gaming.yaml +69 -0
- package/dist/agents/skills/shared/i18n-knowledge/industries/healthcare.yaml +66 -0
- package/dist/agents/skills/shared/i18n-knowledge/industries/legal.yaml +66 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/ar.yaml +41 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/de.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/en.yaml +32 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/es.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/fi.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/fr.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/he.yaml +41 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/hi.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/it.yaml +32 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/ja.yaml +38 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/ko.yaml +38 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/nl.yaml +32 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/pl.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/pt.yaml +32 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/ru.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/sv.yaml +32 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/th.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/tr.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/zh-Hans.yaml +38 -0
- package/dist/agents/skills/shared/i18n-knowledge/locales/zh-Hant.yaml +35 -0
- package/dist/agents/skills/shared/i18n-knowledge/mcp-interop/i18next-mcp.yaml +56 -0
- package/dist/agents/skills/shared/i18n-knowledge/mcp-interop/lingo-dev.yaml +56 -0
- package/dist/agents/skills/shared/i18n-knowledge/mcp-interop/lokalise.yaml +60 -0
- package/dist/agents/skills/shared/i18n-knowledge/mcp-interop/tolgee.yaml +60 -0
- package/dist/agents/skills/shared/i18n-knowledge/testing/locale-testing.yaml +107 -0
- package/dist/agents/skills/shared/i18n-knowledge/testing/pseudo-localization.yaml +86 -0
- package/dist/bin/harness.js +64 -4
- package/dist/{chunk-UDWGSL3T.js → chunk-3JWCBVUZ.js} +3 -3
- package/dist/{chunk-IUFFBBYV.js → chunk-LNI4T7R6.js} +179 -61
- package/dist/{chunk-USEYPS7F.js → chunk-SJECMKSS.js} +2250 -40
- package/dist/{dist-4MYPT3OE.js → dist-BDO5GFEM.js} +295 -14
- package/dist/{dist-RBZXXJHG.js → dist-NT3GXHQZ.js} +95 -1
- package/dist/index.d.ts +266 -7
- package/dist/index.js +7 -3
- package/dist/validate-cross-check-2OPGCGGU.js +7 -0
- package/package.json +7 -7
- package/dist/validate-cross-check-CPEPNLOD.js +0 -7
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
description: "Formatting anti-patterns — common mistakes in number, date, currency, phone, and address formatting across locales"
|
|
2
|
+
|
|
3
|
+
patterns:
|
|
4
|
+
- name: "Date formatting without locale"
|
|
5
|
+
severity: error
|
|
6
|
+
scope: all
|
|
7
|
+
detect:
|
|
8
|
+
method: "Check for toLocaleDateString() without locale argument or manual date string construction"
|
|
9
|
+
context: "new Date().toLocaleDateString() without first arg, or month + '/' + day + '/' + year concatenation"
|
|
10
|
+
reason: "Without explicit locale, the runtime's default locale is used — which may differ from the user's locale on servers or CI."
|
|
11
|
+
instead: "Always pass locale: new Intl.DateTimeFormat(userLocale, options).format(date)"
|
|
12
|
+
strictness:
|
|
13
|
+
permissive: info
|
|
14
|
+
standard: warn
|
|
15
|
+
strict: error
|
|
16
|
+
|
|
17
|
+
- name: "Number formatting with hardcoded separators"
|
|
18
|
+
severity: error
|
|
19
|
+
scope: all
|
|
20
|
+
detect:
|
|
21
|
+
method: "Check for regex or replace operations that insert commas or periods as number separators"
|
|
22
|
+
context: "value.toFixed(2).replace('.', ',') or regex-based thousand separator insertion"
|
|
23
|
+
reason: "Decimal separator is comma in most of Europe (1.234,56), period in US/UK (1,234.56), and momayyez in Arabic."
|
|
24
|
+
instead: "Use Intl.NumberFormat(locale).format(value) for all number display"
|
|
25
|
+
strictness:
|
|
26
|
+
permissive: warn
|
|
27
|
+
standard: error
|
|
28
|
+
strict: error
|
|
29
|
+
|
|
30
|
+
- name: "Currency display with hardcoded symbol"
|
|
31
|
+
severity: error
|
|
32
|
+
scope: all
|
|
33
|
+
detect:
|
|
34
|
+
method: "Check for string concatenation with currency symbols: '$' + amount or amount + ' EUR'"
|
|
35
|
+
context: "Currency symbol prepended or appended to a formatted number"
|
|
36
|
+
reason: "Symbol position varies: $100 (en-US), 100 $ (fr-FR), 100$ (pt-BR). Some currencies use code (CHF) not symbol."
|
|
37
|
+
instead: "Use Intl.NumberFormat(locale, { style: 'currency', currency: code }).format(amount)"
|
|
38
|
+
strictness:
|
|
39
|
+
permissive: warn
|
|
40
|
+
standard: error
|
|
41
|
+
strict: error
|
|
42
|
+
|
|
43
|
+
- name: "Phone number formatting without libphonenumber"
|
|
44
|
+
severity: warning
|
|
45
|
+
scope: all
|
|
46
|
+
detect:
|
|
47
|
+
method: "Check for regex-based phone number formatting or validation"
|
|
48
|
+
context: "Phone regex like /^\\d{10}$/ or manual formatting with dashes and parentheses"
|
|
49
|
+
reason: "Phone number formats vary enormously by country. UK: 020 7946 0958. US: (202) 555-0123. Japan: 03-1234-5678."
|
|
50
|
+
instead: "Use google-libphonenumber for parsing, validation, and formatting; store in E.164 format"
|
|
51
|
+
strictness:
|
|
52
|
+
permissive: info
|
|
53
|
+
standard: warn
|
|
54
|
+
strict: warn
|
|
55
|
+
|
|
56
|
+
- name: "Address formatting with hardcoded field order"
|
|
57
|
+
severity: warning
|
|
58
|
+
scope: all
|
|
59
|
+
detect:
|
|
60
|
+
method: "Check for address string templates that assume a fixed field order"
|
|
61
|
+
context: "Template like `${street}, ${city}, ${state} ${zip}` used for all countries"
|
|
62
|
+
reason: "Japan: postal code first, then prefecture, city, street. Germany: street, then postal code + city (same line). Formats vary worldwide."
|
|
63
|
+
instead: "Use Google's libaddressinput or i18n-postal-address for country-specific address formatting"
|
|
64
|
+
strictness:
|
|
65
|
+
permissive: info
|
|
66
|
+
standard: warn
|
|
67
|
+
strict: warn
|
|
68
|
+
|
|
69
|
+
- name: "Percentage formatting assumptions"
|
|
70
|
+
severity: warning
|
|
71
|
+
scope: all
|
|
72
|
+
detect:
|
|
73
|
+
method: "Check for percentage display constructed by appending '%' to a number"
|
|
74
|
+
context: "value + '%' or `${value}%` without locale-aware formatting"
|
|
75
|
+
reason: "Some locales put a space before % (French: 25 %). Turkish uses %25 (prefix). Arabic may use the Arabic percent sign."
|
|
76
|
+
instead: "Use Intl.NumberFormat(locale, { style: 'percent' }).format(value / 100)"
|
|
77
|
+
strictness:
|
|
78
|
+
permissive: info
|
|
79
|
+
standard: warn
|
|
80
|
+
strict: warn
|
|
81
|
+
|
|
82
|
+
- name: "Hardcoded decimal precision"
|
|
83
|
+
severity: error
|
|
84
|
+
scope: all
|
|
85
|
+
detect:
|
|
86
|
+
method: "Check for .toFixed(2) applied to currency amounts"
|
|
87
|
+
context: "Universal 2-decimal-place formatting for all currencies"
|
|
88
|
+
reason: "JPY and KRW have 0 decimal places. BHD and KWD have 3. Using 2 for all misrepresents amounts."
|
|
89
|
+
instead: "Use Intl.NumberFormat with currency option which auto-selects correct precision per currency code"
|
|
90
|
+
strictness:
|
|
91
|
+
permissive: warn
|
|
92
|
+
standard: error
|
|
93
|
+
strict: error
|
|
94
|
+
|
|
95
|
+
- name: "Time zone display without IANA identifier"
|
|
96
|
+
severity: warning
|
|
97
|
+
scope: all
|
|
98
|
+
detect:
|
|
99
|
+
method: "Check for timezone abbreviations (EST, PST, CET) in user-facing display"
|
|
100
|
+
context: "Displaying 'EST' or 'PST' without the full IANA timezone identifier"
|
|
101
|
+
reason: "Timezone abbreviations are ambiguous (CST = Central Standard Time or China Standard Time) and not universally understood."
|
|
102
|
+
instead: "Use IANA identifiers (America/New_York) for storage; display user-friendly names via Intl.DateTimeFormat with timeZoneName option"
|
|
103
|
+
strictness:
|
|
104
|
+
permissive: info
|
|
105
|
+
standard: warn
|
|
106
|
+
strict: warn
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
description: "Layout anti-patterns — common mistakes in spatial design, text containers, and directional assumptions that break in localized contexts"
|
|
2
|
+
|
|
3
|
+
patterns:
|
|
4
|
+
- name: "Fixed-width containers for text"
|
|
5
|
+
severity: error
|
|
6
|
+
scope: all
|
|
7
|
+
detect:
|
|
8
|
+
method: "Check for fixed pixel widths on containers that hold translated text"
|
|
9
|
+
context: "Buttons, labels, or cards with hardcoded width in px that hold user-facing text"
|
|
10
|
+
reason: "German expands 30-35% over English. Finnish expands up to 40%. Fixed-width containers cause text overflow or truncation."
|
|
11
|
+
instead: "Use min-width/max-width with flexible layouts; test with the longest target language (typically German or Finnish)"
|
|
12
|
+
strictness:
|
|
13
|
+
permissive: info
|
|
14
|
+
standard: warn
|
|
15
|
+
strict: error
|
|
16
|
+
|
|
17
|
+
- name: "Hardcoded CSS left/right instead of logical properties"
|
|
18
|
+
severity: warning
|
|
19
|
+
scope: web
|
|
20
|
+
detect:
|
|
21
|
+
method: "Check for CSS properties using physical directions: margin-left, padding-right, text-align: left, float: left"
|
|
22
|
+
context: "Layout CSS that uses physical direction properties instead of logical equivalents"
|
|
23
|
+
reason: "Physical left/right breaks in RTL layouts. margin-left becomes the wrong side for Arabic and Hebrew users."
|
|
24
|
+
instead: "Use CSS logical properties: margin-inline-start, padding-inline-end, text-align: start, float: inline-start"
|
|
25
|
+
strictness:
|
|
26
|
+
permissive: info
|
|
27
|
+
standard: warn
|
|
28
|
+
strict: warn
|
|
29
|
+
|
|
30
|
+
- name: "Icon placement assumptions"
|
|
31
|
+
severity: warning
|
|
32
|
+
scope: all
|
|
33
|
+
detect:
|
|
34
|
+
method: "Check for icons hardcoded to left or right side of text"
|
|
35
|
+
context: "Arrow icons, action icons, or navigation indicators with fixed directional placement"
|
|
36
|
+
reason: "Left-side icons in LTR become wrong-side icons in RTL. But some icons should NOT mirror (checkmarks, media play buttons)."
|
|
37
|
+
instead: "Use logical placement (inline-start/end); create an icon exception list for icons that should not mirror in RTL"
|
|
38
|
+
strictness:
|
|
39
|
+
permissive: info
|
|
40
|
+
standard: warn
|
|
41
|
+
strict: warn
|
|
42
|
+
|
|
43
|
+
- name: "Text truncation without dir attribute"
|
|
44
|
+
severity: warning
|
|
45
|
+
scope: web
|
|
46
|
+
detect:
|
|
47
|
+
method: "Check for text-overflow: ellipsis without corresponding dir attribute on the container"
|
|
48
|
+
context: "Truncated text elements that may contain RTL content"
|
|
49
|
+
reason: "Ellipsis appears at the wrong end for RTL text without dir attribute. Bidi truncation shows '...مرحبا' instead of 'مرحبا...'"
|
|
50
|
+
instead: "Set dir attribute on text containers; use CSS direction property in conjunction with text-overflow"
|
|
51
|
+
strictness:
|
|
52
|
+
permissive: info
|
|
53
|
+
standard: warn
|
|
54
|
+
strict: warn
|
|
55
|
+
|
|
56
|
+
- name: "Fixed height on text containers"
|
|
57
|
+
severity: warning
|
|
58
|
+
scope: all
|
|
59
|
+
detect:
|
|
60
|
+
method: "Check for fixed pixel heights on containers that hold multi-line translated text"
|
|
61
|
+
context: "Cards, tooltips, or modals with hardcoded height that hold translatable content"
|
|
62
|
+
reason: "Translated text may wrap to more lines due to expansion. Thai and Devanagari need more vertical space for diacritics."
|
|
63
|
+
instead: "Use min-height instead of height; increase line-height for scripts with stacking diacritics (Thai: 1.8, Devanagari: 1.6)"
|
|
64
|
+
strictness:
|
|
65
|
+
permissive: info
|
|
66
|
+
standard: warn
|
|
67
|
+
strict: warn
|
|
68
|
+
|
|
69
|
+
- name: "Images or graphics containing embedded text"
|
|
70
|
+
severity: error
|
|
71
|
+
scope: all
|
|
72
|
+
detect:
|
|
73
|
+
method: "Check for images, SVGs, or canvas elements with text baked into the visual"
|
|
74
|
+
context: "Hero images, infographics, or icons with embedded text labels"
|
|
75
|
+
reason: "Text in images cannot be translated, searched, or read by screen readers. Each locale requires a new image asset."
|
|
76
|
+
instead: "Overlay text on images using CSS positioning; use SVG with <text> elements that can reference translation keys"
|
|
77
|
+
strictness:
|
|
78
|
+
permissive: info
|
|
79
|
+
standard: warn
|
|
80
|
+
strict: error
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
description: "Pluralization anti-patterns — common mistakes in handling plural forms across languages with varying CLDR plural categories"
|
|
2
|
+
|
|
3
|
+
patterns:
|
|
4
|
+
- name: "Binary plural logic"
|
|
5
|
+
severity: error
|
|
6
|
+
scope: all
|
|
7
|
+
detect:
|
|
8
|
+
method: "Check for ternary or if/else that only handles singular and plural (two forms)"
|
|
9
|
+
context: "count === 1 ? singular : plural or count > 1 ? plural : singular"
|
|
10
|
+
reason: "Many languages need more than two forms. Arabic has 6 (zero, one, two, few, many, other). Polish has 4. Russian has 4. Binary logic covers only English-like languages."
|
|
11
|
+
instead: "Use ICU MessageFormat plural syntax with all needed CLDR categories: {count, plural, zero {...} one {...} two {...} few {...} many {...} other {...}}"
|
|
12
|
+
strictness:
|
|
13
|
+
permissive: warn
|
|
14
|
+
standard: error
|
|
15
|
+
strict: error
|
|
16
|
+
|
|
17
|
+
- name: "Hardcoded English plural rules applied to all locales"
|
|
18
|
+
severity: error
|
|
19
|
+
scope: all
|
|
20
|
+
detect:
|
|
21
|
+
method: "Check for plural functions that add 's' suffix or check count === 1"
|
|
22
|
+
context: "Functions like pluralize(word, count) that append 's' or select between two English forms"
|
|
23
|
+
reason: "English plural rule (add 's') is specific to English. Most languages have completely different plural morphology."
|
|
24
|
+
instead: "Delegate pluralization entirely to the i18n framework which implements CLDR plural rules per locale"
|
|
25
|
+
strictness:
|
|
26
|
+
permissive: warn
|
|
27
|
+
standard: error
|
|
28
|
+
strict: error
|
|
29
|
+
|
|
30
|
+
- name: "Missing CLDR plural categories for target locales"
|
|
31
|
+
severity: error
|
|
32
|
+
scope: all
|
|
33
|
+
detect:
|
|
34
|
+
method: "Check translation files for missing plural category keys compared to what CLDR requires for each locale"
|
|
35
|
+
context: "Arabic translation file with only 'one' and 'other' forms instead of all 6 required forms"
|
|
36
|
+
reason: "Missing plural forms cause fallback to 'other' which may be grammatically incorrect for specific counts."
|
|
37
|
+
instead: "Audit translation files against CLDR plural rules for each target locale; ensure all required categories are present"
|
|
38
|
+
strictness:
|
|
39
|
+
permissive: info
|
|
40
|
+
standard: warn
|
|
41
|
+
strict: error
|
|
42
|
+
|
|
43
|
+
- name: "Ordinal pluralization not handled"
|
|
44
|
+
severity: warning
|
|
45
|
+
scope: all
|
|
46
|
+
detect:
|
|
47
|
+
method: "Check for ordinal display (1st, 2nd, 3rd) constructed manually or with English-only rules"
|
|
48
|
+
context: "Appending 'st', 'nd', 'rd', 'th' suffixes based on English ordinal rules"
|
|
49
|
+
reason: "Ordinal rules vary by locale. English: 1st, 2nd, 3rd. French: 1er, 2e. German: 1., 2. Some languages have no written ordinal form."
|
|
50
|
+
instead: "Use Intl.PluralRules(locale, { type: 'ordinal' }) to select the correct ordinal form per locale"
|
|
51
|
+
strictness:
|
|
52
|
+
permissive: info
|
|
53
|
+
standard: warn
|
|
54
|
+
strict: warn
|
|
55
|
+
|
|
56
|
+
- name: "Plural forms embedded in UI code instead of message format"
|
|
57
|
+
severity: warning
|
|
58
|
+
scope: all
|
|
59
|
+
detect:
|
|
60
|
+
method: "Check for conditional rendering of plural text in component code rather than translation files"
|
|
61
|
+
context: "JSX like {count === 1 ? <span>1 item</span> : <span>{count} items</span>}"
|
|
62
|
+
reason: "Plural logic in code bypasses the translation pipeline. Translators cannot adapt plural forms for their locale."
|
|
63
|
+
instead: "Move plural selection to translation files using ICU or framework plural syntax; render with a single translation key"
|
|
64
|
+
strictness:
|
|
65
|
+
permissive: info
|
|
66
|
+
standard: warn
|
|
67
|
+
strict: warn
|
|
68
|
+
|
|
69
|
+
- name: "Count-based display without using plural-aware formatter"
|
|
70
|
+
severity: warning
|
|
71
|
+
scope: all
|
|
72
|
+
detect:
|
|
73
|
+
method: "Check for number + noun patterns constructed without plural formatter"
|
|
74
|
+
context: "Displaying '5 file(s)' or using parenthetical plural as a workaround"
|
|
75
|
+
reason: "The '(s)' workaround is not grammatical in any language. It looks unprofessional and confuses screen readers."
|
|
76
|
+
instead: "Use proper plural formatting: t('files', { count: 5 }) with locale-specific plural forms in translation files"
|
|
77
|
+
strictness:
|
|
78
|
+
permissive: info
|
|
79
|
+
standard: warn
|
|
80
|
+
strict: warn
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
description: "String handling anti-patterns — common mistakes in text manipulation, concatenation, and string operations that break in localized contexts"
|
|
2
|
+
|
|
3
|
+
patterns:
|
|
4
|
+
- name: "String concatenation for sentences"
|
|
5
|
+
severity: error
|
|
6
|
+
scope: all
|
|
7
|
+
detect:
|
|
8
|
+
method: "Check for string concatenation of user-facing text segments"
|
|
9
|
+
context: "Expressions like 'Hello, ' + name + '!' or `Welcome to ${place}`"
|
|
10
|
+
reason: "Word order varies by locale. 'Hello, {name}!' in English becomes '{name}さん、こんにちは!' in Japanese — the name comes first."
|
|
11
|
+
instead: "Use ICU MessageFormat or framework translation function with named placeholders: t('greeting', { name })"
|
|
12
|
+
strictness:
|
|
13
|
+
permissive: info
|
|
14
|
+
standard: warn
|
|
15
|
+
strict: error
|
|
16
|
+
|
|
17
|
+
- name: "Hardcoded user-facing strings"
|
|
18
|
+
severity: error
|
|
19
|
+
scope: all
|
|
20
|
+
detect:
|
|
21
|
+
method: "Check for string literals in JSX, templates, or UI rendering code"
|
|
22
|
+
context: "Text nodes in JSX, template literals in HTML attributes, string args to UI components"
|
|
23
|
+
reason: "Untranslated strings are invisible to the localization pipeline. They remain in the source language for all users."
|
|
24
|
+
instead: "Wrap all user-facing strings in the framework's translation function (t(), $t(), FormattedMessage, etc.)"
|
|
25
|
+
strictness:
|
|
26
|
+
permissive: info
|
|
27
|
+
standard: warn
|
|
28
|
+
strict: error
|
|
29
|
+
|
|
30
|
+
- name: "Substring or split on translated strings"
|
|
31
|
+
severity: warning
|
|
32
|
+
scope: all
|
|
33
|
+
detect:
|
|
34
|
+
method: "Check for .substring(), .split(), .slice() applied to translated text"
|
|
35
|
+
context: "Operations that assume word boundaries or character positions in translated strings"
|
|
36
|
+
reason: "Word boundaries differ by locale. CJK has no spaces. Thai has no word breaks. Splitting on space fails."
|
|
37
|
+
instead: "Use Intl.Segmenter for locale-aware text segmentation; redesign to avoid splitting translated text"
|
|
38
|
+
strictness:
|
|
39
|
+
permissive: info
|
|
40
|
+
standard: warn
|
|
41
|
+
strict: warn
|
|
42
|
+
|
|
43
|
+
- name: "Hardcoded plural logic"
|
|
44
|
+
severity: error
|
|
45
|
+
scope: all
|
|
46
|
+
detect:
|
|
47
|
+
method: "Check for ternary or if/else choosing singular vs plural form"
|
|
48
|
+
context: "count === 1 ? 'item' : 'items' or similar binary plural selection"
|
|
49
|
+
reason: "Arabic has 6 plural forms. Polish has 4. Welsh has 6. Binary singular/plural only works for a few languages."
|
|
50
|
+
instead: "Use CLDR plural rules via ICU MessageFormat: {count, plural, one {# item} other {# items}}"
|
|
51
|
+
strictness:
|
|
52
|
+
permissive: warn
|
|
53
|
+
standard: error
|
|
54
|
+
strict: error
|
|
55
|
+
|
|
56
|
+
- name: "String templates with positional assumptions"
|
|
57
|
+
severity: warning
|
|
58
|
+
scope: all
|
|
59
|
+
detect:
|
|
60
|
+
method: "Check for template literals or format strings that assume a fixed order of interpolated values"
|
|
61
|
+
context: "Patterns like `${greeting} ${name}` or String.format('%s %s', greeting, name)"
|
|
62
|
+
reason: "Interpolation order may need to change per locale. Japanese may need name before greeting."
|
|
63
|
+
instead: "Use named placeholders ({greeting} {name}) so translators can reorder: '{name}さん、{greeting}'"
|
|
64
|
+
strictness:
|
|
65
|
+
permissive: info
|
|
66
|
+
standard: warn
|
|
67
|
+
strict: warn
|
|
68
|
+
|
|
69
|
+
- name: "UI text in code comments used as display strings"
|
|
70
|
+
severity: warning
|
|
71
|
+
scope: all
|
|
72
|
+
detect:
|
|
73
|
+
method: "Check for display text stored in constants or enums without translation function wrapping"
|
|
74
|
+
context: "const LABEL = 'Submit' or enum Status { Active = 'Active' } used directly in UI"
|
|
75
|
+
reason: "Constants and enum values are not extracted by localization tools. They bypass the translation pipeline."
|
|
76
|
+
instead: "Map enum values to translation keys: const LABELS = { active: t('status.active') }"
|
|
77
|
+
strictness:
|
|
78
|
+
permissive: info
|
|
79
|
+
standard: warn
|
|
80
|
+
strict: warn
|
|
81
|
+
|
|
82
|
+
- name: "String length validation on translated input"
|
|
83
|
+
severity: warning
|
|
84
|
+
scope: all
|
|
85
|
+
detect:
|
|
86
|
+
method: "Check for maxLength validation on text that will be translated or on user input from localized forms"
|
|
87
|
+
context: "Input validation like value.length <= 50 applied to fields that accept multilingual input"
|
|
88
|
+
reason: "A 50-character limit may be too short for German translations (30-35% expansion) or too long for CJK (visual width differs)."
|
|
89
|
+
instead: "Use visual width constraints (CSS max-width) instead of character count; adjust limits per locale if needed"
|
|
90
|
+
strictness:
|
|
91
|
+
permissive: info
|
|
92
|
+
standard: warn
|
|
93
|
+
strict: warn
|
|
94
|
+
|
|
95
|
+
- name: "Case transformation without locale"
|
|
96
|
+
severity: error
|
|
97
|
+
scope: all
|
|
98
|
+
detect:
|
|
99
|
+
method: "Check for .toUpperCase() and .toLowerCase() without locale argument"
|
|
100
|
+
context: "String case operations that do not use toLocaleUpperCase() or toLocaleLowerCase()"
|
|
101
|
+
reason: "Turkish dotted-i bug: 'istanbul'.toUpperCase() produces 'ISTANBUL' (wrong) instead of 'ISTANBUL'. German eszett: 'strasse'.toUpperCase() should consider locale."
|
|
102
|
+
instead: "Always use toLocaleUpperCase(locale) and toLocaleLowerCase(locale) for user-facing text"
|
|
103
|
+
strictness:
|
|
104
|
+
permissive: info
|
|
105
|
+
standard: warn
|
|
106
|
+
strict: error
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: "Android Resources"
|
|
2
|
+
description: "Android platform localization using XML string resources and the Android resource system"
|
|
3
|
+
platforms: ["mobile"]
|
|
4
|
+
message_format: "custom"
|
|
5
|
+
|
|
6
|
+
detection:
|
|
7
|
+
package_json_keys:
|
|
8
|
+
dependencies: []
|
|
9
|
+
devDependencies: []
|
|
10
|
+
file_patterns: ["**/res/values*/strings.xml", "**/*.kt", "**/*.java"]
|
|
11
|
+
config_files: ["build.gradle", "build.gradle.kts", "AndroidManifest.xml"]
|
|
12
|
+
|
|
13
|
+
conventions:
|
|
14
|
+
key_format: "snake_case"
|
|
15
|
+
file_format: "xml"
|
|
16
|
+
file_structure: "res/values-{locale}/strings.xml"
|
|
17
|
+
plural_syntax: "<plurals name='items'><item quantity='one'>%d item</item><item quantity='other'>%d items</item></plurals>"
|
|
18
|
+
interpolation_syntax: "%s (positional: %1$s, %2$s)"
|
|
19
|
+
|
|
20
|
+
recommended_tooling:
|
|
21
|
+
extraction: ["Android Studio translation editor", "Lint checks"]
|
|
22
|
+
linting: ["Android Lint (MissingTranslation, HardcodedText)"]
|
|
23
|
+
pseudo_locale: ["Android built-in pseudolocales: en-XA (accented), ar-XB (bidi)"]
|
|
24
|
+
|
|
25
|
+
anti_patterns:
|
|
26
|
+
- pattern: "String concatenation instead of getString() with format args"
|
|
27
|
+
severity: error
|
|
28
|
+
instead: "Use getString(R.string.greeting, userName) with %1$s placeholders in strings.xml"
|
|
29
|
+
- pattern: "Hardcoded strings in layout XML"
|
|
30
|
+
severity: error
|
|
31
|
+
instead: "Use @string/key references in all android:text and android:hint attributes; Android Lint flags these"
|
|
32
|
+
- pattern: "Missing quantity entries in <plurals>"
|
|
33
|
+
severity: error
|
|
34
|
+
instead: "Include all CLDR quantities for target locales (zero, one, two, few, many, other as applicable)"
|
|
35
|
+
- pattern: "Using string-array for translatable content without context"
|
|
36
|
+
severity: warning
|
|
37
|
+
instead: "Add translatorNote attributes or XML comments for string arrays to provide context for translators"
|
|
38
|
+
- pattern: "Not using Android pseudolocales for testing"
|
|
39
|
+
severity: warning
|
|
40
|
+
instead: "Enable en-XA (accented English) and ar-XB (bidi) pseudolocales in developer settings for testing"
|
|
41
|
+
|
|
42
|
+
migration_notes:
|
|
43
|
+
from:
|
|
44
|
+
- framework: "legacy Java ResourceBundle"
|
|
45
|
+
guidance: "Move .properties files to Android XML resources; convert MessageFormat patterns to Android format strings; update getString() calls"
|
|
46
|
+
- framework: "custom key-value solution"
|
|
47
|
+
guidance: "Map keys to Android resource naming conventions (snake_case); create strings.xml per locale in res/values-{locale}/ directories"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: "Apple Strings"
|
|
2
|
+
description: "Apple platform localization using .strings, .stringsdict, and .xcstrings (String Catalogs) files"
|
|
3
|
+
platforms: ["mobile"]
|
|
4
|
+
message_format: "custom"
|
|
5
|
+
|
|
6
|
+
detection:
|
|
7
|
+
package_json_keys:
|
|
8
|
+
dependencies: []
|
|
9
|
+
devDependencies: []
|
|
10
|
+
file_patterns: ["**/*.strings", "**/*.stringsdict", "**/*.xcstrings", "**/*.lproj/**"]
|
|
11
|
+
config_files: ["*.xcodeproj/project.pbxproj", "*.xcworkspace"]
|
|
12
|
+
|
|
13
|
+
conventions:
|
|
14
|
+
key_format: "dot-notation"
|
|
15
|
+
file_format: "strings"
|
|
16
|
+
file_structure: "{locale}.lproj/Localizable.strings"
|
|
17
|
+
plural_syntax: ".stringsdict plist format with NSStringLocalizedFormatKey"
|
|
18
|
+
interpolation_syntax: "%@ (positional: %1$@, %2$@)"
|
|
19
|
+
|
|
20
|
+
recommended_tooling:
|
|
21
|
+
extraction: ["genstrings", "Xcode String Catalog auto-extraction"]
|
|
22
|
+
linting: ["SwiftLint custom rules", "Xcode build warnings"]
|
|
23
|
+
pseudo_locale: ["xcstrings pseudo-locale generation"]
|
|
24
|
+
|
|
25
|
+
anti_patterns:
|
|
26
|
+
- pattern: "Missing .stringsdict for plurals"
|
|
27
|
+
severity: error
|
|
28
|
+
instead: "Create .stringsdict files for all pluralized strings; NSLocalizedString alone cannot handle plural forms"
|
|
29
|
+
- pattern: "Concatenating localized strings"
|
|
30
|
+
severity: error
|
|
31
|
+
instead: "Use String(format:) with positional specifiers: String(format: NSLocalizedString('greeting_%@_%@'), firstName, lastName)"
|
|
32
|
+
- pattern: "Not using String Catalogs (Xcode 15+)"
|
|
33
|
+
severity: info
|
|
34
|
+
instead: "Migrate from .strings/.stringsdict to .xcstrings (String Catalogs) for unified translation management"
|
|
35
|
+
- pattern: "Hardcoded strings in storyboards/XIBs without localization"
|
|
36
|
+
severity: warning
|
|
37
|
+
instead: "Use Base Internationalization with .strings files for Interface Builder, or switch to programmatic UI with NSLocalizedString"
|
|
38
|
+
- pattern: "Not testing with pseudolanguage locale on device"
|
|
39
|
+
severity: warning
|
|
40
|
+
instead: "Enable Double-Length Pseudolanguage in Xcode scheme settings to test text expansion"
|
|
41
|
+
|
|
42
|
+
migration_notes:
|
|
43
|
+
from:
|
|
44
|
+
- framework: "legacy .strings"
|
|
45
|
+
guidance: "Open .strings files in Xcode 15+ and migrate to String Catalogs (.xcstrings); Xcode provides automatic migration; review pluralization rules post-migration"
|
|
46
|
+
- framework: "third-party (Lokalise SDK, Crowdin SDK)"
|
|
47
|
+
guidance: "Replace OTA SDK with build-time .xcstrings integration; use export/import workflows instead of runtime translation fetching"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: "Backend Patterns"
|
|
2
|
+
description: "Cross-platform backend i18n patterns for Node.js, Python, Go, and Java server-side localization"
|
|
3
|
+
platforms: ["backend"]
|
|
4
|
+
message_format: "varies"
|
|
5
|
+
|
|
6
|
+
detection:
|
|
7
|
+
package_json_keys:
|
|
8
|
+
dependencies: ["i18next", "i18n-node", "messageformat", "globalize"]
|
|
9
|
+
devDependencies: []
|
|
10
|
+
file_patterns: ["**/*.py", "**/*.go", "**/*.java", "**/*.ts", "**/*.js", "**/locales/**", "**/translations/**"]
|
|
11
|
+
config_files: ["babel.cfg", "locale.go", "messages.properties", "i18n.config.*"]
|
|
12
|
+
|
|
13
|
+
conventions:
|
|
14
|
+
key_format: "dot-notation"
|
|
15
|
+
file_format: "json"
|
|
16
|
+
file_structure: "locales/{locale}/{namespace}.json"
|
|
17
|
+
plural_syntax: "Varies by runtime: ICU (Node/Java), gettext (Python), go-i18n (Go)"
|
|
18
|
+
interpolation_syntax: "Varies: {variable} (ICU), %(variable)s (Python gettext), {{.Variable}} (Go)"
|
|
19
|
+
|
|
20
|
+
recommended_tooling:
|
|
21
|
+
extraction: ["i18next-parser (Node)", "pybabel extract (Python)", "go-i18n extract (Go)", "ResourceBundle (Java)"]
|
|
22
|
+
linting: ["custom CI checks for missing translations"]
|
|
23
|
+
pseudo_locale: ["pseudo-localization npm package (Node)", "custom generators per platform"]
|
|
24
|
+
|
|
25
|
+
anti_patterns:
|
|
26
|
+
- pattern: "Hardcoded error messages in API responses"
|
|
27
|
+
severity: error
|
|
28
|
+
instead: "Return error codes with locale-neutral keys; resolve to localized messages on the client or in a localization middleware"
|
|
29
|
+
- pattern: "Locale detection only from Accept-Language header"
|
|
30
|
+
severity: warning
|
|
31
|
+
instead: "Support explicit locale preference (user settings), URL path/query, cookie, then Accept-Language as fallback chain"
|
|
32
|
+
- pattern: "Storing translated content in code"
|
|
33
|
+
severity: warning
|
|
34
|
+
instead: "Externalize translations to resource files (JSON, PO, properties); load at runtime per locale"
|
|
35
|
+
- pattern: "Email templates with hardcoded language"
|
|
36
|
+
severity: error
|
|
37
|
+
instead: "Use locale-aware email template engines; store templates per locale; resolve from user's language preference"
|
|
38
|
+
- pattern: "Logging in the user's locale instead of a fixed locale"
|
|
39
|
+
severity: warning
|
|
40
|
+
instead: "Always log in English (or a fixed locale) for consistent log analysis; localize only user-facing output"
|
|
41
|
+
- pattern: "Date/time in API responses without timezone"
|
|
42
|
+
severity: error
|
|
43
|
+
instead: "Always return ISO 8601 with timezone (UTC preferred) in API responses; let clients format for display locale"
|
|
44
|
+
|
|
45
|
+
migration_notes:
|
|
46
|
+
from:
|
|
47
|
+
- framework: "hardcoded strings"
|
|
48
|
+
guidance: "Extract all user-facing strings to resource files; implement a translation function (t/gettext); add locale resolution middleware"
|
|
49
|
+
- framework: "gettext to i18next"
|
|
50
|
+
guidance: "Convert PO/MO files to JSON format; map gettext plurals to i18next suffix convention; update translation function calls"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: "Flutter Intl"
|
|
2
|
+
description: "Dart/Flutter internationalization using the intl package and ARB (Application Resource Bundle) files"
|
|
3
|
+
platforms: ["mobile"]
|
|
4
|
+
message_format: "icu"
|
|
5
|
+
|
|
6
|
+
detection:
|
|
7
|
+
package_json_keys:
|
|
8
|
+
dependencies: ["flutter_localizations", "intl"]
|
|
9
|
+
devDependencies: ["intl_utils", "flutter_gen"]
|
|
10
|
+
file_patterns: ["lib/**/*.dart", "lib/l10n/**/*.arb"]
|
|
11
|
+
config_files: ["l10n.yaml", "pubspec.yaml"]
|
|
12
|
+
|
|
13
|
+
conventions:
|
|
14
|
+
key_format: "camelCase"
|
|
15
|
+
file_format: "arb"
|
|
16
|
+
file_structure: "lib/l10n/app_{locale}.arb"
|
|
17
|
+
plural_syntax: "ICU {count, plural, one {# item} other {# items}}"
|
|
18
|
+
interpolation_syntax: "{variable}"
|
|
19
|
+
|
|
20
|
+
recommended_tooling:
|
|
21
|
+
extraction: ["flutter gen-l10n", "intl_utils"]
|
|
22
|
+
linting: ["dart analyze"]
|
|
23
|
+
pseudo_locale: ["custom ARB generator"]
|
|
24
|
+
|
|
25
|
+
anti_patterns:
|
|
26
|
+
- pattern: "Hardcoded strings in Text() widgets"
|
|
27
|
+
severity: error
|
|
28
|
+
instead: "Use AppLocalizations.of(context)!.messageKey for all user-facing strings"
|
|
29
|
+
- pattern: "Not running flutter gen-l10n after adding new messages"
|
|
30
|
+
severity: warning
|
|
31
|
+
instead: "Run flutter gen-l10n to regenerate the AppLocalizations class after modifying ARB files"
|
|
32
|
+
- pattern: "Using string interpolation for plurals"
|
|
33
|
+
severity: error
|
|
34
|
+
instead: "Use ICU plural syntax in ARB files: {count, plural, one {# item} other {# items}}"
|
|
35
|
+
- pattern: "Missing @metadata descriptions in ARB files"
|
|
36
|
+
severity: info
|
|
37
|
+
instead: "Add @messageKey descriptions for translator context: '@greeting': {'description': 'Welcome message on home screen'}"
|
|
38
|
+
- pattern: "Not testing with different locale settings on device"
|
|
39
|
+
severity: warning
|
|
40
|
+
instead: "Use Flutter integration tests with different locale configurations; test RTL with Arabic locale"
|
|
41
|
+
|
|
42
|
+
migration_notes:
|
|
43
|
+
from:
|
|
44
|
+
- framework: "easy_localization"
|
|
45
|
+
guidance: "Convert JSON translation files to ARB format; replace tr() calls with AppLocalizations.of(context); update locale loading configuration in MaterialApp"
|
|
46
|
+
- framework: "get (GetX i18n)"
|
|
47
|
+
guidance: "Move from Map-based translations to ARB files; replace 'key'.tr with AppLocalizations; configure locale resolution in MaterialApp.localizationsDelegates"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: "i18next"
|
|
2
|
+
description: "Full-featured i18n framework with plugins for React, Vue, Node, and many other platforms"
|
|
3
|
+
platforms: ["web", "mobile", "backend"]
|
|
4
|
+
message_format: "i18next"
|
|
5
|
+
|
|
6
|
+
detection:
|
|
7
|
+
package_json_keys:
|
|
8
|
+
dependencies: ["i18next", "react-i18next", "next-i18next", "i18next-http-backend", "i18next-browser-languagedetector"]
|
|
9
|
+
devDependencies: ["i18next-parser", "i18next-scanner"]
|
|
10
|
+
file_patterns: ["src/**/*.{ts,tsx,js,jsx}", "public/locales/**/*.json"]
|
|
11
|
+
config_files: ["i18next.config.js", "i18next.config.ts", "next-i18next.config.js"]
|
|
12
|
+
|
|
13
|
+
conventions:
|
|
14
|
+
key_format: "nested"
|
|
15
|
+
file_format: "json"
|
|
16
|
+
file_structure: "public/locales/{locale}/{namespace}.json"
|
|
17
|
+
plural_syntax: "key_one: '# item', key_other: '# items' (suffix-based)"
|
|
18
|
+
interpolation_syntax: "{{variable}}"
|
|
19
|
+
|
|
20
|
+
recommended_tooling:
|
|
21
|
+
extraction: ["i18next-parser", "i18next-scanner"]
|
|
22
|
+
linting: ["eslint-plugin-i18next"]
|
|
23
|
+
pseudo_locale: ["i18next-pseudo"]
|
|
24
|
+
|
|
25
|
+
anti_patterns:
|
|
26
|
+
- pattern: "Not using the count option for pluralization"
|
|
27
|
+
severity: error
|
|
28
|
+
instead: "Pass { count: n } to t() function: t('items', { count: itemCount }) — i18next auto-selects the plural form"
|
|
29
|
+
- pattern: "Nesting translations with $t() inside translations"
|
|
30
|
+
severity: warning
|
|
31
|
+
instead: "Use flat interpolation or context-based keys instead of nested $t() references which are hard to extract"
|
|
32
|
+
- pattern: "Loading all namespaces upfront"
|
|
33
|
+
severity: warning
|
|
34
|
+
instead: "Use lazy loading with i18next-http-backend to load namespaces on demand for better performance"
|
|
35
|
+
- pattern: "Hardcoding the fallback language in multiple places"
|
|
36
|
+
severity: warning
|
|
37
|
+
instead: "Configure fallbackLng once in i18next.init(); use namespace-level fallbacks for granular control"
|
|
38
|
+
- pattern: "Using the t() function outside React components without proper initialization"
|
|
39
|
+
severity: error
|
|
40
|
+
instead: "Use i18next.t() directly for non-React code, or pass the t function from initialized context"
|
|
41
|
+
|
|
42
|
+
migration_notes:
|
|
43
|
+
from:
|
|
44
|
+
- framework: "react-intl"
|
|
45
|
+
guidance: "Convert ICU {variable} to {{variable}}; convert ICU plural syntax to suffix-based keys (_one, _other); replace FormattedMessage with useTranslation() hook"
|
|
46
|
+
- framework: "custom-solution"
|
|
47
|
+
guidance: "Map existing key-value pairs to i18next namespace structure; configure fallback chains; add language detection plugin"
|