Skip to content

Commit 0cdf73b

Browse files
refactor(i18n): improve i18n (#209)
1 parent 7772a4d commit 0cdf73b

File tree

10 files changed

+86
-79
lines changed

10 files changed

+86
-79
lines changed

cypress/support/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
import '@ui5/webcomponents-cypress-commands';
2-
import "../../i18n";
2+
import "../../src/utils/i18n/i18n";

i18n.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/components/Dialogs/CreateProjectDialog.cy.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
import React, { useState, useRef } from 'react';
1+
import React, { useState, useRef, useMemo } from 'react';
22
import { CreateProjectWorkspaceDialog, OnCreatePayload } from './CreateProjectWorkspaceDialog';
33
import { MemberRoles } from '../../lib/api/types/shared/members';
44
import { ErrorDialogHandle } from '../Shared/ErrorMessageBox';
55

66
import { useForm } from 'react-hook-form';
77
import { zodResolver } from '@hookform/resolvers/zod';
8-
import { validationSchemaProjectWorkspace } from '../../lib/api/validations/schemas.ts';
8+
import { createProjectWorkspaceSchema } from '../../lib/api/validations/schemas.ts';
99
import { CreateDialogProps } from './CreateWorkspaceDialogContainer.tsx';
10+
import { useTranslation } from 'react-i18next';
1011

1112
export const CreateProjectWorkspaceDialogWrapper: React.FC<{
1213
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1314
spyFormBody?: (data: any) => object;
1415
}> = ({ spyFormBody }) => {
1516
const [isOpen, setIsOpen] = useState(true);
17+
const { t } = useTranslation();
1618

1719
const errorDialogRef = useRef<ErrorDialogHandle>(null);
1820

21+
const validationSchemaProjectWorkspace = useMemo(() => createProjectWorkspaceSchema(t), [t]);
22+
1923
const {
2024
register,
2125
handleSubmit,

src/components/Dialogs/CreateProjectDialogContainer.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useRef } from 'react';
1+
import { useCallback, useEffect, useMemo, useRef } from 'react';
22
import { useApiResourceMutation } from '../../lib/api/useApiResource';
33
import { ErrorDialogHandle } from '../Shared/ErrorMessageBox.tsx';
44
import { APIError } from '../../lib/api/error';
@@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
1212
import { zodResolver } from '@hookform/resolvers/zod';
1313
import { useForm } from 'react-hook-form';
1414
import { CreateProject, CreateProjectResource, CreateProjectType } from '../../lib/api/types/crate/createProject.ts';
15-
import { validationSchemaProjectWorkspace } from '../../lib/api/validations/schemas.ts';
15+
import { createProjectWorkspaceSchema } from '../../lib/api/validations/schemas.ts';
1616
import { CreateDialogProps } from './CreateWorkspaceDialogContainer.tsx';
1717

1818
export function CreateProjectDialogContainer({
@@ -22,6 +22,8 @@ export function CreateProjectDialogContainer({
2222
isOpen: boolean;
2323
setIsOpen: (isOpen: boolean) => void;
2424
}) {
25+
const { t } = useTranslation();
26+
const validationSchemaProjectWorkspace = useMemo(() => createProjectWorkspaceSchema(t), [t]);
2527
const {
2628
watch,
2729
register,
@@ -39,7 +41,6 @@ export function CreateProjectDialogContainer({
3941
members: [],
4042
},
4143
});
42-
const { t } = useTranslation();
4344
const { user } = useAuthOnboarding();
4445

4546
const username = user?.email;

src/components/Dialogs/CreateWorkspaceDialogContainer.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useRef } from 'react';
1+
import { useCallback, useEffect, useMemo, useRef } from 'react';
22
import { useApiResourceMutation, useRevalidateApiResource } from '../../lib/api/useApiResource';
33
import { ErrorDialogHandle } from '../Shared/ErrorMessageBox.tsx';
44
import { APIError } from '../../lib/api/error';
@@ -16,7 +16,7 @@ import { Member, MemberRoles } from '../../lib/api/types/shared/members.ts';
1616
import { useTranslation } from 'react-i18next';
1717
import { zodResolver } from '@hookform/resolvers/zod';
1818
import { useForm } from 'react-hook-form';
19-
import { validationSchemaProjectWorkspace } from '../../lib/api/validations/schemas.ts';
19+
import { createProjectWorkspaceSchema } from '../../lib/api/validations/schemas.ts';
2020
import { ComponentsListItem } from '../../lib/api/types/crate/createManagedControlPlane.ts';
2121

2222
export type CreateDialogProps = {
@@ -37,6 +37,8 @@ export function CreateWorkspaceDialogContainer({
3737
setIsOpen: (isOpen: boolean) => void;
3838
project?: string;
3939
}) {
40+
const { t } = useTranslation();
41+
const validationSchemaProjectWorkspace = useMemo(() => createProjectWorkspaceSchema(t), [t]);
4042
const {
4143
register,
4244
handleSubmit,
@@ -54,7 +56,6 @@ export function CreateWorkspaceDialogContainer({
5456
chargingTargetType: '',
5557
},
5658
});
57-
const { t } = useTranslation();
5859
const { user } = useAuthOnboarding();
5960

6061
const username = user?.email;

src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { useTranslation } from 'react-i18next';
2323
import { useAuthOnboarding } from '../../../spaces/onboarding/auth/AuthContextOnboarding.tsx';
2424
import { ErrorDialog, ErrorDialogHandle } from '../../Shared/ErrorMessageBox.tsx';
2525
import { CreateDialogProps } from '../../Dialogs/CreateWorkspaceDialogContainer.tsx';
26-
import { validationSchemaCreateManagedControlPlane } from '../../../lib/api/validations/schemas.ts';
26+
import { createManagedControlPlaneSchema } from '../../../lib/api/validations/schemas.ts';
2727
import { Member, MemberRoles } from '../../../lib/api/types/shared/members.ts';
2828
import { useApiResourceMutation } from '../../../lib/api/useApiResource.ts';
2929
import {
@@ -62,6 +62,7 @@ export const CreateManagedControlPlaneWizardContainer: FC<CreateManagedControlPl
6262
const errorDialogRef = useRef<ErrorDialogHandle>(null);
6363

6464
const [selectedStep, setSelectedStep] = useState<WizardStepType>('metadata');
65+
const validationSchemaCreateManagedControlPlane = useMemo(() => createManagedControlPlaneSchema(t), [t]);
6566

6667
const {
6768
register,

src/lib/api/validations/schemas.ts

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,59 @@
11
import { z } from 'zod';
22
import { Member } from '../types/shared/members.ts';
3-
import i18n from '../../../../i18n.ts';
3+
import { TFunction } from 'i18next';
44
import { btpChargingTargetRegex, managedControlPlaneNameRegex, projectWorkspaceNameRegex } from './regex.ts';
55

6-
const { t } = i18n;
7-
86
const member = z.custom<Member>();
97

108
// Shared superRefine helper for charging target validation
11-
function validateChargingTarget<T extends { chargingTargetType?: string; chargingTarget?: string }>(
12-
data: T,
13-
ctx: z.RefinementCtx,
9+
function createValidateChargingTarget<T extends { chargingTargetType?: string; chargingTarget?: string }>(
10+
t: TFunction,
1411
) {
15-
if (data.chargingTargetType && data.chargingTarget && !btpChargingTargetRegex.test(data.chargingTarget ?? '')) {
16-
ctx.addIssue({
17-
path: ['chargingTarget'],
18-
code: z.ZodIssueCode.custom,
19-
message: t('validationErrors.notValidChargingTargetFormat'),
20-
});
21-
} else if (data.chargingTargetType && !data.chargingTarget) {
22-
ctx.addIssue({
23-
path: ['chargingTarget'],
24-
code: z.ZodIssueCode.custom,
25-
message: t('validationErrors.required'),
26-
});
27-
}
12+
return (data: T, ctx: z.RefinementCtx) => {
13+
if (data.chargingTargetType && data.chargingTarget && !btpChargingTargetRegex.test(data.chargingTarget ?? '')) {
14+
ctx.addIssue({
15+
path: ['chargingTarget'],
16+
code: z.ZodIssueCode.custom,
17+
message: t('validationErrors.notValidChargingTargetFormat'),
18+
});
19+
} else if (data.chargingTargetType && !data.chargingTarget) {
20+
ctx.addIssue({
21+
path: ['chargingTarget'],
22+
code: z.ZodIssueCode.custom,
23+
message: t('validationErrors.required'),
24+
});
25+
}
26+
};
2827
}
2928

30-
export const validationSchemaProjectWorkspace = z
31-
.object({
32-
name: z
33-
.string()
34-
.min(1, t('validationErrors.required'))
35-
.regex(projectWorkspaceNameRegex, t('validationErrors.properFormatting'))
36-
.max(25, t('validationErrors.maxChars', { maxLength: 25 })),
37-
displayName: z.string().optional(),
38-
chargingTarget: z.string().optional(),
39-
chargingTargetType: z.string().optional(),
40-
members: z.array(member).refine((members) => members?.length > 0),
41-
})
42-
.superRefine(validateChargingTarget);
29+
export function createProjectWorkspaceSchema(t: TFunction) {
30+
return z
31+
.object({
32+
name: z
33+
.string()
34+
.min(1, t('validationErrors.required'))
35+
.regex(projectWorkspaceNameRegex, t('validationErrors.properFormatting'))
36+
.max(25, t('validationErrors.maxChars', { maxLength: 25 })),
37+
displayName: z.string().optional(),
38+
chargingTarget: z.string().optional(),
39+
chargingTargetType: z.string().optional(),
40+
members: z.array(member).refine((members) => members?.length > 0),
41+
})
42+
.superRefine(createValidateChargingTarget(t));
43+
}
4344

44-
export const validationSchemaCreateManagedControlPlane = z
45-
.object({
46-
name: z
47-
.string()
48-
.min(1, t('validationErrors.required'))
49-
.regex(managedControlPlaneNameRegex, t('validationErrors.properFormattingLowercase'))
50-
.max(36, t('validationErrors.maxChars', { maxLength: 36 })),
51-
displayName: z.string().optional(),
52-
chargingTarget: z.string().optional(),
53-
chargingTargetType: z.string().optional(),
54-
members: z.array(member),
55-
})
56-
.superRefine(validateChargingTarget);
45+
export function createManagedControlPlaneSchema(t: TFunction) {
46+
return z
47+
.object({
48+
name: z
49+
.string()
50+
.min(1, t('validationErrors.required'))
51+
.regex(managedControlPlaneNameRegex, t('validationErrors.properFormattingLowercase'))
52+
.max(36, t('validationErrors.maxChars', { maxLength: 36 })),
53+
displayName: z.string().optional(),
54+
chargingTarget: z.string().optional(),
55+
chargingTargetType: z.string().optional(),
56+
members: z.array(member),
57+
})
58+
.superRefine(createValidateChargingTarget(t));
59+
}

src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { CopyButtonProvider } from './context/CopyButtonContext.tsx';
88
import { FrontendConfigProvider } from './context/FrontendConfigContext.tsx';
99
import '@ui5/webcomponents-react/dist/Assets'; //used for loading themes
1010
import { ThemeManager } from './components/ThemeManager.tsx';
11-
import '.././i18n.ts';
11+
import './utils/i18n/i18n.ts';
1212
import './utils/i18n/timeAgo';
1313
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
1414
import { ApolloClientProvider } from './spaces/onboarding/services/ApolloClientProvider/ApolloClientProvider.tsx';

src/utils/i18n/i18n.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import i18n from 'i18next';
2+
import { initReactI18next } from 'react-i18next';
3+
4+
import translationEN from '../../../public/locales/en.json';
5+
6+
const resources = {
7+
en: {
8+
translation: translationEN,
9+
},
10+
};
11+
12+
i18n.use(initReactI18next).init({
13+
resources,
14+
lng: 'en',
15+
fallbackLng: 'en',
16+
debug: process.env.NODE_ENV === 'development',
17+
});
18+
19+
export default i18n;

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"noFallthroughCasesInSwitch": true,
2020
"types": ["node", "cypress"]
2121
},
22-
"include": ["src", "cypress.d.ts", "i18n.ts"],
22+
"include": ["src", "cypress.d.ts"],
2323
"references": [
2424
{
2525
"path": "./tsconfig.node.json"

0 commit comments

Comments
 (0)