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:
@@ -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} />
|
||||
);
|
||||
|
||||
+2
-2
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
|
||||
+14
-10
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user