Skip to content

Commit e6ac035

Browse files
authored
Set up more frontend i18n part 1 (#3306)
* Fix typing and remove unused import * Migrate some strings into translation files * Register and use migrated translations * Add machine translations for Mandarin * Refactor welcome page to use CSS and translations * Add and use Stepper translations * Remove old Stepper * Move side content to separate namespace * Move remaining side content strings * Add add register side content machine translations * Refactor side content to use translations * Fix result card not being registered * Refactor CSE default text to own component * Use new CSE machine translations * Register Chinese language for translation * Use translation files in more side content components * Update dataviz to use translations * Add TODO and update label * Update session management * Remove now-unused constant * Update stories translations * Fix lint * Fix welcome page translations * Revert SICP changes * Update test snapshots
1 parent ecc74bf commit e6ac035

Some content is hidden

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

58 files changed

+787
-559
lines changed

src/commons/sideContent/__tests__/__snapshots__/SideContentCseMachine.test.tsx.snap

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,7 @@ exports[`CSE Machine component renders correctly 1`] = `
444444
id="cse-machine-default-text"
445445
>
446446
<span>
447-
The CSE machine generates control, stash and environment model diagrams following a notation introduced in
448-
447+
The CSE machine generates control, stash and environment model diagrams following a notation introduced in
449448
<a
450449
href="https://sourceacademy.org/sicpjs/3.2"
451450
rel="noopener noreferrer"
@@ -455,11 +454,11 @@ exports[`CSE Machine component renders correctly 1`] = `
455454
Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3, Section 2
456455
</i>
457456
</a>
457+
.
458458
</span>
459-
.
460459
<br />
461460
<br />
462-
On this tab, the REPL will be hidden from view, so do check that your code has no errors before running the stepper. You may use this tool by running your program and then dragging the slider above to see the state of the control, stash and environment at different stages in the evaluation of your program. Clicking on the fast-forward button (double chevron) will take you to the next breakpoint in your program
461+
On this tab, the REPL will be hidden from view, so do check that your code has no errors before running the stepper. You may use this tool by running your program and then dragging the slider above to see the state of the control, stash and environment at different stages in the evaluation of your program. Clicking on the fast-forward button (double chevron) will take you to the next breakpoint in your program.
463462
<br />
464463
<br />
465464
<div

src/commons/sideContent/content/SideContentAutograder.tsx

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Button, Collapse, Icon, PopoverPosition, Tooltip } from '@blueprintjs/core';
22
import { IconNames } from '@blueprintjs/icons';
3-
import React from 'react';
3+
import { useCallback, useMemo, useState } from 'react';
4+
import { useTranslation } from 'react-i18next';
45

56
import { AutogradingResult, Testcase } from '../../assessment/AssessmentTypes';
67
import ControlButton from '../../ControlButton';
@@ -28,12 +29,50 @@ type OwnProps = {
2829
};
2930

