Skip to content

Commit e02b9d1

Browse files
committed
fix(no-unlocalized-strings): ignore date/time format strings
Detect and ignore common date-fns/moment format strings like "MMMM d, yyyy", "yyyy-MM-dd", "HH:mm:ss". Uses conservative patterns: - 3+ consecutive format token chars (yyyy, MMMM, EEE) - Time patterns: HH:mm, hh:mm - Date patterns: yyyy-MM, MM-dd, MM/dd
1 parent 81715ba commit e02b9d1

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

src/rules/no-unlocalized-strings.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,16 @@ ruleTester.run("no-unlocalized-strings", noUnlocalizedStrings, {
238238
{ code: 'const x = "my-css-class"', filename: "test.tsx" },
239239
{ code: 'const x = "CONSTANT_VALUE"', filename: "test.tsx" },
240240

241+
// Date/time format strings (date-fns, moment, etc.)
242+
{ code: 'format(date, "MMMM d, yyyy")', filename: "test.tsx" },
243+
{ code: 'format(date, "yyyy-MM-dd")', filename: "test.tsx" },
244+
{ code: 'format(date, "HH:mm:ss")', filename: "test.tsx" },
245+
{ code: 'format(date, "EEE, MMM d")', filename: "test.tsx" },
246+
{ code: 'format(date, "PPP")', filename: "test.tsx" },
247+
{ code: "format(date, \"yyyy-MM-dd'T'HH:mm:ss\")", filename: "test.tsx" },
248+
{ code: "const fmt = 'MMMM d, yyyy'", filename: "test.tsx" },
249+
// Note: Single-letter formats like "h:mm a" are not detected to avoid false positives
250+
241251
// URLs and paths
242252
{ code: 'const url = "https://example.com"', filename: "test.tsx" },
243253
{ code: 'const path = "/api/users"', filename: "test.tsx" },

src/rules/no-unlocalized-strings.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,41 @@ function isInsideLinguiContext(
244244
// Heuristic: Does this string look like user-visible text?
245245
// ============================================================================
246246

247+
/**
248+
* Checks if a string looks like a date/time format string.
249+
*
250+
* Uses very conservative patterns to avoid false positives:
251+
* - 3+ consecutive same format token char (yyyy, MMMM, EEE) - very distinctive
252+
* - Common time patterns: HH:mm, hh:mm, mm:ss
253+
* - Common date patterns: MM/dd, yyyy-MM, etc.
254+
*
255+
* This prevents false positives on format strings from date-fns, moment, etc.
256+
*/
257+
function looksLikeDateFormatString(value: string): boolean {
258+
// Date format strings are typically short
259+
if (value.length > 25) {
260+
return false
261+
}
262+
263+
// Very distinctive: 3+ consecutive same letter (yyyy, MMMM, EEE, etc.)
264+
// These are extremely rare in natural language
265+
if (/([yYMdDHhmsSwWEeQqLcIiRtTpPaAgGkKxXzZO])\1\1+/.test(value)) {
266+
return true
267+
}
268+
269+
// Time format: HH:mm, hh:mm, HH:mm:ss
270+
if (/[Hh]{2}:[mM]{2}/.test(value)) {
271+
return true
272+
}
273+
274+
// Date format with separators: yyyy-MM, MM-dd, MM/dd, etc.
275+
if (/[yY]{2,4}[-/][mM]{2}/.test(value) || /[mM]{2}[-/][dD]{2}/.test(value)) {
276+
return true
277+
}
278+
279+
return false
280+
}
281+
247282
/**
248283
* Determines if a string looks like user-visible UI text.
249284
*
@@ -300,6 +335,12 @@ function looksLikeUIString(value: string): boolean {
300335
return false
301336
}
302337

338+
// Date/time format strings (e.g., "MMMM d, yyyy", "HH:mm:ss", "yyyy-MM-dd")
339+
// Detected by: short string with repeated format tokens and no long natural words
340+
if (looksLikeDateFormatString(trimmed)) {
341+
return false
342+
}
343+
303344
// Non-Latin scripts are almost always user-visible text
304345
// Ranges: CJK, Hangul, Cyrillic, Arabic, Hebrew, Thai, Hangul Jamo
305346
if (/[\u3000-\u9fff\uac00-\ud7af\u0400-\u04ff\u0600-\u06ff\u0590-\u05ff\u0e00-\u0e7f\u1100-\u11ff]/.test(trimmed)) {

0 commit comments

Comments
 (0)