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
@@ -7,6 +7,7 @@ import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { capitalize } from 'twenty-shared/utils';
@@ -38,7 +39,7 @@ export const AdvancedFilterCommandMenuLogicalOperatorCell = ({
return (
<StyledContainer>
{index === 0 ? (
<StyledText>Where</StyledText>
<StyledText>{t`Where`}</StyledText>
) : index === 1 ? (
readonly ? (
<Select
@@ -5,6 +5,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -41,7 +42,7 @@ export const AdvancedFilterCommandMenuRecordFilterOperandSelect = ({
selectedOption={{
label: filter?.operand
? getOperandLabel(filter.operand)
: 'Select operand',
: t`Select operand`,
value: null,
}}
isDisabled
@@ -17,6 +17,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import { useContext } from 'react';
import { RecordFilterGroupLogicalOperator } from 'twenty-shared/types';
import { t } from '@lingui/core/macro';
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
import { IconLibraryPlus, IconPlus } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
@@ -149,8 +150,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({
<ActionButton
action={{
Icon: IconPlus,
label: 'Add rule',
shortLabel: 'Add rule',
label: t`Add rule`,
shortLabel: t`Add rule`,
key: 'add-rule',
}}
onClick={handleAddFilter}
@@ -165,8 +166,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({
<ActionButton
action={{
Icon: IconPlus,
label: 'Add filter rule',
shortLabel: 'Add filter rule',
label: t`Add filter rule`,
shortLabel: t`Add filter rule`,
key: 'add-filter-rule',
}}
/>
@@ -176,13 +177,13 @@ export const AdvancedFilterAddFilterRuleSelect = ({
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPlus}
text="Add rule"
text={t`Add rule`}
onClick={handleAddFilter}
/>
{isFilterRuleGroupOptionVisible && (
<MenuItem
LeftIcon={IconLibraryPlus}
text="Add rule group"
text={t`Add rule group`}
onClick={handleAddFilterGroup}
/>
)}
@@ -4,6 +4,7 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { TextInput } from '@/ui/input/components/TextInput';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
export const AdvancedFilterDropdownNumberInput = () => {
const selectedOperandInDropdown = useRecoilComponentValue(
@@ -32,7 +33,7 @@ export const AdvancedFilterDropdownNumberInput = () => {
<TextInput
value={objectFilterDropdownFilterValue}
onChange={handleChange}
placeholder="Enter value"
placeholder={t`Enter value`}
fullWidth
type="number"
/>
@@ -1,6 +1,7 @@
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { TextInput } from '@/ui/input/components/TextInput';
import { t } from '@lingui/core/macro';
type AdvancedFilterDropdownTextInputProps = {
recordFilter: RecordFilter;
@@ -20,7 +21,7 @@ export const AdvancedFilterDropdownTextInput = ({
<TextInput
value={recordFilter.value}
onChange={handleChange}
placeholder="Enter value"
placeholder={t`Enter value`}
fullWidth
/>
);
@@ -2,6 +2,7 @@ import { AdvancedFilterLogicalOperatorDropdown } from '@/object-record/advanced-
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { capitalize } from 'twenty-shared/utils';
const StyledText = styled.div`
@@ -31,7 +32,7 @@ export const AdvancedFilterLogicalOperatorCell = ({
return (
<StyledContainer>
{index === 0 ? (
<StyledText>Where</StyledText>
<StyledText>{t`Where`}</StyledText>
) : index === 1 ? (
<AdvancedFilterLogicalOperatorDropdown
recordFilterGroup={recordFilterGroup}
@@ -7,6 +7,7 @@ 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 { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { t } from '@lingui/core/macro';
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
@@ -48,7 +49,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
dropdownId={dropdownId}
clickableComponent={
<IconButton
aria-label="Filter group rule options"
aria-label={t`Filter group rule options`}
variant="tertiary"
Icon={IconDotsVertical}
/>
@@ -57,7 +58,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule group"
text={t`Remove rule group`}
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
@@ -5,6 +5,7 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get
import { SelectControl } from '@/ui/input/components/SelectControl';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
const StyledContainer = styled.div`
@@ -43,7 +44,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
selectedOption={{
label: filter?.operand
? getOperandLabel(filter.operand)
: 'Select operand',
: t`Select operand`,
value: null,
}}
isDisabled
@@ -14,6 +14,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { type ViewFilterOperand } from 'twenty-shared/types';
import { MenuItem } from 'twenty-ui/navigation';
@@ -54,7 +55,7 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
selectedOption={{
label: filter?.operand
? getOperandLabel(filter.operand)
: 'Select operand',
: t`Select operand`,
value: null,
}}
/>
@@ -11,6 +11,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
@@ -68,7 +69,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
dropdownId={dropdownId}
clickableComponent={
<IconButton
aria-label="Record filter rule options"
aria-label={t`Record filter rule options`}
variant="tertiary"
Icon={IconDotsVertical}
/>
@@ -77,7 +78,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule"
text={t`Remove rule`}
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
@@ -23,6 +23,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { t } from '@lingui/core/macro';
import { IconChevronLeft, useIcons } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
@@ -112,6 +113,8 @@ export const AdvancedFilterSubFieldSelectMenu = ({
...subFieldNames.map((subFieldName) => subFieldName),
];
const fieldLabel = fieldMetadataItemUsedInDropdown?.label;
return (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
@@ -146,7 +149,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
handleSelectFilter(fieldMetadataItemUsedInDropdown);
}}
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
text={`Any ${fieldMetadataItemUsedInDropdown.label} field`}
text={t`Any ${fieldLabel} field`}
/>
</SelectableListItem>
)}
@@ -9,6 +9,7 @@ import styled from '@emotion/styled';
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
import { useGetRecordFilterDisplayValue } from '@/object-record/record-filter/hooks/useGetRecordFilterDisplayValue';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
// TODO: factorize this with https://github.com/twentyhq/core-team-issues/issues/752
@@ -57,7 +58,7 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({
const placeholderText = isDefined(fieldMetadataItem)
? getAdvancedFilterInputPlaceholderText(fieldMetadataItem)
: 'Enter filter';
: t`Enter filter`;
const recordFilterDisplayValue = getRecordFilterDisplayValue(recordFilter);
@@ -6,6 +6,7 @@ import { recordIndexOpenRecordInState } from '@/object-record/record-index/state
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { canOpenObjectInSidePanel } from '@/object-record/utils/canOpenObjectInSidePanel';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
import { t } from '@lingui/core/macro';
import { type MouseEvent } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
@@ -81,6 +82,7 @@ export const RecordChip = ({
return (
<Chip
label={recordChipData.name}
emptyLabel={t`Untitled`}
size={size}
maxWidth={maxWidth}
className={className}
@@ -104,6 +106,7 @@ export const RecordChip = ({
size={size}
maxWidth={maxWidth}
label={recordChipData.name}
emptyLabel={t`Untitled`}
isLabelHidden={isLabelHidden}
leftComponent={
isIconHidden ? null : (
@@ -1,5 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
@@ -45,7 +46,7 @@ export const ObjectFilterDropdownBooleanSelect = () => {
const handleOptionSelect = (newValue: boolean) => {
applyObjectFilterDropdownFilterValue(
newValue.toString(),
newValue ? 'True' : 'False',
newValue ? t`True` : t`False`,
);
closeDropdown();
@@ -8,6 +8,7 @@ import { type SelectableItem } from '@/object-record/select/types/SelectableItem
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
@@ -64,9 +65,10 @@ export const ObjectFilterDropdownSourceSelect = ({
.filter((option) => newSelectedItemIds.includes(option.id))
.map((option) => option.name);
const selectedCount = selectedItemNames.length;
const filterDisplayValue =
selectedItemNames.length > MAX_ITEMS_TO_DISPLAY
? `${selectedItemNames.length} source types`
? t`${selectedCount} source types`
: selectedItemNames.join(', ');
const newFilterValue =
@@ -50,7 +50,7 @@ export const ObjectOptionsDropdownCustomView = ({
? {
...currentView,
key: ViewKey.Custom,
name: currentView.name || 'Custom View',
name: currentView.name || t`Custom View`,
}
: null;
@@ -76,6 +76,8 @@ export const ObjectOptionsDropdownCustomView = ({
viewBarId: recordIndexId,
});
const visibleFieldsCount = visibleBoardFields.length;
const { deleteViewFromCurrentState } = useDeleteViewFromCurrentState();
const setViewPickerReferenceViewId = useSetRecoilComponentState(
viewPickerReferenceViewIdComponentState,
@@ -211,7 +213,7 @@ export const ObjectOptionsDropdownCustomView = ({
onClick={() => onContentChange('fields')}
LeftIcon={IconListDetails}
text={t`Fields`}
contextualText={`${visibleBoardFields.length} shown`}
contextualText={t`${visibleFieldsCount} shown`}
contextualTextPosition="right"
hasSubMenu
/>
@@ -35,6 +35,8 @@ export const ObjectOptionsDropdownDefaultView = () => {
viewBarId: recordIndexId,
});
const visibleFieldsCount = visibleBoardFields.length;
const selectableItemIdArray = [
'Fields',
'Copy link to view',
@@ -86,7 +88,7 @@ export const ObjectOptionsDropdownDefaultView = () => {
onClick={() => onContentChange('fields')}
LeftIcon={IconListDetails}
text={t`Fields`}
contextualText={`${visibleBoardFields.length} shown`}
contextualText={t`${visibleFieldsCount} shown`}
contextualTextPosition="right"
hasSubMenu
/>
@@ -75,10 +75,10 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
/>
}
>
Hidden {recordGroupFieldMetadata?.label}
{t`Hidden`} {recordGroupFieldMetadata?.label}
</DropdownMenuHeader>
<RecordGroupsVisibilityDropdownSection
title={`Hidden ${recordGroupFieldMetadata?.label}`}
title={`${t`Hidden`} ${recordGroupFieldMetadata?.label}`}
recordGroupIds={hiddenRecordGroupIds}
onVisibilityChange={handleRecordGroupVisibilityChange}
isDraggable={false}
@@ -14,6 +14,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import {
IconChevronLeft,
IconHandMove,
@@ -67,7 +68,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
/>
}
>
Sort
{t`Sort`}
</DropdownMenuHeader>
<DropdownMenuItemsContainer>
<SelectableList
@@ -106,7 +106,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
/>
}
>
Group
{t`Group`}
</DropdownMenuHeader>
<DropdownMenuItemsContainer>
<SelectableList
@@ -187,7 +187,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
<MenuItemNavigate
onClick={() => onContentChange('hiddenRecordGroups')}
LeftIcon={IconEyeOff}
text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`}
text={`${t`Hidden`} ${recordGroupFieldMetadata?.label ?? ''}`}
/>
</SelectableListItem>
</SelectableList>
@@ -6,6 +6,7 @@ import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useC
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { IconPlus } from 'twenty-ui/display';
@@ -66,7 +67,7 @@ export const RecordBoardColumnNewRecordButton = () => {
}}
>
<IconPlus size={theme.icon.size.md} />
New
{t`New`}
</StyledNewButton>
);
};
@@ -91,15 +91,15 @@ export const RecordCalendarTopBar = () => {
selectSizeVariant="small"
options={[
{
label: 'Month',
label: t`Month`,
value: ViewCalendarLayout.MONTH,
},
{
label: 'Week',
label: t`Week`,
value: ViewCalendarLayout.WEEK,
},
{
label: 'Timeline',
label: t`Timeline`,
value: ViewCalendarLayout.DAY,
},
]}
@@ -7,6 +7,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import {
combineFilters,
@@ -56,7 +57,7 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
value: `${firstDayOfFirstWeek.toISOString()}`,
operand: RecordFilterOperand.IS_AFTER,
type: 'DATE',
label: 'After',
label: t`After`,
displayValue: `${firstDayOfFirstWeek.toISOString()}`,
};
@@ -66,7 +67,7 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
value: `${lastDayOfLastWeek.toISOString()}`,
operand: RecordFilterOperand.IS_BEFORE,
type: 'DATE',
label: 'Before',
label: t`Before`,
displayValue: `${lastDayOfLastWeek.toISOString()}`,
};
@@ -35,6 +35,8 @@ import {
computeMorphRelationFieldName,
CustomError,
} from 'twenty-shared/utils';
import { Trans } from '@lingui/react/macro';
import { t } from '@lingui/core/macro';
import {
IconChevronDown,
IconDotsVertical,
@@ -263,14 +265,14 @@ export const RecordDetailRelationRecordsListItem = ({
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconUnlink}
text="Detach"
text={t`Detach`}
onClick={handleDetach}
/>
{!isAccountOwnerRelation &&
relationObjectPermissions.canSoftDeleteObjectRecords && (
<MenuItem
LeftIcon={IconTrash}
text="Delete"
text={t`Delete`}
accent="danger"
onClick={handleDelete}
/>
@@ -295,17 +297,17 @@ export const RecordDetailRelationRecordsListItem = ({
{createPortal(
<ConfirmationModal
modalId={getDeleteRelationModalId(relationRecord.id)}
title={`Delete Related ${relationObjectTypeName}`}
title={t`Delete Related ${relationObjectTypeName}`}
subtitle={
<>
<Trans>
Are you sure you want to delete this related{' '}
{relationObjectMetadataNameSingular}?
<br />
This action will break all its relationships with other objects.
</>
</Trans>
}
onConfirmClick={handleConfirmDelete}
confirmButtonText={`Delete ${relationObjectTypeName}`}
confirmButtonText={t`Delete ${relationObjectTypeName}`}
/>,
document.body,
)}
@@ -23,7 +23,7 @@ export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => {
onClick={() => {
copyToClipboard(copyText, t`Text copied to clipboard`);
}}
aria-label="Copy to Clipboard"
aria-label={t`Copy to Clipboard`}
/>
</StyledButtonContainer>
);
@@ -6,6 +6,7 @@ import { type VariablePickerComponent } from '@/object-record/record-field/ui/fo
import { type FieldAddressDraftValue } from '@/object-record/record-field/ui/types/FieldInputDraftValue';
import { type FieldAddressValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { t } from '@lingui/core/macro';
type FormAddressFieldInputProps = {
label?: string;
@@ -43,47 +44,47 @@ export const FormAddressFieldInput = ({
{label ? <InputLabel>{label}</InputLabel> : null}
<FormNestedFieldInputContainer>
<FormTextFieldInput
label="Address 1"
label={t`Address 1`}
defaultValue={defaultValue?.addressStreet1 ?? ''}
onChange={handleChange('addressStreet1')}
readonly={readonly}
VariablePicker={VariablePicker}
placeholder="Street address"
placeholder={t`Street address`}
/>
<FormTextFieldInput
label="Address 2"
label={t`Address 2`}
defaultValue={defaultValue?.addressStreet2 ?? ''}
onChange={handleChange('addressStreet2')}
readonly={readonly}
VariablePicker={VariablePicker}
placeholder="Street address 2"
placeholder={t`Street address 2`}
/>
<FormTextFieldInput
label="City"
label={t`City`}
defaultValue={defaultValue?.addressCity ?? ''}
onChange={handleChange('addressCity')}
readonly={readonly}
VariablePicker={VariablePicker}
placeholder="City"
placeholder={t`City`}
/>
<FormTextFieldInput
label="State"
label={t`State`}
defaultValue={defaultValue?.addressState ?? ''}
onChange={handleChange('addressState')}
readonly={readonly}
VariablePicker={VariablePicker}
placeholder="State"
placeholder={t`State`}
/>
<FormTextFieldInput
label="Post Code"
label={t`Post Code`}
defaultValue={defaultValue?.addressPostcode ?? ''}
onChange={handleChange('addressPostcode')}
readonly={readonly}
VariablePicker={VariablePicker}
placeholder="Post Code"
placeholder={t`Post Code`}
/>
<FormCountrySelectInput
label="Country"
label={t`Country`}
selectedCountryName={defaultValue?.addressCountry ?? ''}
onChange={handleChange('addressCountry')}
readonly={readonly}
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
import { t } from '@lingui/core/macro';
import type { CountryCode } from 'libphonenumber-js';
import { IconCircleOff, type IconComponentProps } from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
@@ -35,7 +36,7 @@ export const FormCountryCodeSelectInput = ({
);
return [
{
label: 'No country',
label: t`No country`,
value: '',
Icon: IconCircleOff,
},
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { useMemo } from 'react';
import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
@@ -32,7 +33,7 @@ export const FormCountrySelectInput = ({
);
return [
{
label: 'No country',
label: t`No country`,
value: '',
Icon: IconCircleOff,
},
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput';
@@ -56,7 +57,7 @@ export const FormCurrencyFieldInput = ({
const currencies = useMemo(() => {
return [
{
label: 'No currency',
label: t`No currency`,
value: '',
Icon: IconCircleOff,
},
@@ -85,7 +86,7 @@ export const FormCurrencyFieldInput = ({
{label ? <InputLabel>{label}</InputLabel> : null}
<FormNestedFieldInputContainer>
<FormSelectFieldInput
label="Currency Code"
label={t`Currency Code`}
defaultValue={defaultValue?.currencyCode ?? ''}
onChange={handleCurrencyCodeChange}
options={currencies}
@@ -93,11 +94,11 @@ export const FormCurrencyFieldInput = ({
readonly={readonly}
/>
<FormNumberFieldInput
label="Amount"
label={t`Amount`}
defaultValue={formatMicrosToDisplayAmount(defaultValue?.amountMicros)}
onChange={handleAmountMicrosChange}
VariablePicker={VariablePicker}
placeholder="Set 3.21 for $3.21"
placeholder={t`Set 3.21 for $3.21`}
readonly={readonly}
/>
</FormNestedFieldInputContainer>
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
@@ -41,7 +42,7 @@ export const FormFullNameFieldInput = ({
{label ? <InputLabel>{label}</InputLabel> : null}
<FormNestedFieldInputContainer>
<FormTextFieldInput
label="First Name"
label={t`First Name`}
defaultValue={defaultValue?.firstName}
onChange={handleFirstNameChange}
placeholder={
@@ -51,7 +52,7 @@ export const FormFullNameFieldInput = ({
VariablePicker={VariablePicker}
/>
<FormTextFieldInput
label="Last Name"
label={t`Last Name`}
defaultValue={defaultValue?.lastName}
onChange={handleLastNameChange}
placeholder={
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
@@ -39,18 +40,18 @@ export const FormLinksFieldInput = ({
{label ? <InputLabel>{label}</InputLabel> : null}
<FormNestedFieldInputContainer>
<FormTextFieldInput
label="Primary Link Label"
label={t`Primary Link Label`}
defaultValue={defaultValue?.primaryLinkLabel}
onChange={handleChange('primaryLinkLabel')}
placeholder={placeholder ?? 'Primary Link Label'}
placeholder={placeholder ?? t`Primary Link Label`}
readonly={readonly}
VariablePicker={VariablePicker}
/>
<FormTextFieldInput
label="Primary Link URL"
label={t`Primary Link URL`}
defaultValue={defaultValue?.primaryLinkUrl}
onChange={handleChange('primaryLinkUrl')}
placeholder={placeholder ?? 'Primary Link URL'}
placeholder={placeholder ?? t`Primary Link URL`}
readonly={readonly}
VariablePicker={VariablePicker}
/>
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
@@ -232,7 +233,7 @@ export const FormMultiSelectFieldInput = ({
data-open={draftValue.editingMode === 'edit'}
onClick={handleDisplayModeClick}
>
<VisibilityHidden>Edit</VisibilityHidden>
<VisibilityHidden>{t`Edit`}</VisibilityHidden>
{isDefined(selectedOptions) && selectedOptions.length > 0 ? (
<MultiSelectDisplay
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
@@ -74,8 +75,8 @@ export const FormNumberFieldInput = ({
const persistNumber = (newValue: string) => {
if (!canBeCastAsNumberOrNull(newValue)) {
setErrorMessage('Invalid number');
onError?.('Invalid number');
setErrorMessage(t`Invalid number`);
onError?.(t`Invalid number`);
return;
}
@@ -117,7 +118,7 @@ export const FormNumberFieldInput = ({
placeholder={
isDefined(placeholder) && !isEmpty(placeholder)
? placeholder
: 'Enter a number'
: t`Enter a number`
}
value={draftValue.value}
copyButton={false}
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import {
FormCountryCodeSelectInput,
type FormCountryCodeSelectInputUpdatedValue,
@@ -55,18 +56,18 @@ export const FormPhoneFieldInput = ({
{label && <InputLabel>{label}</InputLabel>}
<FormNestedFieldInputContainer>
<FormCountryCodeSelectInput
label="Country Code"
label={t`Country Code`}
selectedCountryCode={defaultValue?.primaryPhoneCountryCode ?? ''}
onChange={handleCountryChange}
readonly={readonly}
/>
<FormNumberFieldInput
label="Phone Number"
label={t`Phone Number`}
defaultValue={defaultValue?.primaryPhoneNumber ?? ''}
onChange={handleNumberChange}
VariablePicker={VariablePicker}
placeholder="Enter phone number"
hint="Without calling code"
placeholder={t`Enter phone number`}
hint={t`Without calling code`}
readonly={readonly}
/>
</FormNestedFieldInputContainer>
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
@@ -34,7 +35,7 @@ export const FormRawJsonFieldInput = ({
const instanceId = useId();
const editor = useTextVariableEditor({
placeholder: placeholder ?? 'Enter a JSON object',
placeholder: placeholder ?? t`Enter a JSON object`,
multiline: true,
readonly,
defaultValue: defaultValue ?? undefined,
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
@@ -96,7 +97,7 @@ export const FormSelectFieldInput = ({
);
const defaultEmptyOption = {
label: `No ${label ?? 'value'}`,
label: label ? t`No ${label}` : t`No value`,
value: '',
icon: IconCircleOff,
};
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { RecordChip } from '@/object-record/components/RecordChip';
import { FormFieldPlaceholder } from '@/object-record/record-field/ui/form-types/components/FormFieldPlaceholder';
import {
@@ -63,5 +64,7 @@ export const FormSingleRecordFieldChip = ({
);
}
return <StyledPlaceholder>Select a {objectNameSingular}</StyledPlaceholder>;
return (
<StyledPlaceholder>{t`Select a ${objectNameSingular}`}</StyledPlaceholder>
);
};
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
@@ -159,6 +160,8 @@ export const FormSingleRecordPicker = ({
}
};
const objectNames = objectNameSingulars.join(' or ');
return (
<FormFieldInputContainer data-testid={testId}>
{label ? <InputLabel>{label}</InputLabel> : null}
@@ -211,7 +214,7 @@ export const FormSingleRecordPicker = ({
focusId={dropdownId}
componentInstanceId={dropdownId}
EmptyIcon={IconForbid}
emptyLabel={'No ' + objectNameSingulars.join(' or ')}
emptyLabel={t`No ${objectNames}`}
onCancel={() => closeDropdown(dropdownId)}
onMorphItemSelected={handleMorphItemSelected}
objectNameSingulars={objectNameSingulars}
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
@@ -39,7 +40,7 @@ export const FormTextFieldInput = ({
const instanceId = useId();
const editor = useTextVariableEditor({
placeholder: placeholder ?? 'Enter text',
placeholder: placeholder ?? t`Enter text`,
multiline,
readonly,
defaultValue,
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputInnerContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
@@ -101,7 +102,7 @@ export const FormUuidFieldInput = ({
{draftValue.type === 'static' ? (
<StyledInput
instanceId={instanceId}
placeholder={placeholder ?? 'Enter a UUID'}
placeholder={placeholder ?? t`Enter a UUID`}
value={draftValue.value}
copyButton={false}
disabled={readonly}
@@ -109,7 +109,7 @@ export const VariableChip = ({
{onRemove ? (
<StyledDelete
onClick={onRemove}
aria-label="Remove variable"
aria-label={t`Remove variable`}
danger={isVariableNotFound}
>
<IconX size={theme.icon.size.sm} stroke={theme.icon.stroke.sm} />
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useArrayField } from '@/object-record/record-field/ui/meta-types/hooks/useArrayField';
import { ArrayFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/ArrayFieldMenuItem';
@@ -62,13 +63,13 @@ export const ArrayFieldInput = () => {
return (
<MultiItemFieldInput
newItemLabel="Add Item"
newItemLabel={t`Add Item`}
items={arrayItems}
onChange={handleChange}
onEnter={handleEnter}
onEscape={handleEscape}
onClickOutside={handleClickOutside}
placeholder="Enter value"
placeholder={t`Enter value`}
fieldMetadataType={FieldMetadataType.ARRAY}
renderItem={({ value, index, handleEdit, handleDelete }) => (
<ArrayFieldMenuItem
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { type FieldCurrencyValue } from '@/object-record/record-field/ui/types/FieldMetadata';
@@ -137,7 +138,7 @@ export const CurrencyFieldInput = () => {
value={draftValue?.amount?.toString() ?? ''}
currencyCode={currencyCode}
autoFocus
placeholder="Currency"
placeholder={t`Currency`}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
@@ -109,7 +109,7 @@ export const EmailsFieldInput = () => {
onEnter={handleEnter}
onEscape={handleEscape}
onClickOutside={handleClickOutside}
placeholder="Email"
placeholder={t`Email`}
fieldMetadataType={FieldMetadataType.EMAILS}
validateInput={validateInput}
renderItem={({
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
@@ -76,12 +77,14 @@ export const MorphRelationManyToOneFieldInput = () => {
(morphRelation) => morphRelation.targetObjectMetadata.nameSingular,
);
const fieldLabel = fieldDefinition.label;
return (
<SingleRecordPicker
focusId={instanceId}
componentInstanceId={instanceId}
EmptyIcon={IconForbid}
emptyLabel={'No ' + fieldDefinition.label}
emptyLabel={t`No ${fieldLabel}`}
onCancel={onCancel}
onMorphItemSelected={handleMorphItemSelected}
objectNameSingulars={objectNameSingulars}
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
@@ -86,25 +87,25 @@ export const MultiItemFieldMenuItem = <T,>({
{showSetAsPrimaryButton && (
<MenuItem
LeftIcon={IconBookmarkPlus}
text="Set as Primary"
text={t`Set as Primary`}
onClick={handleSetAsPrimaryClick}
/>
)}
<MenuItem
LeftIcon={IconPencil}
text="Edit"
text={t`Edit`}
onClick={handleEditClick}
/>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text="Delete"
text={t`Delete`}
onClick={handleDeleteClick}
/>
{showCopyButton && (
<MenuItem
LeftIcon={IconCopy}
text="Copy"
text={t`Copy`}
onClick={handleCopyClick}
/>
)}
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { usePhonesField } from '@/object-record/record-field/ui/meta-types/hooks/usePhonesField';
import { PhonesFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/PhonesFieldMenuItem';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState';
@@ -160,7 +161,7 @@ export const PhonesFieldInput = () => {
onClickOutside={handleClickOutside}
onEscape={handleEscape}
onEnter={handleEnter}
placeholder="Phone"
placeholder={t`Phone`}
fieldMetadataType={FieldMetadataType.PHONES}
validateInput={validateInput}
formatInput={(input) => {
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useClearField } from '@/object-record/record-field/ui/hooks/useClearField';
import { useAddSelectOption } from '@/object-record/record-field/ui/meta-types/hooks/useAddSelectOption';
@@ -68,8 +69,9 @@ export const SelectFieldInput = () => {
dependencies: [onCancel, resetSelectedItem],
});
const fieldLabel = fieldDefinition.label;
const optionIds = [
`No ${fieldDefinition.label}`,
t`No ${fieldLabel}`,
...filteredOptions.map((option) => option.value),
];
@@ -10,7 +10,9 @@ import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { ArrayFieldInput } from '../ArrayFieldInput';
const { FieldInputEventContextProviderWithJestMocks } =
@@ -100,6 +102,7 @@ const ArrayInputWithContext = ({
const meta: Meta<typeof ArrayInputWithContext> = {
title: 'UI/Input/ArrayFieldInput',
component: ArrayInputWithContext,
decorators: [I18nFrontDecorator],
};
export default meta;
@@ -7,6 +7,7 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
@@ -84,6 +85,7 @@ const BooleanFieldInputWithContext = ({
const meta: Meta = {
title: 'UI/Data/Field/Input/BooleanFieldInput',
component: BooleanFieldInputWithContext,
decorators: [I18nFrontDecorator],
args: {
value: true,
recordId: 'id-1',
@@ -11,6 +11,7 @@ import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFi
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { LinksFieldInput } from '../LinksFieldInput';
@@ -121,6 +122,7 @@ const getPrimaryLinkBookmarkIcon = (canvasElement: HTMLElement) =>
const meta: Meta = {
title: 'UI/Data/Field/Input/LinksFieldInput',
component: LinksInputWithContext,
decorators: [I18nFrontDecorator],
args: {
value: {
primaryLinkUrl: null,
@@ -4,6 +4,7 @@ import { useEffect } from 'react';
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { usePhonesField } from '@/object-record/record-field/ui/meta-types/hooks/usePhonesField';
import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
@@ -98,6 +99,7 @@ const PhoneInputWithContext = ({
const meta: Meta<typeof PhoneInputWithContext> = {
title: 'UI/Input/PhonesFieldInput',
component: PhoneInputWithContext,
decorators: [I18nFrontDecorator],
};
export default meta;
@@ -5,6 +5,7 @@ import { useEffect } from 'react';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
@@ -107,7 +108,7 @@ const meta: Meta = {
argTypes: {
onSubmit: { control: false },
},
decorators: [clearMocksDecorator],
decorators: [I18nFrontDecorator, clearMocksDecorator],
parameters: {
clearMocks: true,
},
@@ -4,6 +4,7 @@ import {
RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecoilValue } from 'recoil';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { Tag } from 'twenty-ui/components';
import { IconEye, IconEyeOff } from 'twenty-ui/display';
@@ -33,12 +34,13 @@ export const RecordGroupMenuItemDraggable = ({
const isNoValue = recordGroup.type === RecordGroupDefinitionType.NoValue;
const getIconButtons = (recordGroup: RecordGroupDefinition) => {
const groupValue = recordGroup.value;
const iconButtons = [
{
Icon: recordGroup.isVisible ? IconEyeOff : IconEye,
ariaLabel: recordGroup.isVisible
? `Hide group ${recordGroup.value}`
: `Show group ${recordGroup.value}`,
? t`Hide group ${groupValue}`
: t`Show group ${groupValue}`,
dataTestId: recordGroup.isVisible
? `hide-group-${recordGroup.value?.toLowerCase().replace(' ', '-') ?? ''}`
: `show-group-${recordGroup.value?.toLowerCase().replace(' ', '-') ?? ''}`,
@@ -132,13 +132,15 @@ export const displayedExportProgress = (progress?: ExportProgress): string => {
progress.displayType === 'percentage' &&
isDefined(progress?.totalRecordCount)
) {
return `Export (${percentage(
const percentageValue = percentage(
progress.exportedRecordCount,
progress.totalRecordCount,
)}%)`;
);
return t`Export (${percentageValue}%)`;
}
return `Export (${progress.exportedRecordCount})`;
const exportedCount = progress.exportedRecordCount;
return t`Export (${exportedCount})`;
};
const downloader = (mimeType: string, generator: GenerateExport) => {
@@ -7,6 +7,7 @@ import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUp
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { t } from '@lingui/core/macro';
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
import { useRecoilCallback } from 'recoil';
@@ -60,7 +61,7 @@ export const useHandleToggleTrashColumnFilter = ({
operand: ViewFilterOperand.IS_NOT_EMPTY,
displayValue: '',
type: filterType,
label: `Deleted`,
label: t`Deleted`,
value: '',
};
@@ -3,6 +3,7 @@ import { useMergeRecordsActions } from '@/object-record/record-merge/hooks/useMe
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import styled from '@emotion/styled';
import { Key } from 'ts-key-enum';
import { t } from '@lingui/core/macro';
import { IconArrowMerge } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
@@ -48,7 +49,7 @@ export const MergeRecordsFooter = ({
<StyledFooterContainer>
<StyledFooterActions>
<Button
title={isMerging ? 'Merging...' : 'Merge'}
title={isMerging ? t`Merging...` : t`Merge`}
variant="primary"
accent="blue"
size="medium"
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { useMergeRecordsSelectedRecords } from '@/object-record/record-merge/hooks/useMergeRecordsSelectedRecords';
import { useMergeRecordsSettings } from '@/object-record/record-merge/hooks/useMergeRecordsSettings';
import { Select } from '@/ui/input/components/Select';
@@ -16,12 +17,15 @@ export const MergeSettingsTab = () => {
useMergeRecordsSettings();
const { selectedRecords } = useMergeRecordsSelectedRecords();
const priorityOptions = selectedRecords.map((_, index) => ({
value: index,
label: `${getPositionWordLabel(index)} record holds priority`,
Icon: getPositionNumberIcon(index),
recordIndex: index,
}));
const priorityOptions = selectedRecords.map((_, index) => {
const positionLabel = getPositionWordLabel(index);
return {
value: index,
label: t`${positionLabel} record holds priority`,
Icon: getPositionNumberIcon(index),
recordIndex: index,
};
});
const handleSelectionChange = (index: number) => {
updatePriorityRecordIndex(index);
@@ -38,7 +42,7 @@ export const MergeSettingsTab = () => {
options={priorityOptions}
value={mergeSettings.conflictPriorityIndex}
onChange={handleSelectionChange}
label="Fields conflicts"
label={t`Fields conflicts`}
/>
</StyledSection>
);
@@ -61,7 +61,7 @@ export const useMergeRecordsActions = ({
message:
error instanceof Error
? error.message
: 'Failed to merge records. Please try again.',
: t`Failed to merge records. Please try again.`,
});
} finally {
setMergeInProgress(false);
@@ -18,6 +18,7 @@ import { useRef } from 'react';
import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum';
import { isDefined } from 'twenty-shared/utils';
import { t } from '@lingui/core/macro';
import { IconPlus } from 'twenty-ui/display';
type MultipleRecordPickerProps = {
@@ -118,7 +119,7 @@ export const MultipleRecordPicker = ({
<CreateNewButton
onClick={handleCreateNewButtonClick}
LeftIcon={IconPlus}
text="Add New"
text={t`Add New`}
/>
</DropdownMenuItemsContainer>
) : null;
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext';
@@ -118,7 +119,7 @@ export const MultipleRecordPickerFetchMoreLoader = () => {
<>
<StyledIntersectionObserver ref={ref} />
{multipleRecordPickerIsFetchingMore && (
<StyledText>Loading more...</StyledText>
<StyledText>{t`Loading more...`}</StyledText>
)}
</>
);
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
@@ -62,9 +63,9 @@ export const MultipleRecordPickerMenuItemContent = ({
});
};
const labelSingular = objectMetadataItem.labelSingular;
const displayText =
searchRecord.label?.trim() ||
`Untitled ${objectMetadataItem.labelSingular}`;
searchRecord.label?.trim() || t`Untitled ${labelSingular}`;
const searchableObjectMetadataItems = useRecoilComponentValue(
multipleRecordPickerSearchableObjectMetadataItemsComponentState,
@@ -17,6 +17,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { isDefined } from 'twenty-shared/utils';
import { t } from '@lingui/core/macro';
import { IconPlus } from 'twenty-ui/display';
export type SingleRecordPickerMenuItemsWithSearchProps = {
@@ -88,7 +89,7 @@ export const SingleRecordPickerMenuItemsWithSearch = ({
<CreateNewButton
onClick={handleCreateNew}
LeftIcon={IconPlus}
text="Add New"
text={t`Add New`}
/>
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
@@ -137,7 +138,7 @@ export const SingleRecordPickerMenuItemsWithSearch = ({
<CreateNewButton
onClick={handleCreateNew}
LeftIcon={IconPlus}
text="Add New"
text={t`Add New`}
/>
</DropdownMenuItemsContainer>
</>
@@ -17,6 +17,7 @@ import { evaluateTabVisibility } from '@/object-record/record-show/utils/evaluat
import { type RecordLayoutTab } from '@/ui/layout/tab-list/types/RecordLayoutTab';
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useLingui } from '@lingui/react/macro';
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { IconHome, useIcons } from 'twenty-ui/display';
@@ -40,6 +41,7 @@ export const useRecordShowContainerTabs = (
isInRightDrawer: boolean,
objectMetadataItem: ObjectMetadataItem,
): { layout: RecordLayout; tabs: SingleTabProps[] } => {
const { t } = useLingui();
const isMobile = useIsMobile();
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
@@ -109,7 +111,7 @@ export const useRecordShowContainerTabs = (
...acc,
{
id: 'home',
title: 'Home',
title: t`Home`,
Icon: IconHome,
cards: [
...(tab.hide ? [] : tab.cards),
@@ -1,4 +1,5 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
@@ -16,6 +17,7 @@ export const useRecordShowPagePagination = (
propsObjectNameSingular: string,
propsObjectRecordId: string,
) => {
const { t } = useLingui();
const {
objectNameSingular: paramObjectNameSingular,
objectRecordId: paramObjectRecordId,
@@ -220,9 +222,10 @@ export const useRecordShowPagePagination = (
const totalCount = 1 + Math.max(totalCountBefore, totalCountAfter);
const currentRank = rankInView + 1;
const viewNameWithCount = rankFoundInView
? `${rankInView + 1} of ${totalCount} in ${objectLabelPlural}`
: `${objectLabelPlural} (${totalCount})`;
? t`${currentRank} of ${totalCount} in ${objectLabelPlural}`
: t`${objectLabelPlural} (${totalCount})`;
return {
viewName: viewNameWithCount,
@@ -2,6 +2,7 @@ import { useObjectLabel } from '@/object-metadata/hooks/useObjectLabel';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay';
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
import { t } from '@lingui/core/macro';
import { IconPlus } from 'twenty-ui/display';
export const RecordTableEmptyStateNoRecordFoundForFilter = () => {
@@ -17,11 +18,11 @@ export const RecordTableEmptyStateNoRecordFoundForFilter = () => {
const objectLabelSingular = useObjectLabel(objectMetadataItem);
const buttonTitle = `Add a ${objectLabelSingular}`;
const buttonTitle = t`Add a ${objectLabelSingular}`;
const title = `No ${objectLabelSingular} found`;
const title = t`No ${objectLabelSingular} found`;
const subTitle = 'No records matching the filter criteria were found.';
const subTitle = t`No records matching the filter criteria were found.`;
return (
<RecordTableEmptyStateDisplay
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { type Theme, withTheme } from '@emotion/react';
import { styled } from '@linaria/react';
import { type Ref } from 'react';
@@ -52,7 +53,7 @@ export const RecordTableCellDisplayContainer = ({
>
{placeholderForEmptyCell ? (
<StyledEmptyPlaceholderField>
{'Set ' + placeholderForEmptyCell}
{t`Set ${placeholderForEmptyCell}`}
</StyledEmptyPlaceholderField>
) : (
<StyledInnerContainer>{children}</StyledInnerContainer>
@@ -4,6 +4,7 @@ import { recordIndexHasFetchedAllRecordsByGroupComponentState } from '@/object-r
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableActionRow } from '@/object-record/record-table/record-table-row/components/RecordTableActionRow';
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
import { t } from '@lingui/core/macro';
import { IconArrowDown } from 'twenty-ui/display';
export const RecordTableRecordGroupSectionLoadMore = () => {
@@ -30,7 +31,7 @@ export const RecordTableRecordGroupSectionLoadMore = () => {
return (
<RecordTableActionRow
LeftIcon={IconArrowDown}
text="Load more"
text={t`Load more`}
onClick={handleLoadMore}
/>
);
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
@@ -60,7 +61,7 @@ export const RecordTitleCellSingleTextDisplayMode = ({
}}
>
{isEmpty ? (
<StyledEmptyText>Untitled</StyledEmptyText>
<StyledEmptyText>{t`Untitled`}</StyledEmptyText>
) : (
<OverflowingTextWithTooltip
text={
@@ -1,3 +1,4 @@
import { t } from '@lingui/core/macro';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useFullNameFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useFullNameFieldDisplay';
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
@@ -82,7 +83,7 @@ export const RecordTitleFullNameFieldDisplay = ({
}}
>
{!content ? (
<StyledEmptyText>Untitled</StyledEmptyText>
<StyledEmptyText>{t`Untitled`}</StyledEmptyText>
) : (
<OverflowingTextWithTooltip
text={isNonEmptyString(content) ? content : fieldDefinition.label}
@@ -10,6 +10,7 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { t } from '@lingui/core/macro';
import { Avatar } from 'twenty-ui/display';
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
@@ -117,7 +118,7 @@ export const MultipleSelectDropdown = ({
</SelectableListItem>
);
})}
{showNoResult && <MenuItem text="No results" />}
{showNoResult && <MenuItem text={t`No results`} />}
{loadingItems && <DropdownMenuSkeletonItem />}
</DropdownMenuItemsContainer>
</SelectableList>