3031
const SideContentAutograder: React.FC<SideContentAutograderProps> = props => {
31-
const [showsTestcases, setTestcasesShown] = React.useState(true);
32-
const [showsResults, setResultsShown] = React.useState(true);
32+
const { t } = useTranslation('sideContent', { keyPrefix: 'autograder' });
33+
const [showsTestcases, setTestcasesShown] = useState(true);
34+
const [showsResults, setResultsShown] = useState(true);
3335

3436
const { testcases, autogradingResults, handleTestcaseEval, workspaceLocation } = props;
3537

36-
const testcaseCards = React.useMemo(
38+
const autograderTooltip = useMemo(
39+
() => (
40+
<div className="autograder-help-tooltip">
41+
<p>{t('tooltip.clickTestcase')}</p>
42+
<p>{t('tooltip.executeAll')}</p>
43+
<p>{t('tooltip.backgroundInfo')}</p>
44+
<p>{t('tooltip.privateTestcases')}</p>
45+
</div>
46+
),
47+
[t]
48+
);
49+
50+
const testcasesHeader = useMemo(
51+
() => (
52+
<div className="testcases-header" data-testid="testcases-header">
53+
{columnHeader('header-fn', t('headers.testcase'))}
54+
{columnHeader('header-expected', t('headers.expected'))}
55+
{columnHeader('header-actual', t('headers.actual'))}
56+
</div>
57+
),
58+
[t]
59+
);
60+
61+
const resultsHeader = useMemo(
62+
() => (
63+
<div className="results-header" data-testid="results-header">
64+
<div className="header-data">
65+
{columnHeader('header-sn', t('headers.sn'))}
66+
{columnHeader('header-status', t('headers.status'))}
67+
</div>
68+
{columnHeader('header-expected', t('headers.expected'))}
69+
{columnHeader('header-actual', t('headers.actual'))}
70+
</div>
71+
),
72+
[t]
73+
);
74+
75+
const testcaseCards = useMemo(
3776
() =>
3877
testcases.length > 0 ? (
3978
<div className="testcaseCards">
@@ -50,13 +89,13 @@ const SideContentAutograder: React.FC<SideContentAutograderProps> = props => {
5089
</div>
5190
) : (
5291
<div className="noResults" data-testid="noResults">
53-
There are no testcases provided for this question.
92+
{t('noTestcases')}
5493
</div>
5594
),
56-
[testcases, handleTestcaseEval, workspaceLocation]
95+
[testcases, testcasesHeader, t, handleTestcaseEval, workspaceLocation]
5796
);
5897

59-
const resultCards = React.useMemo(
98+
const resultCards = useMemo(
6099
() =>
61100
autogradingResults.length > 0 ? (
62101
<div>
@@ -67,17 +106,17 @@ const SideContentAutograder: React.FC<SideContentAutograderProps> = props => {
67106
</div>
68107
) : (
69108
<div className="noResults" data-testid="noResults">
70-
There are no results to show.
109+
{t('noResults')}
71110
</div>
72111
),
73-
[autogradingResults]
112+
[autogradingResults, resultsHeader, t]
74113
);
75114

76-
const toggleTestcases = React.useCallback(() => {
115+
const toggleTestcases = useCallback(() => {
77116
setTestcasesShown(!showsTestcases);
78117
}, [showsTestcases]);
79118

80-
const toggleResults = React.useCallback(() => setResultsShown(!showsResults), [showsResults]);
119+
const toggleResults = useCallback(() => setResultsShown(!showsResults), [showsResults]);
81120

82121
return (
83122
<div className="Autograder">
@@ -87,59 +126,29 @@ const SideContentAutograder: React.FC<SideContentAutograderProps> = props => {
87126
minimal={true}
88127
onClick={toggleTestcases}
89128
>
90-
<span>Testcases</span>
129+
<span>{t('testcases')}</span>
91130
<Tooltip content={autograderTooltip} placement={PopoverPosition.LEFT}>
92131
<Icon icon={IconNames.HELP} />
93132
</Tooltip>
94133
</Button>
95134
<Collapse isOpen={showsTestcases} keepChildrenMounted={true}>
96135
{testcaseCards}
97136
</Collapse>
98-
{collapseButton('Autograder Results', showsResults, toggleResults)}
137+
{collapseButton(t('results'), showsResults, toggleResults)}
99138
<Collapse isOpen={showsResults} keepChildrenMounted={true}>
100139
{resultCards}
101140
</Collapse>
102141
</div>
103142
);
104143
};
105144

106-
const autograderTooltip = (
107-
<div className="autograder-help-tooltip">
108-
<p>Click on each testcase below to execute it with the program in the editor.</p>
109-
<p>
110-
To execute all testcases at once, evaluate the program in the editor with this tab active.
111-
</p>
112-
<p>A green or red background indicates a passed or failed testcase respectively.</p>
113-
<p>Private testcases (only visible to staff when grading) have a grey background.</p>
114-
</div>
115-
);
116-
117145
const columnHeader = (colClass: string, colTitle: string) => (
118146
<div className={colClass}>
119147
{colTitle}
120148
<Icon icon={IconNames.CARET_DOWN} />
121149
</div>
122150
);
123151

124-
const testcasesHeader = (
125-
<div className="testcases-header" data-testid="testcases-header">
126-
{columnHeader('header-fn', 'Testcase')}
127-
{columnHeader('header-expected', 'Expected result')}
128-
{columnHeader('header-actual', 'Actual result')}
129-
</div>
130-
);
131-
132-
const resultsHeader = (
133-
<div className="results-header" data-testid="results-header">
134-
<div className="header-data">
135-
{columnHeader('header-sn', 'S/N')}
136-
{columnHeader('header-status', 'Testcase status')}
137-
</div>
138-
{columnHeader('header-expected', 'Expected result')}
139-
{columnHeader('header-actual', 'Actual result')}
140-
</div>
141-
);
142-
143152
const collapseButton = (label: string, isOpen: boolean, toggleFunc: () => void) => (
144153
<ControlButton
145154
label={label}

src/commons/sideContent/content/SideContentContestLeaderboard.tsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Button, Collapse, Icon, Tooltip } from '@blueprintjs/core';
22
import { IconNames } from '@blueprintjs/icons';
33
import React, { useMemo, useState } from 'react';
4+
import { useTranslation } from 'react-i18next';
45

56
import { ContestEntry } from '../../assessment/AssessmentTypes';
67
import { SideContentType } from '../SideContentTypes';
@@ -26,6 +27,7 @@ type StateProps = {
2627
* handleContestEntryClick: displays contest entry answer in assessment workspace editor}
2728
*/
2829
const SideContentContestLeaderboard: React.FC<SideContentContestLeaderboardProps> = props => {
30+
const { t } = useTranslation('sideContent', { keyPrefix: 'contestLeaderboard' });
2931
const { orderedContestEntries, handleContestEntryClick, leaderboardType } = props;
3032
const [showLeaderboard, setShowLeaderboard] = useState(true);
3133

@@ -35,19 +37,19 @@ const SideContentContestLeaderboard: React.FC<SideContentContestLeaderboardProps
3537

3638
const leaderboardTitle = useMemo(() => {
3739
return leaderboardType === SideContentType.scoreLeaderboard
38-
? 'Score Leaderboard'
40+
? t('titles.score')
3941
: leaderboardType === SideContentType.popularVoteLeaderboard
40-
? 'Popular Vote Leaderboard'
41-
: 'Contest Leaderboard';
42-
}, [leaderboardType]);
42+
? t('titles.popularVote')
43+
: t('titles.default');
44+
}, [leaderboardType, t]);
4345

4446
const contestLeaderboardTooltipContent = useMemo(() => {
4547
return leaderboardType === SideContentType.scoreLeaderboard
46-
? 'View the highest scoring contest entries!'
48+
? t('tooltips.score')
4749
: leaderboardType === SideContentType.popularVoteLeaderboard
48-
? 'View the most popular contest entries!'
49-
: 'View the top-rated contest entries!';
50-
}, [leaderboardType]);
50+
? t('tooltips.popularVote')
51+
: t('tooltips.default');
52+
}, [leaderboardType, t]);
5153

5254
const columnHeader = (colClass: string, colTitle: string) => (
5355
<div className={colClass}>
@@ -59,19 +61,19 @@ const SideContentContestLeaderboard: React.FC<SideContentContestLeaderboardProps
5961
const contestEntryHeader = useMemo(() => {
6062
return (
6163
<div className="leaderboard-header">
62-
{columnHeader('header-entryid', 'Student Name')}
63-
{columnHeader('header-entryrank', 'Rank')}
64+
{columnHeader('header-entryid', t('headers.studentName'))}
65+
{columnHeader('header-entryrank', t('headers.rank'))}
6466
{columnHeader(
6567
'header-score',
6668
leaderboardType === SideContentType.scoreLeaderboard
67-
? 'Calculated Score'
69+
? t('headers.score.calculated')
6870
: leaderboardType === SideContentType.popularVoteLeaderboard
69-
? 'Popularity Score'
70-
: 'Metric'
71+
? t('headers.score.popularity')
72+
: t('headers.score.default')
7173
)}
7274
</div>
7375
);
74-
}, [leaderboardType]);
76+
}, [leaderboardType, t]);
7577

7678
const contestEntryCards = useMemo(
7779
() => (
@@ -87,11 +89,11 @@ const SideContentContestLeaderboard: React.FC<SideContentContestLeaderboardProps
8789
/>
8890
))
8991
) : (
90-
<div className="noResults">There are no eligible contest leaderboard entries found.</div>
92+
<div className="noResults">{t('noEntries')}</div>
9193
)}
9294
</div>
9395
),
94-
[handleContestEntryClick, orderedContestEntries, contestEntryHeader]
96+
[handleContestEntryClick, orderedContestEntries, contestEntryHeader, t]
9597
);
9698

