feat: add lingui/no-unlocalized-strings ESLint rule and fix translations (#16610)

## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.

## Changes

### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files

### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components

## Status
🚧 **Work in Progress** - ~124 files remaining to fix

This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.

## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
This commit is contained in:
Félix Malfait
2025-12-17 22:08:33 +01:00
committed by GitHub
parent c13b955a36
commit 1088f7bbab
368 changed files with 56823 additions and 836 deletions
@@ -152,45 +152,51 @@ type TimingTabProps = {
};
const TimingTab = ({ debug }: TimingTabProps) => {
const { t } = useLingui();
const totalTime =
debug.agentExecutionStartTimeMs !== undefined
? `${debug.agentExecutionStartTimeMs + (debug.agentExecutionTimeMs || 0)}ms`
: undefined;
const totalCost =
debug.totalCostInCredits !== undefined
? formatNumber(debug.totalCostInCredits)
: undefined;
return (
<StyledTimingSection>
<TimingRow
label="Routing decision"
label={t`Routing decision`}
value={debug.routingTimeMs && `${debug.routingTimeMs}ms`}
/>
<TimingRow
label="Context building (routing)"
label={t`Context building (routing)`}
value={debug.contextBuildTimeMs && `${debug.contextBuildTimeMs}ms`}
/>
<TimingRow
label="Context building (agent)"
label={t`Context building (agent)`}
value={
debug.agentContextBuildTimeMs && `${debug.agentContextBuildTimeMs}ms`
}
/>
<TimingRow
label="Tool generation"
label={t`Tool generation`}
value={debug.toolGenerationTimeMs && `${debug.toolGenerationTimeMs}ms`}
/>
<TimingRow
label="AI request prep"
label={t`AI request prep`}
value={debug.aiRequestPrepTimeMs && `${debug.aiRequestPrepTimeMs}ms`}
/>
<TimingRow
label="Agent execution"
label={t`Agent execution`}
value={debug.agentExecutionTimeMs && `${debug.agentExecutionTimeMs}ms`}
/>
<TimingRow label="Total time" value={totalTime} />
<TimingRow label="Available tools" value={debug.toolCount} />
<TimingRow label="Tool calls made" value={debug.toolCallCount} />
<TimingRow label="Context records" value={debug.contextRecordCount} />
<TimingRow label={t`Total time`} value={totalTime} />
<TimingRow label={t`Available tools`} value={debug.toolCount} />
<TimingRow label={t`Tool calls made`} value={debug.toolCallCount} />
<TimingRow label={t`Context records`} value={debug.contextRecordCount} />
<TimingRow
label="Context size"
label={t`Context size`}
value={
debug.contextSizeBytes !== undefined
? formatBytes(debug.contextSizeBytes)
@@ -198,7 +204,7 @@ const TimingTab = ({ debug }: TimingTabProps) => {
}
/>
<TimingRow
label="Routing tokens"
label={t`Routing tokens`}
value={
debug.routingTotalTokens !== undefined
? formatTokenBreakdown(
@@ -210,7 +216,7 @@ const TimingTab = ({ debug }: TimingTabProps) => {
}
/>
<TimingRow
label="Agent tokens"
label={t`Agent tokens`}
value={
debug.agentTotalTokens !== undefined
? formatTokenBreakdown(
@@ -222,12 +228,8 @@ const TimingTab = ({ debug }: TimingTabProps) => {
}
/>
<TimingRow
label="Total cost"
value={
debug.totalCostInCredits !== undefined
? `${formatNumber(debug.totalCostInCredits)} credits`
: undefined
}
label={t`Total cost`}
value={totalCost !== undefined ? t`${totalCost} credits` : undefined}
/>
</StyledTimingSection>
);
@@ -279,7 +281,7 @@ const ContextTab = ({ debug, copyToClipboard }: ContextTabProps) => {
if (!debug.context) {
return (
<StyledTimingLabel>
No context was provided for this request
{t`No context was provided for this request`}
</StyledTimingLabel>
);
}
@@ -302,9 +304,10 @@ const ContextTab = ({ debug, copyToClipboard }: ContextTabProps) => {
</StyledJsonTreeContainer>
);
} catch {
const contextValue = debug.context;
return (
<StyledTimingLabel>
Failed to parse context: {debug.context}
{t`Failed to parse context: ${contextValue}`}
</StyledTimingLabel>
);
}
@@ -315,6 +318,7 @@ type RoutingDebugDisplayProps = {
};
export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
const { t } = useLingui();
const theme = useTheme();
const { copyToClipboard } = useCopyToClipboard();
const [isExpanded, setIsExpanded] = useState(false);
@@ -323,7 +327,7 @@ export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
return (
<StyledContainer>
<StyledToggleButton onClick={() => setIsExpanded(!isExpanded)}>
<StyledTimingLabel>Debug Info</StyledTimingLabel>
<StyledTimingLabel>{t`Debug Info`}</StyledTimingLabel>
{isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
) : (
@@ -338,20 +342,20 @@ export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
isActive={activeTab === 'timing'}
onClick={() => setActiveTab('timing')}
>
Timing
{t`Timing`}
</StyledTab>
<StyledTab
isActive={activeTab === 'details'}
onClick={() => setActiveTab('details')}
>
Details
{t`Details`}
</StyledTab>
{debug.context && (
<StyledTab
isActive={activeTab === 'context'}
onClick={() => setActiveTab('context')}
>
Context
{t`Context`}
</StyledTab>
)}
</StyledTabContainer>