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
@@ -98,8 +98,9 @@ export const SettingsAdminWorkspaceContent = ({
);
},
onError: (error) => {
const errorMessage = error.message;
enqueueErrorSnackBar({
message: `Failed to impersonate user. ${error.message}`,
message: t`Failed to impersonate user. ${errorMessage}`,
});
},
}).finally(() => {
@@ -128,8 +129,9 @@ export const SettingsAdminWorkspaceContent = ({
if (isDefined(previousValue)) {
updateFeatureFlagState(workspaceId, featureFlag, previousValue);
}
const errorMessage = error.message;
enqueueErrorSnackBar({
message: `Failed to update feature flag. ${error.message}`,
message: t`Failed to update feature flag. ${errorMessage}`,
});
},
});
@@ -146,6 +148,7 @@ export const SettingsAdminWorkspaceContent = ({
value: (
<Chip
label={activeWorkspace?.name ?? ''}
emptyLabel={t`Untitled`}
leftComponent={
<AvatarChip
avatarUrl={
@@ -5,6 +5,7 @@ import { TextInput } from '@/ui/input/components/TextInput';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { t } from '@lingui/core/macro';
import { type ConfigVariableValue } from 'twenty-shared/types';
import { CustomError } from 'twenty-shared/utils';
import { MenuItemMultiSelect } from 'twenty-ui/navigation';
@@ -109,7 +110,7 @@ export const ConfigVariableDatabaseInput = ({
label:
Array.isArray(value) && value.length > 0
? value.join(', ')
: 'Select options',
: t`Select options`,
}}
isDisabled={disabled}
hasRightElement={false}
@@ -152,7 +153,7 @@ export const ConfigVariableDatabaseInput = ({
}
}}
disabled={disabled}
placeholder={placeholder || 'Enter JSON array'}
placeholder={placeholder || t`Enter JSON array`}
/>
)}
</>
@@ -184,7 +185,7 @@ export const ConfigVariableDatabaseInput = ({
}
onChange={(text) => onChange(text)}
disabled={disabled}
placeholder={placeholder || 'Enter value'}
placeholder={placeholder || t`Enter value`}
fullWidth
/>
);
@@ -41,7 +41,7 @@ export const ConfigVariableValueInput = ({
options={variable.options}
disabled={disabled}
placeholder={
disabled ? 'Undefined' : t`Enter a value to store in database`
disabled ? t`Undefined` : t`Enter a value to store in database`
}
/>
) : (
@@ -50,7 +50,7 @@ export const SettingsAdminConfigVariables = () => {
const groupOptions = useMemo(
() => [
{ value: 'all', label: 'All Groups' },
{ value: 'all', label: t`All Groups` },
...allGroups.map((group) => ({
value: group.name,
label: group.name,
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { SettingsAdminConfigVariablesRow } from '@/settings/admin-panel/config-variables/components/SettingsAdminConfigVariablesRow';
import { Table } from '@/ui/layout/table/components/Table';
import { TableBody } from '@/ui/layout/table/components/TableBody';
@@ -20,8 +21,8 @@ export const SettingsAdminConfigVariablesTable = ({
return (
<Table>
<TableRow gridAutoColumns="5fr 3fr 1fr">
<TableHeader>Name</TableHeader>
<TableHeader align="right">Value</TableHeader>
<TableHeader>{t`Name`}</TableHeader>
<TableHeader align="right">{t`Value`}</TableHeader>
<TableHeader align="right"></TableHeader>
</TableRow>
<StyledTableBody>
@@ -1,5 +1,6 @@
import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { H2Title } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
@@ -17,25 +18,27 @@ export const SettingsAdminHealthAccountSyncCountersTable = ({
title: string;
description: string;
}) => {
const { t } = useLingui();
if (!details) {
return null;
}
const items = [
{
label: 'Active Sync',
label: t`Active Sync`,
value: details.counters.ACTIVE,
},
{
label: 'Total Jobs',
label: t`Total Jobs`,
value: details.totalJobs,
},
{
label: 'Failed Jobs',
label: t`Failed Jobs`,
value: details.failedJobs,
},
{
label: 'Failure Rate',
label: t`Failure Rate`,
value: `${details.failureRate}%`,
},
];
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { Status } from 'twenty-ui/display';
import { AdminPanelHealthServiceStatus } from '~/generated-metadata/graphql';
@@ -9,10 +10,10 @@ export const SettingsAdminHealthStatusRightContainer = ({
return (
<>
{status === AdminPanelHealthServiceStatus.OPERATIONAL && (
<Status color="green" text="Operational" weight="medium" />
<Status color="green" text={t`Operational`} weight="medium" />
)}
{status === AdminPanelHealthServiceStatus.OUTAGE && (
<Status color="red" text="Outage" weight="medium" />
<Status color="red" text={t`Outage`} weight="medium" />
)}
</>
);
@@ -38,12 +38,14 @@ export const SettingsAdminJsonDataIndicatorHealthStatus = () => {
const isAnyNode = () => true;
const serviceLabel = indicatorHealth.label;
return (
<Section>
{isDown && (
<StyledErrorMessage>
{indicatorHealth.errorMessage ||
`${indicatorHealth.label} service is unreachable`}
t`${serviceLabel} service is unreachable`}
</StyledErrorMessage>
)}
{parsedDetails && (
@@ -40,7 +40,7 @@ export const SettingsAdminQueueJobRowDropdownMenu = ({
dropdownPlacement="right-start"
clickableComponent={
<LightIconButton
aria-label="Job Actions"
aria-label={t`Job Actions`}
Icon={IconDotsVertical}
accent="tertiary"
/>
@@ -53,8 +53,9 @@ export const SettingsAdminWorkerMetricsGraph = ({
},
fetchPolicy: 'no-cache',
onError: (error) => {
const errorMessage = error.message;
enqueueErrorSnackBar({
message: `Error fetching worker metrics: ${error.message}`,
message: t`Error fetching worker metrics: ${errorMessage}`,
});
},
});