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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user