Set up localization with feature flag control (#9649)

Refers #8128 

Changes Introduced:
- Added i18n configuration.
- Added a feature flag for localization.
- Enabled language switching based on the flag.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Anne Deepa Prasanna
2025-01-17 01:30:56 +05:30
committed by GitHub
parent b81ffcc77c
commit f44b31573a
38 changed files with 912 additions and 79 deletions

View File

@ -7,15 +7,15 @@ import {
import { graphqlMocks } from '~/testing/graphqlMocks';
import { userEvent, within } from '@storybook/test';
import { SettingsAppearance } from '../profile/appearance/components/SettingsAppearance';
import { SettingsExperience } from '../profile/appearance/components/SettingsExperience';
Date.now = () => new Date('2022-06-13T12:33:37.000Z').getTime();
const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/SettingsAppearance',
component: SettingsAppearance,
title: 'Pages/Settings/SettingsExperience',
component: SettingsExperience,
decorators: [PageDecorator],
args: { routePath: '/settings/appearance' },
args: { routePath: '/settings/experience' },
parameters: {
msw: graphqlMocks,
date: new Date(2021, 1, 1),
@ -24,13 +24,13 @@ const meta: Meta<PageDecoratorArgs> = {
export default meta;
export type Story = StoryObj<typeof SettingsAppearance>;
export type Story = StoryObj<typeof SettingsExperience>;
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('Appearance', undefined, {
await canvas.findAllByText('Experience', undefined, {
timeout: 3000,
});

View File

@ -0,0 +1,96 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { Select } from '@/ui/input/components/Select';
import { i18n } from '@lingui/core';
import { isDefined } from '~/utils/isDefined';
import { logError } from '~/utils/logError';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
`;
export const LocalePicker = () => {
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
currentWorkspaceMemberState,
);
const { updateOneRecord } = useUpdateOneRecord({
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
});
const updateWorkspaceMember = async (changedFields: any) => {
if (!currentWorkspaceMember?.id) {
throw new Error('User is not logged in');
}
try {
await updateOneRecord({
idToUpdate: currentWorkspaceMember.id,
updateOneRecordInput: changedFields,
});
} catch (error) {
logError(error);
}
};
if (!isDefined(currentWorkspaceMember)) return;
const handleLocaleChange = (value: string) => {
setCurrentWorkspaceMember({
...currentWorkspaceMember,
...{ locale: value },
});
updateWorkspaceMember({ locale: value });
i18n.activate(value);
};
return (
<StyledContainer>
<Select
dropdownId="preferred-locale"
dropdownWidthAuto
fullWidth
value={i18n.locale}
options={[
{
label: 'Portuguese',
value: 'pt',
},
{
label: 'French',
value: 'fr',
},
{
label: 'German',
value: 'de',
},
{
label: 'Italian',
value: 'it',
},
{
label: 'Spanish',
value: 'es',
},
{
label: 'English',
value: 'en',
},
{
label: 'Chinese',
value: 'zh',
},
]}
onChange={(value) => handleLocaleChange(value)}
/>
</StyledContainer>
);
};

View File

@ -5,11 +5,22 @@ import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { useColorScheme } from '@/ui/theme/hooks/useColorScheme';
import { DateTimeSettings } from '~/pages/settings/profile/appearance/components/DateTimeSettings';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
export const SettingsAppearance = () => {
import { useLingui } from '@lingui/react/macro';
import { FeatureFlagKey } from '~/generated/graphql';
import { DateTimeSettings } from '~/pages/settings/profile/appearance/components/DateTimeSettings';
import { LocalePicker } from '~/pages/settings/profile/appearance/components/LocalePicker';
export const SettingsExperience = () => {
const { colorScheme, setColorScheme } = useColorScheme();
const isLocalizationEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsLocalizationEnabled,
);
const { t } = useLingui();
return (
<SubMenuTopBarContainer
title="Experience"
@ -33,6 +44,16 @@ export const SettingsAppearance = () => {
/>
<DateTimeSettings />
</Section>
{isLocalizationEnabled && (
<Section>
<H2Title
title={t`Language`}
description={t`Select your preferred language`}
/>
<LocalePicker />
</Section>
)}
</SettingsPageContainer>
</SubMenuTopBarContainer>
);