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:
@@ -2,6 +2,7 @@ import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRe
|
||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
|
||||
import { useVectorSearchFieldInRecordIndexContextOrThrow } from '@/views/hooks/useVectorSearchFieldInRecordIndexContextOrThrow';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
@@ -28,7 +29,7 @@ export const useVectorSearchFilterActions = () => {
|
||||
displayValue: value,
|
||||
operand: ViewFilterOperand.VECTOR_SEARCH,
|
||||
type: getFilterTypeFromFieldType(vectorSearchField.type),
|
||||
label: 'Search',
|
||||
label: t`Search`,
|
||||
};
|
||||
|
||||
upsertRecordFilter(vectorSearchRecordFilter);
|
||||
|
||||
+9
-5
@@ -36,7 +36,7 @@ import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states
|
||||
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
|
||||
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
|
||||
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { IconX } from 'twenty-ui/display';
|
||||
|
||||
@@ -149,6 +149,8 @@ export const ViewPickerContentCreateMode = () => {
|
||||
setViewPickerMode('list');
|
||||
};
|
||||
|
||||
const objectLabel = objectMetadataItem.labelPlural;
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
@@ -210,8 +212,9 @@ export const ViewPickerContentCreateMode = () => {
|
||||
</ViewPickerSelectContainer>
|
||||
{availableFieldsForKanban.length === 0 && (
|
||||
<StyledFieldAvailableContainer>
|
||||
Set up a Select field on {objectMetadataItem.labelPlural} to
|
||||
create a Kanban
|
||||
<Trans>
|
||||
Set up a Select field on {objectLabel} to create a Kanban
|
||||
</Trans>
|
||||
</StyledFieldAvailableContainer>
|
||||
)}
|
||||
</>
|
||||
@@ -240,8 +243,9 @@ export const ViewPickerContentCreateMode = () => {
|
||||
</ViewPickerSelectContainer>
|
||||
{availableFieldsForCalendar.length === 0 && (
|
||||
<StyledFieldAvailableContainer>
|
||||
Set up a Date field on {objectMetadataItem.labelPlural} to
|
||||
create a Calendar
|
||||
<Trans>
|
||||
Set up a Date field on {objectLabel} to create a Calendar
|
||||
</Trans>
|
||||
</StyledFieldAvailableContainer>
|
||||
)}
|
||||
</>
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@ import { viewPickerInputNameComponentState } from '@/views/view-picker/states/vi
|
||||
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
|
||||
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
|
||||
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconChevronLeft } from 'twenty-ui/display';
|
||||
|
||||
export const ViewPickerContentEditMode = () => {
|
||||
@@ -75,7 +76,7 @@ export const ViewPickerContentEditMode = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
Edit view
|
||||
{t`Edit view`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<ViewPickerIconAndNameContainer>
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ export const ViewPickerCreateButton = () => {
|
||||
<Button
|
||||
title={t`Create`}
|
||||
onClick={handleCreateButtonClick}
|
||||
ariaLabel="Create new view"
|
||||
ariaLabel={t`Create new view`}
|
||||
accent="blue"
|
||||
fullWidth
|
||||
size="small"
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
@@ -85,7 +86,7 @@ export const ViewPickerDropdown = () => {
|
||||
<IconList size={theme.icon.size.md} />
|
||||
)}
|
||||
<StyledViewName>
|
||||
<OverflowingTextWithTooltip text={currentView?.name ?? 'All'} />
|
||||
<OverflowingTextWithTooltip text={currentView?.name ?? t`All`} />
|
||||
</StyledViewName>
|
||||
<StyledDropdownLabelAdornments>
|
||||
{isDefined(totalCount) && <>· {totalCount} </>}
|
||||
|
||||
Reference in New Issue
Block a user