diff --git a/scripts/check-i18n.ts b/scripts/check-i18n.ts index 020d47a..65d86d5 100644 --- a/scripts/check-i18n.ts +++ b/scripts/check-i18n.ts @@ -11,6 +11,17 @@ const I18N_PROPS = [ 'alt', ]; +const I18N_OBJECT_KEYS = [ + 'label', + 'description', + 'placeholder', + 'subtitle', + 'heading', + 'tooltip', +]; + +const CLASS_LIKE = /[-/:[\]]/; + const SKIP_LINE_PATTERNS = [ /^\s*\/\//, /^\s*\*/, @@ -90,9 +101,43 @@ function checkFile(filePath: string): Violation[] { }); } } + + for (const key of I18N_OBJECT_KEYS) { + const keyRegex = new RegExp( + `(?:^|[\\s,{(])${key}\\s*:\\s*(['"])([^'"]*[a-zA-Z]{2,}[^'"]*)\\1`, + 'g', + ); + for (const m of line.matchAll(keyRegex)) { + if (CLASS_LIKE.test(m[2])) continue; + violations.push({ + file: rel, + line: i + 1, + text: trimmed, + reason: `hardcoded ${key}: "${m[2]}"`, + }); + } + } + + const exprRegex = /[?:]\s*(['"])([^'"]*[a-zA-Z]{2,}[^'"]*)\1/g; + for (const m of line.matchAll(exprRegex)) { + const value = m[2]; + if (!value.includes(' ') || CLASS_LIKE.test(value)) continue; + violations.push({ + file: rel, + line: i + 1, + text: trimmed, + reason: `hardcoded expression string: "${value}"`, + }); + } } - return violations; + const seen = new Set(); + return violations.filter((v) => { + const id = `${v.line}:${v.reason.slice(v.reason.indexOf('"'))}`; + if (seen.has(id)) return false; + seen.add(id); + return true; + }); } const files = findTsxFiles(SRC_DIR); diff --git a/src/components/Form/Dropdown.tsx b/src/components/Form/Dropdown.tsx index 91a610c..9f7bc3e 100644 --- a/src/components/Form/Dropdown.tsx +++ b/src/components/Form/Dropdown.tsx @@ -1,4 +1,5 @@ import * as SelectPrimitive from '@radix-ui/react-select'; +import { useTranslations } from 'next-intl'; import React, { useEffect, useLayoutEffect, @@ -66,6 +67,7 @@ export const Dropdown = React.forwardRef( }, forwardedRef, ) => { + const t = useTranslations('Dropdown'); const isMinimalVariant = variant === 'minimal'; const isDefaultVariant = variant === 'default'; const [open, setOpen] = useState(false); @@ -457,7 +459,7 @@ export const Dropdown = React.forwardRef( ) : (
- {searchable && searchTerm ? 'No results found' : 'No options'} + {searchable && searchTerm ? t('noResults') : t('noOptions')}
)} diff --git a/src/locales/en.json b/src/locales/en.json index 8674841..b0f9c53 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -7,6 +7,10 @@ "loading": "Loading...", "noResults": "No results found." }, + "Dropdown": { + "noResults": "No results found", + "noOptions": "No options" + }, "Inbox": { "notifications": "Notifications", "noNotifications": "No notifications yet", diff --git a/src/locales/ja.json b/src/locales/ja.json index d5692d2..d73343e 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -7,6 +7,10 @@ "loading": "読み込み中...", "noResults": "結果が見つかりませんでした。" }, + "Dropdown": { + "noResults": "結果が見つかりませんでした", + "noOptions": "選択肢がありません" + }, "Inbox": { "notifications": "受信ボックス", "noNotifications": "通知はまだありません",