9799
return (

src/commons/sideContent/content/SideContentContestVoting.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Button, Card, Classes, Collapse, Elevation, Icon, Pre, Tooltip } from '
22
import { IconNames } from '@blueprintjs/icons';
33
import classNames from 'classnames';
44
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5+
import { useTranslation } from 'react-i18next';
56

67
import { ContestEntry } from '../../assessment/AssessmentTypes';
78

@@ -34,6 +35,7 @@ const SideContentContestVoting: React.FC<SideContentContestVotingProps> = ({
3435
handleContestEntryClick,
3536
handleVotingSubmissionChange
3637
}) => {
38+
const { t } = useTranslation('sideContent', { keyPrefix: 'contestVoting' });
3739
const [showContestEntries, setShowContestEntries] = useState(true);
3840
const [currentDraggedItem, setCurrentDraggedItem] = useState<HTMLElement | null>(null);
3941
const [hoveredTier, setHoveredTier] = useState<string | null>(null);
@@ -166,21 +168,22 @@ const SideContentContestVoting: React.FC<SideContentContestVotingProps> = ({
166168
</div>
167169
</div>
168170
) : (
169-
<div className="noResults">There are no eligible entries for voting found.</div>
171+
<div className="noResults">{t('noEntries')}</div>
170172
)}
171173
</div>
172174
),
173175
[
174-
isValid,
175-
canSave,
176+
tierBoard,
176177
sortedContestEntries,
177-
handleContestEntryClick,
178-
handleDragEnd,
179-
handleDragEnter,
180-
handleDragLeave,
181178
handleDragOver,
179+
handleDragLeave,
180+
handleDragEnter,
182181
handleDrop,
183-
tierBoard
182+
t,
183+
isValid,
184+
canSave,
185+
handleDragEnd,
186+
handleContestEntryClick
184187
]
185188
);
186189

@@ -209,15 +212,11 @@ const SideContentContestVoting: React.FC<SideContentContestVotingProps> = ({
209212
<Button
210213
className="collapse-button"
211214
icon={showContestEntries ? IconNames.CARET_DOWN : IconNames.CARET_RIGHT}
212-
minimal={true}
215+
variant="minimal"
213216
onClick={() => setShowContestEntries(!showContestEntries)}
214217
>
215-
<span>Contest Voting</span>
216-
<Tooltip
217-
content={
218-
<span>Rank your favourite contest entries from tiers D (worst) to S (best)!</span>
219-
}
220-
>
218+
<span>{t('title')}</span>
219+
<Tooltip content={<span>{t('tooltip')}</span>}>
221220
<Icon icon={IconNames.HELP} />
222221
</Tooltip>
223222
</Button>

0 commit comments

Comments
 (0)