'use client'; import ScanFixPanel from './ScanFixPanel'; import { useTranslation } from '../../../i18n/useTranslation'; import type { WorkspaceScanResult } from '../hooks/useI18nScanWorkflow'; import { useModalAnimation } from '../hooks/useModalAnimation'; type LanguageEntry = { code: string; name: string; }; type NamespaceScanResult = { ns: string; total: number; translated: number; missing: number; }; type Props = { isOpen: boolean; onClose: () => void; lastScanTime: Date | null; workspaceScan: WorkspaceScanResult | null; totalKeys: number; namespacesCount: number; allLanguages: LanguageEntry[]; activeLang: string; scanResults: NamespaceScanResult[]; scanError: string | null; isScanning: boolean; isAutoFixing: boolean; fixableFiles: string[]; selectedFiles: string[]; forceConvertToClient: boolean; onToggleFile: (file: string) => void; onSelectAll: () => void; onClear: () => void; onToggleForceConvertToClient: () => void; onRunFixSelected: () => void; }; export default function ScanResultsModal({ isOpen, onClose, lastScanTime, workspaceScan, totalKeys, namespacesCount, allLanguages, activeLang, scanResults, scanError, isScanning, isAutoFixing, fixableFiles, selectedFiles, forceConvertToClient, onToggleFile, onSelectAll, onClear, onToggleForceConvertToClient, onRunFixSelected, }: Props) { const { t } = useTranslation(); const { isRendered, isVisible } = useModalAnimation(isOpen); if (!isRendered) return null; const totalTranslated = scanResults.reduce((sum, row) => sum + row.translated, 0); const coveragePercent = totalKeys === 0 ? 100 : Math.round((totalTranslated / totalKeys) * 100); return (
{workspaceScan ? `${workspaceScan.scannedFiles} files across ${workspaceScan.scannedDirectories} directories scanned` : `${totalKeys} keys across ${namespacesCount} namespaces`} {lastScanTime && ( · Scanned {lastScanTime.toLocaleTimeString()} )}
{t('autofix.k91052e3f')}
{workspaceScan.translationCallCount}
{t('autofix.k90a6e795')}
{workspaceScan.uniqueKeyCount}
{t('autofix.k8cf40180')}
{workspaceScan.missingKeys.length}
{t('autofix.k1bf4ffa4')}
{workspaceScan.untranslatedLiterals.length}
{t('autofix.k9b173204')}
{workspaceScan.changedFileCount ?? 0}
{t('autofix.k60874ea3')}
{workspaceScan.createdKeyCount ?? 0}
{entry.file}
{entry.replacements} replacements {entry.addedImport ? ' · import added' : ''} {entry.addedHook ? ' · hook added' : ''}
{entry.file}
{entry.reason}
{t('autofix.k9bd0812b')}
{entry.file}
{entry.status}before: {entry.beforeLiteralCount} · text: {entry.textReplacements} · attr: {entry.attrReplacements} · after: {entry.afterLiteralCount}
{entry.reason &&{entry.reason}
}| Namespace | Keys | Translated | Missing | Coverage |
|---|---|---|---|---|
| {ns} | {total} | {translated} | 0 ? 'text-red-500' : 'text-gray-400'}`}> {missing} |
= 50 ? 'bg-yellow-400' : 'bg-red-400'
}`}
style={{ width: `${pct}%` }}
/>
{pct}%
|
{entry.key}
{entry.files.slice(0, 3).join(', ')} {entry.files.length > 3 ? ` (+${entry.files.length - 3} more)` : ''}
These literals appear directly in JSX. Replace them with t('...') to make the page translatable.
{entry.text}
{entry.files.slice(0, 3).join(', ')} {entry.files.length > 3 ? ` (+${entry.files.length - 3} more)` : ''}
Scan now checks workspace files (pages, components, hooks, utils) and compares used keys against en.ts.