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
@@ -24,6 +24,7 @@ export type MultipleAvatarChipProps = {
forceEmptyText?: boolean;
variant?: ChipVariant;
rightComponent?: React.ReactNode;
emptyLabel?: string;
};
export const MultipleAvatarChip = ({
@@ -35,6 +36,7 @@ export const MultipleAvatarChip = ({
rightComponent,
variant = ChipVariant.Static,
forceEmptyText = false,
emptyLabel,
}: MultipleAvatarChipProps) => {
return (
<StyledChipContainer onClick={onClick} data-testid={testId}>
@@ -53,6 +55,7 @@ export const MultipleAvatarChip = ({
rightComponent={rightComponent}
clickable={isDefined(onClick)}
maxWidth={maxWidth}
emptyLabel={emptyLabel}
/>
</StyledChipContainer>
);
@@ -35,6 +35,7 @@ export type ChipProps = {
rightComponent?: (() => ReactNode) | ReactNode | null;
className?: string;
forceEmptyText?: boolean;
emptyLabel?: string;
};
const StyledDiv = withTheme(styled.div<{ theme: Theme }>`
@@ -152,6 +153,7 @@ export const Chip = ({
className,
maxWidth,
forceEmptyText = false,
emptyLabel = 'Untitled',
}: ChipProps) => {
return (
<StyledContainer
@@ -168,7 +170,7 @@ export const Chip = ({
{!isLabelHidden && label && label.trim() ? (
<OverflowingTextWithTooltip size={size} text={label} />
) : !forceEmptyText && !isLabelHidden ? (
<StyledDiv>Untitled</StyledDiv>
<StyledDiv>{emptyLabel}</StyledDiv>
) : (
''
)}
@@ -42,6 +42,7 @@ export const LinkChip = ({
onClick,
triggerEvent,
target,
emptyLabel,
}: LinkChipProps) => {
const { onClick: onClickHandler, onMouseDown: onMouseDownHandler } =
useMouseDownNavigation({
@@ -73,6 +74,7 @@ export const LinkChip = ({
accent={accent}
className={className}
maxWidth={maxWidth}
emptyLabel={emptyLabel}
/>
</StyledLink>
);
@@ -16,6 +16,7 @@ import {
export type AnimatedButtonProps = ButtonProps &
Pick<MotionProps, 'animate' | 'transition'> & {
animatedSvg: React.ReactNode;
soonLabel?: string;
};
const StyledButton = styled.button<
@@ -408,6 +409,7 @@ export const AnimatedButton = ({
transition,
dataClickOutsideId,
dataGloballyPreventClickOutside,
soonLabel = 'Soon',
}: AnimatedButtonProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
@@ -454,7 +456,7 @@ export const AnimatedButton = ({
</StyledShortcutLabel>
</>
)}
{soon && <StyledSoonPill label="Soon" />}
{soon && <StyledSoonPill label={soonLabel} />}
</StyledButton>
);
};
@@ -5,4 +5,10 @@ const StyledSoonPill = styled(Pill)`
margin-left: auto;
`;
export const ButtonSoon = () => <StyledSoonPill label="Soon" />;
type ButtonSoonProps = {
label?: string;
};
export const ButtonSoon = ({ label = 'Soon' }: ButtonSoonProps) => (
<StyledSoonPill label={label} />
);
@@ -91,14 +91,14 @@ export const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => {
<StyledContainer>
<StyledBackgroundImage
src={theme.name === 'dark' ? DARK_BACKGROUND[type] : BACKGROUND[type]}
alt="Background"
alt=""
type={type}
/>
<StyledMovingImage
src={
theme.name === 'dark' ? DARK_MOVING_IMAGE[type] : MOVING_IMAGE[type]
}
alt="Moving"
alt=""
style={{ translateX, translateY }}
transition={{ type: 'spring', stiffness: 100, damping: 10 }}
type={type}
+2 -4
View File
@@ -41,10 +41,8 @@ export {
MenuItemSelect,
} from './menu/menu-item/components/MenuItemSelect';
export { MenuItemSelectAvatar } from './menu/menu-item/components/MenuItemSelectAvatar';
export {
colorLabels,
MenuItemSelectColor,
} from './menu/menu-item/components/MenuItemSelectColor';
export type { ColorLabels } from './menu/menu-item/components/MenuItemSelectColor';
export { MenuItemSelectColor } from './menu/menu-item/components/MenuItemSelectColor';
export { MenuItemSelectTag } from './menu/menu-item/components/MenuItemSelectTag';
export type { MenuItemSuggestionProps } from './menu/menu-item/components/MenuItemSuggestion';
export { MenuItemSuggestion } from './menu/menu-item/components/MenuItemSuggestion';
@@ -10,17 +10,9 @@ import { ColorSample, type ColorSampleVariant } from '@ui/display';
import { type ThemeColor } from '@ui/theme';
import { StyledMenuItemSelect } from './MenuItemSelect';
type MenuItemSelectColorProps = {
selected: boolean;
className?: string;
onClick?: () => void;
disabled?: boolean;
focused?: boolean;
color: ThemeColor;
variant?: ColorSampleVariant;
};
export type ColorLabels = Record<ThemeColor, string>;
export const colorLabels: Record<ThemeColor, string> = {
const DEFAULT_COLOR_LABELS: ColorLabels = {
gray: 'Gray',
tomato: 'Tomato',
red: 'Red',
@@ -48,6 +40,17 @@ export const colorLabels: Record<ThemeColor, string> = {
yellow: 'Yellow',
};
type MenuItemSelectColorProps = {
selected: boolean;
className?: string;
onClick?: () => void;
disabled?: boolean;
focused?: boolean;
color: ThemeColor;
variant?: ColorSampleVariant;
colorLabels?: ColorLabels;
};
export const MenuItemSelectColor = ({
color,
selected,
@@ -56,6 +59,7 @@ export const MenuItemSelectColor = ({
disabled,
focused,
variant = 'default',
colorLabels = DEFAULT_COLOR_LABELS,
}: MenuItemSelectColorProps) => {
const theme = useTheme();