mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
localization fixes
This commit is contained in:
@@ -1,21 +1,23 @@
|
|||||||
import { Loader, Select, SimpleGrid, TextInput } from '@mantine/core';
|
import { Loader, Select, SimpleGrid, TextInput } from '@mantine/core';
|
||||||
import type { FieldErrors, UseFormRegister, UseFormSetValue, UseFormWatch, UseFormTrigger } from 'react-hook-form';
|
import type { FieldErrors, UseFormRegister, UseFormSetValue, UseFormWatch, UseFormTrigger } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import type { TFunction } from 'i18next';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AmharicDatePicker } from '@ema-platform/ui';
|
import { AmharicDatePicker } from '@ema-platform/ui';
|
||||||
|
|
||||||
export const profileSchema = z.object({
|
export const profileSchema = (t: TFunction) =>
|
||||||
professionId: z.string().min(1, 'Select your profession'),
|
z.object({
|
||||||
firstName: z.string().min(3, 'First name must be at least 3 characters'),
|
professionId: z.string().min(1, t('profileForm.validation.professionRequired')),
|
||||||
middleName: z.string().min(3, 'Middle name must be at least 3 characters'),
|
firstName: z.string().min(3, t('profileForm.validation.firstNameMin')),
|
||||||
lastName: z.string().min(3, 'Last name must be at least 3 characters'),
|
middleName: z.string().min(3, t('profileForm.validation.middleNameMin')),
|
||||||
gender: z.string().min(1, 'Select your gender'),
|
lastName: z.string().min(3, t('profileForm.validation.lastNameMin')),
|
||||||
dob: z.string().min(1, 'Select your date of birth'),
|
gender: z.string().min(1, t('profileForm.validation.genderRequired')),
|
||||||
pob: z.string().optional(),
|
dob: z.string().min(1, t('profileForm.validation.dobRequired')),
|
||||||
maritalStatus: z.string().min(1, 'Select your marital status'),
|
pob: z.string().optional(),
|
||||||
});
|
maritalStatus: z.string().min(1, t('profileForm.validation.maritalStatusRequired')),
|
||||||
|
});
|
||||||
|
|
||||||
export type ProfileValues = z.infer<typeof profileSchema>;
|
export type ProfileValues = z.infer<ReturnType<typeof profileSchema>>;
|
||||||
|
|
||||||
export const GENDERS = ['MALE', 'FEMALE'] as const;
|
export const GENDERS = ['MALE', 'FEMALE'] as const;
|
||||||
export const MARITAL_STATUSES = ['SINGLE', 'MARRIED', 'DIVORCED', 'WIDOWED'] as const;
|
export const MARITAL_STATUSES = ['SINGLE', 'MARRIED', 'DIVORCED', 'WIDOWED'] as const;
|
||||||
@@ -45,8 +47,8 @@ export function ProfileFormContent({
|
|||||||
return (
|
return (
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
||||||
<Select
|
<Select
|
||||||
label="Profession"
|
label={t('profileFields.professionId')}
|
||||||
placeholder={professionsLoading ? t('common.loading', 'Loading…') : 'Select'}
|
placeholder={professionsLoading ? t('common.loading') : t('common.select')}
|
||||||
required
|
required
|
||||||
data={professionOptions}
|
data={professionOptions}
|
||||||
error={errors.professionId?.message}
|
error={errors.professionId?.message}
|
||||||
@@ -59,31 +61,31 @@ export function ProfileFormContent({
|
|||||||
rightSection={professionsLoading ? <Loader size="xs" type="oval" /> : undefined}
|
rightSection={professionsLoading ? <Loader size="xs" type="oval" /> : undefined}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="First Name"
|
label={t('profileFields.firstName')}
|
||||||
placeholder="Enter first name"
|
placeholder={t('profileForm.placeholders.firstName')}
|
||||||
required
|
required
|
||||||
{...register('firstName')}
|
{...register('firstName')}
|
||||||
error={errors.firstName?.message}
|
error={errors.firstName?.message}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Middle Name"
|
label={t('profileFields.middleName')}
|
||||||
placeholder="Enter middle name"
|
placeholder={t('profileForm.placeholders.middleName')}
|
||||||
required
|
required
|
||||||
{...register('middleName')}
|
{...register('middleName')}
|
||||||
error={errors.middleName?.message}
|
error={errors.middleName?.message}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Last Name"
|
label={t('profileFields.lastName')}
|
||||||
placeholder="Enter last name"
|
placeholder={t('profileForm.placeholders.lastName')}
|
||||||
required
|
required
|
||||||
{...register('lastName')}
|
{...register('lastName')}
|
||||||
error={errors.lastName?.message}
|
error={errors.lastName?.message}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Gender"
|
label={t('profileFields.gender')}
|
||||||
placeholder="Select"
|
placeholder={t('common.select')}
|
||||||
required
|
required
|
||||||
data={[...GENDERS]}
|
data={GENDERS.map((g) => ({ value: g, label: t(`profileForm.genders.${g}`) }))}
|
||||||
error={errors.gender?.message}
|
error={errors.gender?.message}
|
||||||
value={watch('gender')}
|
value={watch('gender')}
|
||||||
onChange={(val) => setValue('gender', val || '', { shouldValidate: true })}
|
onChange={(val) => setValue('gender', val || '', { shouldValidate: true })}
|
||||||
@@ -91,7 +93,7 @@ export function ProfileFormContent({
|
|||||||
name="gender"
|
name="gender"
|
||||||
/>
|
/>
|
||||||
<AmharicDatePicker
|
<AmharicDatePicker
|
||||||
label="Date of Birth"
|
label={t('profileFields.dob')}
|
||||||
required
|
required
|
||||||
value={watch('dob')}
|
value={watch('dob')}
|
||||||
onChange={(val) => setValue('dob', val, { shouldValidate: true })}
|
onChange={(val) => setValue('dob', val, { shouldValidate: true })}
|
||||||
@@ -100,16 +102,16 @@ export function ProfileFormContent({
|
|||||||
error={errors.dob?.message}
|
error={errors.dob?.message}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Place of Birth"
|
label={t('profileFields.pob')}
|
||||||
placeholder="City, Region"
|
placeholder={t('profileForm.placeholders.pob')}
|
||||||
{...register('pob')}
|
{...register('pob')}
|
||||||
error={errors.pob?.message}
|
error={errors.pob?.message}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Marital Status"
|
label={t('profileFields.maritalStatus')}
|
||||||
placeholder="Select"
|
placeholder={t('common.select')}
|
||||||
required
|
required
|
||||||
data={[...MARITAL_STATUSES]}
|
data={MARITAL_STATUSES.map((m) => ({ value: m, label: t(`profileForm.maritalStatuses.${m}`) }))}
|
||||||
error={errors.maritalStatus?.message}
|
error={errors.maritalStatus?.message}
|
||||||
value={watch('maritalStatus')}
|
value={watch('maritalStatus')}
|
||||||
onChange={(val) => setValue('maritalStatus', val || '', { shouldValidate: true })}
|
onChange={(val) => setValue('maritalStatus', val || '', { shouldValidate: true })}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function ProfileRequirementGate({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Text size="sm">{requirement.reason}</Text>
|
<Text size="sm">{t(requirement.reason)}</Text>
|
||||||
<List size="sm" spacing={2}>
|
<List size="sm" spacing={2}>
|
||||||
{gaps.map((field) => (
|
{gaps.map((field) => (
|
||||||
<List.Item key={field}>
|
<List.Item key={field}>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Center, Loader } from '@mantine/core';
|
|
||||||
import { Navigate, useLocation, useParams } from 'react-router-dom';
|
import { Navigate, useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PageLoader, notify } from '@ema-platform/ui';
|
import { PageLoader, notify } from '@ema-platform/ui';
|
||||||
@@ -35,8 +34,12 @@ export const SEAFARER_PROFILE_REQUIREMENT: ProfileRequirement = {
|
|||||||
'primaryPhoneNumber',
|
'primaryPhoneNumber',
|
||||||
'email',
|
'email',
|
||||||
],
|
],
|
||||||
reason:
|
// Translation key, not literal text — `ProfileRequirementGate` runs it
|
||||||
'Seafarer registration is built from your profile — these details fill it in for you.',
|
// through `t()` at render time (it can't be translated here: this object
|
||||||
|
// is built at module scope, before any component or `useTranslation` call
|
||||||
|
// exists). Reuses `profileGate.seafarerReason`, the same sentence already
|
||||||
|
// shown by the seafarer-redirect toast below.
|
||||||
|
reason: 'profileGate.seafarerReason',
|
||||||
};
|
};
|
||||||
|
|
||||||
const REGISTRATION_TYPE_KEY = 'SEAFARER_REGISTRATION';
|
const REGISTRATION_TYPE_KEY = 'SEAFARER_REGISTRATION';
|
||||||
@@ -75,7 +78,7 @@ export function RequireSeafarerProfile({ children }: { children: React.ReactNode
|
|||||||
if (!gated) return <>{children}</>;
|
if (!gated) return <>{children}</>;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <PageLoader label="Checking seafarer profile…" height={350} />;
|
return <PageLoader label={t('profileGate.checkingProfile')} height={350} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A failed lookup must not lock anyone out — the server still refuses the
|
// A failed lookup must not lock anyone out — the server still refuses the
|
||||||
@@ -89,7 +92,7 @@ export function RequireSeafarerProfile({ children }: { children: React.ReactNode
|
|||||||
// headed back here in the same tick — deciding on the pre-save cache would
|
// headed back here in the same tick — deciding on the pre-save cache would
|
||||||
// bounce them off the screen they just finished.
|
// bounce them off the screen they just finished.
|
||||||
if (isFetching) {
|
if (isFetching) {
|
||||||
return <PageLoader label="Checking seafarer profile…" height={350} />;
|
return <PageLoader label={t('profileGate.checkingProfile')} height={350} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = PROFILE_FIELD_SECTION[gaps[0]];
|
const target = PROFILE_FIELD_SECTION[gaps[0]];
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ export function ProfilePage() {
|
|||||||
.string()
|
.string()
|
||||||
.refine(
|
.refine(
|
||||||
(name) => Object.values(splitProfileName(name)).every(Boolean),
|
(name) => Object.values(splitProfileName(name)).every(Boolean),
|
||||||
{ message: 'Enter your first, middle, and last name' },
|
{ message: t('profileForm.validation.nameParts') },
|
||||||
),
|
),
|
||||||
nameAm: z.string().min(1, { message: t('profile.validation.nameRequired') }),
|
nameAm: z.string().min(1, { message: t('profile.validation.nameRequired') }),
|
||||||
username: z.string().min(1, { message: t('profile.validation.usernameRequired') }),
|
username: z.string().min(1, { message: t('profile.validation.usernameRequired') }),
|
||||||
@@ -341,7 +341,7 @@ export function ProfilePage() {
|
|||||||
trigger: profileTriggerValidation,
|
trigger: profileTriggerValidation,
|
||||||
formState: { errors: profileErrors },
|
formState: { errors: profileErrors },
|
||||||
} = useForm<ProfileValues>({
|
} = useForm<ProfileValues>({
|
||||||
resolver: zodResolver(profileSchema),
|
resolver: zodResolver(profileSchema(t)),
|
||||||
values: loadedProfile ?? undefined,
|
values: loadedProfile ?? undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ export function ProfilePage() {
|
|||||||
|
|
||||||
const fullName = formatProfileName(values);
|
const fullName = formatProfileName(values);
|
||||||
if (user && normalizeName(fullName) !== normalizeName(user.name.en)) {
|
if (user && normalizeName(fullName) !== normalizeName(user.name.en)) {
|
||||||
notify.error('Profile name must match the name in the Personal tab.');
|
notify.error(t('profile.nameMismatch'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +367,7 @@ export function ProfilePage() {
|
|||||||
// the address save below) — without this, `missing` stays stale until
|
// the address save below) — without this, `missing` stays stale until
|
||||||
// a reload.
|
// a reload.
|
||||||
refetchProfile();
|
refetchProfile();
|
||||||
notify.success('Profile updated');
|
notify.success(t('profile.profileUpdated'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -395,7 +395,7 @@ export function ProfilePage() {
|
|||||||
profileId,
|
profileId,
|
||||||
body: toAddressPayload(values),
|
body: toAddressPayload(values),
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
notify.success('Address saved');
|
notify.success(t('profile.addressSaved'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
@@ -569,19 +569,19 @@ export function ProfilePage() {
|
|||||||
>
|
>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Tab value="personal" leftSection={<IconUserCircle size={18} />}>
|
<Tabs.Tab value="personal" leftSection={<IconUserCircle size={18} />}>
|
||||||
Personal
|
{t('profile.tabs.personal')}
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab value="profile" leftSection={<IconUser size={18} />}>
|
<Tabs.Tab value="profile" leftSection={<IconUser size={18} />}>
|
||||||
Profile
|
{t('profile.tabs.profile')}
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab value="address" leftSection={<IconMapPin size={18} />}>
|
<Tabs.Tab value="address" leftSection={<IconMapPin size={18} />}>
|
||||||
Address
|
{t('profile.tabs.address')}
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
value="operations"
|
value="operations"
|
||||||
leftSection={<IconBuildingWarehouse size={18} />}
|
leftSection={<IconBuildingWarehouse size={18} />}
|
||||||
>
|
>
|
||||||
Operations
|
{t('profile.tabs.operations')}
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab value="security" leftSection={<IconShieldLock size={18} />}>
|
<Tabs.Tab value="security" leftSection={<IconShieldLock size={18} />}>
|
||||||
{t('profile.tabs.security')}
|
{t('profile.tabs.security')}
|
||||||
@@ -676,15 +676,15 @@ export function ProfilePage() {
|
|||||||
<Center py="xl"><Loader /></Center>
|
<Center py="xl"><Loader /></Center>
|
||||||
) : !loadedProfile ? (
|
) : !loadedProfile ? (
|
||||||
<Text c="dimmed" ta="center" py="xl">
|
<Text c="dimmed" ta="center" py="xl">
|
||||||
No profile found. Complete your profile setup first.
|
{t('profile.maritimeSection.noProfile')}
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleProfileSubmit(onSaveProfile)}>
|
<form onSubmit={handleProfileSubmit(onSaveProfile)}>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<div>
|
<div>
|
||||||
<Title order={5}>Maritime Profile</Title>
|
<Title order={5}>{t('profile.maritimeSection.title')}</Title>
|
||||||
<Text size="sm" c="dimmed" mb="md">
|
<Text size="sm" c="dimmed" mb="md">
|
||||||
Your professional maritime details
|
{t('profile.maritimeSection.subtitle')}
|
||||||
</Text>
|
</Text>
|
||||||
<ProfileFormContent
|
<ProfileFormContent
|
||||||
register={registerProfile}
|
register={registerProfile}
|
||||||
@@ -703,7 +703,7 @@ export function ProfilePage() {
|
|||||||
loading={isSavingMaritime}
|
loading={isSavingMaritime}
|
||||||
leftSection={<IconDeviceFloppy size={18} />}
|
leftSection={<IconDeviceFloppy size={18} />}
|
||||||
>
|
>
|
||||||
Save Profile
|
{t('profile.maritimeSection.save')}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -721,9 +721,9 @@ export function ProfilePage() {
|
|||||||
<form onSubmit={handleAddressSubmit(onSaveAddress)}>
|
<form onSubmit={handleAddressSubmit(onSaveAddress)}>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<div>
|
<div>
|
||||||
<Title order={5}>Address & Contact</Title>
|
<Title order={5}>{t('profile.addressSection.title')}</Title>
|
||||||
<Text size="sm" c="dimmed" mb="md">
|
<Text size="sm" c="dimmed" mb="md">
|
||||||
Your identity documents, contact details and emergency contact
|
{t('profile.addressSection.subtitle')}
|
||||||
</Text>
|
</Text>
|
||||||
<AddressFormContent
|
<AddressFormContent
|
||||||
register={registerAddress}
|
register={registerAddress}
|
||||||
@@ -877,7 +877,7 @@ export function ProfilePage() {
|
|||||||
{t(`language.${lng}`)}
|
{t(`language.${lng}`)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{lng === 'en' ? 'English (United States)' : 'Amharic'}
|
{t(`profile.languageFull.${lng}`)}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
{active ? (
|
{active ? (
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export const am: Translations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
common: {
|
common: {
|
||||||
|
select: 'ይምረጡ',
|
||||||
back: 'ተመለስ',
|
back: 'ተመለስ',
|
||||||
continue: 'ቀጥል',
|
continue: 'ቀጥል',
|
||||||
submit: 'አስገባ',
|
submit: 'አስገባ',
|
||||||
@@ -283,6 +284,7 @@ export const am: Translations = {
|
|||||||
viewProfile: 'ሙሉ መገለጫ ይመልከቱ',
|
viewProfile: 'ሙሉ መገለጫ ይመልከቱ',
|
||||||
seafarerReason: 'የባህረኞች ምዝገባ የሚዘጋጀው ከመገለጫዎ ነው — እነዚህ መረጃዎች ራሱ ይሞላሉ።',
|
seafarerReason: 'የባህረኞች ምዝገባ የሚዘጋጀው ከመገለጫዎ ነው — እነዚህ መረጃዎች ራሱ ይሞላሉ።',
|
||||||
seafarerRedirect: 'የባህረኛ ምዝገባ ለማድረግ መገለጫዎን ያጠናቅቁ። የሚያስፈልጉ፡ {{fields}}',
|
seafarerRedirect: 'የባህረኛ ምዝገባ ለማድረግ መገለጫዎን ያጠናቅቁ። የሚያስፈልጉ፡ {{fields}}',
|
||||||
|
checkingProfile: 'የባህረኛ መገለጫ በመፈተሽ ላይ…',
|
||||||
},
|
},
|
||||||
|
|
||||||
profileSections: {
|
profileSections: {
|
||||||
@@ -311,10 +313,30 @@ export const am: Translations = {
|
|||||||
verified: 'ተረጋግጧል',
|
verified: 'ተረጋግጧል',
|
||||||
unverified: 'አልተረጋገጠም',
|
unverified: 'አልተረጋገጠም',
|
||||||
tabs: {
|
tabs: {
|
||||||
|
personal: 'የግል መረጃ',
|
||||||
profile: 'መገለጫ',
|
profile: 'መገለጫ',
|
||||||
|
address: 'አድራሻ',
|
||||||
|
operations: 'የስራ ዘርፍ',
|
||||||
security: 'ደህንነት',
|
security: 'ደህንነት',
|
||||||
preferences: 'ምርጫዎች',
|
preferences: 'ምርጫዎች',
|
||||||
},
|
},
|
||||||
|
maritimeSection: {
|
||||||
|
title: 'የባህር ሙያ መገለጫ',
|
||||||
|
subtitle: 'የባህር ሙያ ዝርዝሮችዎ',
|
||||||
|
noProfile: 'ምንም መገለጫ አልተገኘም። መጀመሪያ መገለጫዎን ያጠናቅቁ።',
|
||||||
|
save: 'መገለጫ አስቀምጥ',
|
||||||
|
},
|
||||||
|
addressSection: {
|
||||||
|
title: 'አድራሻ እና መገናኛ',
|
||||||
|
subtitle: 'የመታወቂያ ሰነዶችዎ፣ የመገናኛ ዝርዝሮችዎ እና የአደጋ ጊዜ ተጠሪ',
|
||||||
|
save: 'አድራሻ አስቀምጥ',
|
||||||
|
},
|
||||||
|
addressSaved: 'አድራሻ ተቀምጧል',
|
||||||
|
nameMismatch: 'የመገለጫ ስም ከግል መረጃ ትር ውስጥ ካለው ስም ጋር መዛመድ አለበት።',
|
||||||
|
languageFull: {
|
||||||
|
en: 'እንግሊዝኛ (አሜሪካ)',
|
||||||
|
am: 'አማርኛ',
|
||||||
|
},
|
||||||
personalHint: 'ስምዎ በይፋዊ የ EMA ሰነዶች ላይ እንደሚታየው።',
|
personalHint: 'ስምዎ በይፋዊ የ EMA ሰነዶች ላይ እንደሚታየው።',
|
||||||
languageTitle: 'ቋንቋ',
|
languageTitle: 'ቋንቋ',
|
||||||
languageHint: 'በ EMA ፖርታል ላይ የሚጠቀሙበትን ቋንቋ ይምረጡ።',
|
languageHint: 'በ EMA ፖርታል ላይ የሚጠቀሙበትን ቋንቋ ይምረጡ።',
|
||||||
@@ -369,6 +391,35 @@ export const am: Translations = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
profileForm: {
|
||||||
|
placeholders: {
|
||||||
|
firstName: 'የመጀመሪያ ስም ያስገቡ',
|
||||||
|
middleName: 'የአባት ስም ያስገቡ',
|
||||||
|
lastName: 'የአያት ስም ያስገቡ',
|
||||||
|
pob: 'ከተማ፣ ክልል',
|
||||||
|
},
|
||||||
|
genders: {
|
||||||
|
MALE: 'ወንድ',
|
||||||
|
FEMALE: 'ሴት',
|
||||||
|
},
|
||||||
|
maritalStatuses: {
|
||||||
|
SINGLE: 'ያላገባ/ች',
|
||||||
|
MARRIED: 'ያገባ/ች',
|
||||||
|
DIVORCED: 'የፈታ/ች',
|
||||||
|
WIDOWED: 'የሞተበት/ባት',
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
professionRequired: 'ሙያዎን ይምረጡ',
|
||||||
|
firstNameMin: 'የመጀመሪያ ስም ቢያንስ 3 ቁምፊዎች መሆን አለበት',
|
||||||
|
middleNameMin: 'የአባት ስም ቢያንስ 3 ቁምፊዎች መሆን አለበት',
|
||||||
|
lastNameMin: 'የአያት ስም ቢያንስ 3 ቁምፊዎች መሆን አለበት',
|
||||||
|
genderRequired: 'ጾታዎን ይምረጡ',
|
||||||
|
dobRequired: 'የትውልድ ቀንዎን ይምረጡ',
|
||||||
|
maritalStatusRequired: 'የጋብቻ ሁኔታዎን ይምረጡ',
|
||||||
|
nameParts: 'የመጀመሪያ፣ የአባት እና የአያት ስምዎን ያስገቡ',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
country: {
|
country: {
|
||||||
select: 'አገር ይምረጡ',
|
select: 'አገር ይምረጡ',
|
||||||
notFound: 'ምንም አገር አልተገኘም',
|
notFound: 'ምንም አገር አልተገኘም',
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export const en = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
common: {
|
common: {
|
||||||
|
select: 'Select',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
continue: 'Continue',
|
continue: 'Continue',
|
||||||
submit: 'Submit',
|
submit: 'Submit',
|
||||||
@@ -283,6 +284,7 @@ export const en = {
|
|||||||
seafarerReason:
|
seafarerReason:
|
||||||
'Seafarer registration is built from your profile — these details fill it in for you.',
|
'Seafarer registration is built from your profile — these details fill it in for you.',
|
||||||
seafarerRedirect: 'Finish your profile to register as a seafarer. Still needed: {{fields}}',
|
seafarerRedirect: 'Finish your profile to register as a seafarer. Still needed: {{fields}}',
|
||||||
|
checkingProfile: 'Checking seafarer profile…',
|
||||||
},
|
},
|
||||||
|
|
||||||
profileSections: {
|
profileSections: {
|
||||||
@@ -311,10 +313,30 @@ export const en = {
|
|||||||
verified: 'Verified',
|
verified: 'Verified',
|
||||||
unverified: 'Unverified',
|
unverified: 'Unverified',
|
||||||
tabs: {
|
tabs: {
|
||||||
|
personal: 'Personal',
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
address: 'Address',
|
||||||
|
operations: 'Operations',
|
||||||
security: 'Security',
|
security: 'Security',
|
||||||
preferences: 'Preferences',
|
preferences: 'Preferences',
|
||||||
},
|
},
|
||||||
|
maritimeSection: {
|
||||||
|
title: 'Maritime Profile',
|
||||||
|
subtitle: 'Your professional maritime details',
|
||||||
|
noProfile: 'No profile found. Complete your profile setup first.',
|
||||||
|
save: 'Save Profile',
|
||||||
|
},
|
||||||
|
addressSection: {
|
||||||
|
title: 'Address & Contact',
|
||||||
|
subtitle: 'Your identity documents, contact details and emergency contact',
|
||||||
|
save: 'Save Address',
|
||||||
|
},
|
||||||
|
addressSaved: 'Address saved',
|
||||||
|
nameMismatch: 'Profile name must match the name in the Personal tab.',
|
||||||
|
languageFull: {
|
||||||
|
en: 'English (United States)',
|
||||||
|
am: 'Amharic',
|
||||||
|
},
|
||||||
personalHint: 'Your name as it appears on official EMA documents.',
|
personalHint: 'Your name as it appears on official EMA documents.',
|
||||||
languageTitle: 'Language',
|
languageTitle: 'Language',
|
||||||
languageHint: 'Choose the language used across the EMA portal.',
|
languageHint: 'Choose the language used across the EMA portal.',
|
||||||
@@ -369,6 +391,35 @@ export const en = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
profileForm: {
|
||||||
|
placeholders: {
|
||||||
|
firstName: 'Enter first name',
|
||||||
|
middleName: 'Enter middle name',
|
||||||
|
lastName: 'Enter last name',
|
||||||
|
pob: 'City, Region',
|
||||||
|
},
|
||||||
|
genders: {
|
||||||
|
MALE: 'Male',
|
||||||
|
FEMALE: 'Female',
|
||||||
|
},
|
||||||
|
maritalStatuses: {
|
||||||
|
SINGLE: 'Single',
|
||||||
|
MARRIED: 'Married',
|
||||||
|
DIVORCED: 'Divorced',
|
||||||
|
WIDOWED: 'Widowed',
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
professionRequired: 'Select your profession',
|
||||||
|
firstNameMin: 'First name must be at least 3 characters',
|
||||||
|
middleNameMin: 'Middle name must be at least 3 characters',
|
||||||
|
lastNameMin: 'Last name must be at least 3 characters',
|
||||||
|
genderRequired: 'Select your gender',
|
||||||
|
dobRequired: 'Select your date of birth',
|
||||||
|
maritalStatusRequired: 'Select your marital status',
|
||||||
|
nameParts: 'Enter your first, middle, and last name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
country: {
|
country: {
|
||||||
select: 'Select a country',
|
select: 'Select a country',
|
||||||
notFound: 'No countries found',
|
notFound: 'No countries found',
|
||||||
|
|||||||
Reference in New Issue
Block a user