Skip to content

Commit 7840c64

Browse files
johnmeshulamitamaroryanCoolGame8
authored
[Localization] Added localization to the Portal (#1147)
Co-authored-by: WiFi <itamaroryan@users.noreply.github.com> Co-authored-by: CoolGame8 <78743717+CoolGame8@users.noreply.github.com>
1 parent 17ca4fb commit 7840c64

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1118
-221
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Add files here to ignore them from prettier formatting
22
**/*.json
3+
!**/locale/**/*.json
34
**/*.md
45
**/*.yml
56
.next

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
"editor.defaultFormatter": "ms-python.black-formatter",
88
},
99
"nxConsole.generateAiAgentRules": true,
10+
"i18n-ally.localesPaths": [
11+
"apps/portal/locale",
12+
"apps/frontend/locale",
13+
],
14+
"i18n-ally.keystyle": "nested"
1015
}

apps/portal/.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
{
1111
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
1212
"rules": {
13-
"@next/next/no-html-link-for-pages": ["error", "apps/portal/pages"]
13+
"@next/next/no-html-link-for-pages": ["error", "apps/portal/pages"],
14+
"react/jsx-no-literals": "warn"
1415
}
1516
},
1617
{

apps/portal/components/app-bar.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,23 @@ import {
1313
Button
1414
} from '@mui/material';
1515
import MenuIcon from '@mui/icons-material/MenuRounded';
16+
import { useTranslations } from 'next-intl';
17+
import LanguageSwitcher from './language-switcher';
1618

1719
const pages = [
1820
{
19-
name: 'אירועים',
21+
name: 'events',
2022
href: '/events'
2123
},
2224
{
23-
name: 'מחשבון ניקוד',
25+
name: 'scorer',
2426
href: '/scorer'
2527
}
2628
];
2729

2830
const ResponsiveAppBar = () => {
2931
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
32+
const t = useTranslations('components:app-bar');
3033

3134
return (
3235
<AppBar position="static">
@@ -62,7 +65,7 @@ const ResponsiveAppBar = () => {
6265
>
6366
{pages.map(page => (
6467
<MenuItem key={page.name} component={Link} href={page.href}>
65-
<Typography sx={{ textAlign: 'center' }}>{page.name}</Typography>
68+
<Typography sx={{ textAlign: 'center' }}>{t(page.name)}</Typography>
6669
</MenuItem>
6770
))}
6871
</Menu>
@@ -91,6 +94,11 @@ const ResponsiveAppBar = () => {
9194
</Box>
9295
</Box>
9396

97+
{/* Mobile Language Switcher */}
98+
<Box sx={{ display: { xs: 'flex', md: 'none' } }}>
99+
<LanguageSwitcher />
100+
</Box>
101+
94102
{/* Desktop */}
95103
<Box
96104
display={{ xs: 'none', md: 'flex' }}
@@ -117,10 +125,15 @@ const ResponsiveAppBar = () => {
117125
href={page.href}
118126
sx={{ my: 2, mx: 1, color: 'white', display: 'block' }}
119127
>
120-
{page.name}
128+
{t(page.name)}
121129
</Button>
122130
))}
123131
</Box>
132+
133+
{/* Desktop Language Switcher */}
134+
<Box sx={{ display: { xs: 'none', md: 'flex' } }}>
135+
<LanguageSwitcher />
136+
</Box>
124137
</Toolbar>
125138
</Container>
126139
</AppBar>

apps/portal/components/events/award-winner.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,50 @@
11
import { Typography, Stack } from '@mui/material';
22
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
3+
import { useTranslations } from 'next-intl';
34
import {
45
CoreValuesAwards,
56
CoreValuesAwardsTypes,
67
PersonalAwards,
78
PersonalAwardTypes,
8-
PortalAward
9+
PortalAward,
10+
PortalTeam
911
} from '@lems/types';
1012
import { getColorByPlace } from '../../lib/styling';
11-
13+
import { useLocaleAwardName } from '../../locale/hooks/use-locale-award';
1214
interface AwardWinnerProps {
1315
award: PortalAward;
14-
winnerText: string;
16+
/**
17+
* Whether to show the winner's name or just the award name and place.
18+
* Defaults to true.
19+
*/
20+
showWinner?: boolean;
1521
}
1622

17-
const AwardWinner: React.FC<AwardWinnerProps> = ({ award, winnerText }) => {
23+
const AwardWinner: React.FC<AwardWinnerProps> = ({ award, showWinner = true }) => {
24+
const t = useTranslations('components:events:award-winner');
25+
const awardNameToText = useLocaleAwardName();
26+
27+
const isTeamAward = typeof award.winner !== 'string';
28+
const winnerText: string = isTeamAward
29+
? `${(award.winner as PortalTeam).name} #${(award.winner as PortalTeam).number}`
30+
: (award.winner as string);
31+
1832
const showTrophys = ![...CoreValuesAwardsTypes, ...PersonalAwardTypes].includes(
1933
award.name as CoreValuesAwards | PersonalAwards
2034
);
2135

2236
return (
2337
<Stack direction="row" spacing={1} mt={1}>
2438
{typeof award.winner === 'string' ? (
25-
<Typography variant="body1">{winnerText}</Typography>
39+
<Typography variant="body1">{award.winner}</Typography>
2640
) : (
2741
<>
2842
{showTrophys && <EmojiEventsIcon sx={{ mr: 1, color: getColorByPlace(award.place) }} />}
29-
<Typography variant="body1">{winnerText}</Typography>
43+
<Typography variant="body1">
44+
{showWinner && winnerText}
45+
{!showWinner && t('award.name', { name: awardNameToText(award.name) })}
46+
{!showWinner && showTrophys && t('award.place', { place: award.place })}
47+
</Typography>
3048
</>
3149
)}
3250
</Stack>

apps/portal/components/events/event-link.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import {
99
Divider,
1010
Stack,
1111
Typography,
12+
useTheme,
1213
useMediaQuery
1314
} from '@mui/material';
1415
import Grid, { GridProps } from '@mui/material/Grid';
15-
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
1616
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
1717
import { PortalEvent } from '@lems/types';
18-
import theme from '../../lib/theme';
18+
import ChevronEndIcon from '../icons/chevron-end';
1919

2020
interface EventDescriptionProps extends GridProps {
2121
event: PortalEvent;
@@ -71,6 +71,7 @@ interface EventLinkProps {
7171
}
7272

7373
const EventLink: React.FC<EventLinkProps> = ({ event, includeDate = false }) => {
74+
const theme = useTheme();
7475
const isDesktop = useMediaQuery(theme.breakpoints.up('md'));
7576

7677
if (!event.divisions || event.divisions.length === 0) {
@@ -82,7 +83,7 @@ const EventLink: React.FC<EventLinkProps> = ({ event, includeDate = false }) =>
8283
'& .MuiButton-endIcon svg': { fontSize: 24 },
8384
borderRadius: 3
8485
}}
85-
endIcon={<ChevronLeftIcon sx={{ color: 'rgba(0, 0, 0, 0.54)', position: 'relative' }} />}
86+
endIcon={<ChevronEndIcon sxProps={{ position: 'relative' }} />}
8687
fullWidth
8788
size="small"
8889
LinkComponent={Link}
@@ -144,7 +145,7 @@ const EventLink: React.FC<EventLinkProps> = ({ event, includeDate = false }) =>
144145
minHeight: 48,
145146
borderRadius: 3
146147
}}
147-
endIcon={<ChevronLeftIcon sx={{ color: 'rgba(0, 0, 0, 0.54)' }} />}
148+
endIcon={<ChevronEndIcon />}
148149
fullWidth
149150
size="small"
150151
LinkComponent={Link}

apps/portal/components/events/event-quick-links.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import Link from 'next/link';
22
import { Button, Paper } from '@mui/material';
33
import Grid from '@mui/material/Grid';
44
import { PortalEvent } from '@lems/types';
5+
import { useTranslations } from 'next-intl';
56

67
interface EventQuickLinksProps {
78
event: PortalEvent;
89
hasAwards: boolean;
910
}
1011

1112
const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) => {
13+
const t = useTranslations('components:events:event-quick-links');
14+
1215
return (
1316
<Grid
1417
container
@@ -25,7 +28,7 @@ const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) =
2528
LinkComponent={Link}
2629
href={`/events/${event.id}/scoreboard`}
2730
>
28-
לוח תוצאות
31+
{t('scoreboard')}
2932
</Button>
3033
</Grid>
3134
{hasAwards && (
@@ -37,7 +40,7 @@ const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) =
3740
LinkComponent={Link}
3841
href={`/events/${event.id}/awards`}
3942
>
40-
פרסים
43+
{t('awards')}
4144
</Button>
4245
</Grid>
4346
)}
@@ -49,7 +52,7 @@ const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) =
4952
LinkComponent={Link}
5053
href={`/events/${event.id}/schedule/field`}
5154
>
52-
לוח זמנים - זירה
55+
{t('field-schedule')}
5356
</Button>
5457
</Grid>
5558
<Grid size={{ xs: 6, md: 3 }}>
@@ -60,7 +63,7 @@ const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) =
6063
LinkComponent={Link}
6164
href={`/events/${event.id}/schedule/judging`}
6265
>
63-
לוח זמנים - שיפוט
66+
{t('judging-schedule')}
6467
</Button>
6568
</Grid>
6669
<Grid size={{ xs: 6, md: 3 }}>
@@ -71,7 +74,7 @@ const EventQuickLinks: React.FC<EventQuickLinksProps> = ({ event, hasAwards }) =
7174
LinkComponent={Link}
7275
href={`/events/${event.id}/schedule/general`}
7376
>
74-
לוח זמנים כללי
77+
{t('general-schedule')}
7578
</Button>
7679
</Grid>
7780
</Grid>

apps/portal/components/events/event-status.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,57 @@
11
import dayjs from 'dayjs';
22
import { Paper, Stack, Typography } from '@mui/material';
33
import Grid from '@mui/material/Grid';
4-
import { PortalEvent, PortalEventStatus } from '@lems/types';
4+
import { PortalEventStatus } from '@lems/types';
55
import LiveIcon from '../live-icon';
6+
import { useTranslations } from 'next-intl';
67

78
interface EventStatusProps {
8-
event: PortalEvent;
99
status: PortalEventStatus;
1010
}
1111

12-
const EventStatus: React.FC<EventStatusProps> = ({ event, status }) => {
12+
const EventStatus: React.FC<EventStatusProps> = ({ status }) => {
13+
const t = useTranslations('components:events:event-status');
14+
1315
const hasCurrentMatch = status.field.match.number > 0;
1416
const hasCurrentSession = status.judging.session.number > 0;
1517

1618
return (
1719
<Paper sx={{ p: 2, my: 2, width: '100%' }}>
1820
<Stack direction="row" alignItems="center" spacing={2} mb={1}>
1921
<Typography variant="h2" maxWidth="90%">
20-
אירוע פעיל
22+
{t('active-event')}
2123
</Typography>
2224
<LiveIcon />
2325
</Stack>
2426
<Grid container width="100%">
2527
<Grid size={{ xs: 12, md: 6 }}>
2628
{hasCurrentMatch ? (
2729
<>
28-
<Typography variant="h6">מקצה נוכחי - מקצה #{status.field.match.number}</Typography>
29-
<Typography color="text.secondary" gutterBottom>
30-
זמן מתוכנן: {dayjs(status.field.match.time).format('HH:mm')}
30+
<Typography variant="h6">
31+
{t('current-match', { number: status.field.match.number })}
32+
</Typography>
33+
<Typography variant="h6">
34+
{t('current-match-time', { time: dayjs(status.field.match.time).format('HH:mm') })}
3135
</Typography>
3236
</>
3337
) : (
34-
<Typography variant="h6">כל המקצים הושלמו</Typography>
38+
<Typography variant="h6">{t('all-matches-completed')}</Typography>
3539
)}
3640
</Grid>
3741
<Grid size={{ xs: 12, md: 6 }}>
3842
{hasCurrentSession ? (
3943
<>
40-
<Typography variant="h6">
41-
סבב שיפוט נוכחי - סבב #{status.judging.session.number}
44+
<Typography color="text.secondary" gutterBottom>
45+
{t('current-session', { number: status.judging.session.number })}
4246
</Typography>
4347
<Typography color="text.secondary" gutterBottom>
44-
זמן מתוכנן: {dayjs(status.judging.session.time).format('HH:mm')}
48+
{t('current-session-time', {
49+
time: dayjs(status.judging.session.time).format('HH:mm')
50+
})}
4551
</Typography>
4652
</>
4753
) : (
48-
<Typography variant="h6">כל סבבי השיפוט הושלמו</Typography>
54+
<Typography variant="h6">{t('all-sessions-completed')}</Typography>
4955
)}
5056
</Grid>
5157
</Grid>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { SvgIconProps, SxProps, useTheme } from '@mui/material';
2+
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
3+
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
4+
5+
interface DirectionalIconProps extends SvgIconProps {
6+
sxProps?: SxProps;
7+
}
8+
9+
const ChevronEndIcon: React.FC<DirectionalIconProps> = ({ sxProps, ...props }) => {
10+
const theme = useTheme();
11+
const isRtl = theme.direction === 'rtl';
12+
13+
return isRtl ? (
14+
<ChevronLeftIcon {...props} sx={{ ...sxProps, color: 'rgba(0, 0, 0, 0.54)' }} />
15+
) : (
16+
<ChevronRightIcon {...props} sx={{ ...sxProps, color: 'rgba(0, 0, 0, 0.54)' }} />
17+
);
18+
};
19+
20+
export default ChevronEndIcon;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { SvgIconProps, SxProps, useTheme } from '@mui/material';
2+
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
3+
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
4+
5+
interface DirectionalIconProps extends SvgIconProps {
6+
sxProps?: SxProps;
7+
}
8+
9+
const ChevronStartIcon: React.FC<DirectionalIconProps> = ({ sxProps, ...props }) => {
10+
const theme = useTheme();
11+
const isRtl = theme.direction === 'rtl';
12+
13+
return isRtl ? (
14+
<ChevronRightIcon {...props} sx={{ ...sxProps, color: 'rgba(0, 0, 0, 0.54)' }} />
15+
) : (
16+
<ChevronLeftIcon {...props} sx={{ ...sxProps, color: 'rgba(0, 0, 0, 0.54)' }} />
17+
);
18+
};
19+
20+
export default ChevronStartIcon;

0 commit comments

Comments
 (0)