Skip to content

Commit 44ccb08

Browse files
Add missing translation strings (#1222)
closes RaspberryPiFoundation/digital-editor-issues#697
1 parent 018a5b0 commit 44ccb08

10 files changed

Lines changed: 107 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1414
- Changed `SkulptRunner.jsx` implementation of hiding elements to use `display: none` rather than `block-size: 0` (#1219)
1515
- Enabled `hyphens: auto` globally (with exceptions) to prevent overflow with longer words (#1215)
1616
- Removed fixed size from `ProjectBar` to prevent overflow when text wraps (#1221)
17+
- Added missing translation strings (#1222)
1718

1819
## Changed
1920

src/components/Button/Button.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
55
import classNames from "classnames";
66

77
import "../../assets/stylesheets/Button.scss";
8+
import { useTranslation } from "react-i18next";
89

910
const Button = (props) => {
1011
const {
@@ -26,6 +27,8 @@ const Button = (props) => {
2627
buttonIconPosition = "left",
2728
} = props;
2829

30+
const { t } = useTranslation();
31+
2932
const buttonClass = classNames("btn", className, {
3033
"btn--svg-only": !buttonText,
3134
});
@@ -40,11 +43,11 @@ const Button = (props) => {
4043
message: confirmText,
4144
buttons: [
4245
{
43-
label: "Yes",
46+
label: t("button.yes"),
4447
onClick: () => onClickHandler(e),
4548
},
4649
{
47-
label: "No",
50+
label: t("button.no"),
4851
},
4952
],
5053
});

src/components/Editor/EditorInput/EditorInput.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ const EditorInput = () => {
163163
{!["main.py", "index.html"].includes(fileName) ? (
164164
<Button
165165
className="btn--tertiary react-tabs__tab-close-btn"
166-
label="close"
166+
label={t("editorPanel.close")}
167167
onClickHandler={(e) => closeFileTab(e, fileName)}
168168
ButtonIcon={() => <CloseIcon scaleFactor={0.85} />}
169169
/>

src/components/Editor/ImageUploadButton/ImageUploadButton.jsx

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,13 @@ import Button from "../../Button/Button";
1010
import NameErrorMessage from "../ErrorMessage/NameErrorMessage";
1111
import store from "../../../app/store";
1212
import ApiCallHandler from "../../../utils/apiCallHandler";
13+
import { useTranslation } from "react-i18next";
14+
import { allowedExtensionsString } from "../../../utils/allowedExtensionsString";
1315

1416
const allowedExtensions = {
1517
python: ["jpg", "jpeg", "png", "gif"],
1618
};
1719

18-
const allowedExtensionsString = (projectType) => {
19-
const extensionsList = allowedExtensions[projectType];
20-
if (extensionsList.length === 1) {
21-
return `'.${extensionsList[0]}'`;
22-
} else {
23-
return (
24-
`'.` +
25-
extensionsList.slice(0, -1).join(`', '.`) +
26-
`' or '.` +
27-
extensionsList[extensionsList.length - 1] +
28-
`'`
29-
);
30-
}
31-
};
32-
3320
const ImageUploadButton = ({ reactAppApiEndpoint }) => {
3421
const [modalIsOpen, setIsOpen] = useState(false);
3522
const [files, setFiles] = useState([]);
@@ -41,6 +28,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
4128
const projectImages = useSelector((state) => state.editor.project.image_list);
4229
const imageNames = projectImages.map((image) => `${image.filename}`);
4330
const user = useSelector((state) => state.auth.user);
31+
const { t } = useTranslation();
4432

4533
const closeModal = () => {
4634
setFiles([]);
@@ -62,19 +50,27 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
6250
imageNames.includes(fileName) ||
6351
files.filter((file) => file.name === fileName).length > 1
6452
) {
65-
dispatch(setNameError("Image names must be unique."));
53+
dispatch(
54+
setNameError(t("imageUploadButton.errors.imageNameNotUnique")),
55+
);
6656
return false;
6757
} else if (isValidFileName(fileName, files)) {
6858
return true;
6959
} else if (!allowedExtensions[projectType].includes(extension)) {
7060
dispatch(
7161
setNameError(
72-
`Image names must end in ${allowedExtensionsString(projectType)}.`,
62+
t("errors.invalidImageExtension", {
63+
extensions: allowedExtensionsString(
64+
projectType,
65+
t,
66+
allowedExtensions,
67+
),
68+
}),
7369
),
7470
);
7571
return false;
7672
} else {
77-
dispatch(setNameError("Error"));
73+
dispatch(setNameError("imageUploadButton.error"));
7874
return false;
7975
}
8076
});
@@ -119,7 +115,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
119115
return (
120116
<>
121117
<Button
122-
buttonText="Upload Image"
118+
buttonText={t("imageUploadButton.uploadImage")}
123119
onClickHandler={showModal}
124120
className="proj-image-upload-button"
125121
/>
@@ -128,10 +124,10 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
128124
isOpen={modalIsOpen}
129125
onRequestClose={closeModal}
130126
style={customStyles}
131-
contentLabel="Upload Image"
127+
contentLabel={t("imageUploadButton.uploadImage")}
132128
appElement={document.getElementById("root") || undefined}
133129
>
134-
<h2>Upload an image</h2>
130+
<h2>{t("imageUploadButton.uploadAnImage")}</h2>
135131

136132
<NameErrorMessage />
137133
<Dropzone
@@ -143,9 +139,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
143139
<section>
144140
<div {...getRootProps()} className="dropzone-area">
145141
<input {...getInputProps()} />
146-
<p className="dropzone-info">
147-
Drag and drop images here, or click to select images from file
148-
</p>
142+
<p className="dropzone-info">{t("imageUploadButton.info")}</p>
149143
{files.map((file, i) => (
150144
<p key={i}>{file.name}</p>
151145
))}
@@ -154,8 +148,14 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
154148
)}
155149
</Dropzone>
156150
<div className="modal-footer">
157-
<Button buttonText="Cancel" onClickHandler={closeModal} />
158-
<Button buttonText="Save" onClickHandler={saveImages} />
151+
<Button
152+
buttonText={t("imageUploadButton.cancel")}
153+
onClickHandler={closeModal}
154+
/>
155+
<Button
156+
buttonText={t("imageUploadButton.save")}
157+
onClickHandler={saveImages}
158+
/>
159159
</div>
160160
</Modal>
161161
</>

src/components/Editor/ImageUploadButton/ImageUploadButton.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ describe("When user logged in and owns project", () => {
4444
</div>
4545
</Provider>,
4646
));
47-
button = queryByText(/Upload Image/);
47+
button = queryByText("imageUploadButton.uploadImage");
4848
});
4949

5050
test("Modal opens when Image Upload button clicked", () => {
5151
fireEvent.click(button);
52-
const dropzone = queryByText(/Drag and drop/);
52+
const dropzone = queryByText("imageUploadButton.info");
5353
expect(dropzone).not.toBeNull();
5454
});
5555

5656
test("Modal closes when cancel button clicked", () => {
5757
fireEvent.click(button);
58-
const cancelButton = queryByText(/Cancel/);
58+
const cancelButton = queryByText("imageUploadButton.cancel");
5959
fireEvent.click(cancelButton);
60-
const dropzone = queryByText(/Drag and drop/);
60+
const dropzone = queryByText("imageUploadButton.info");
6161
expect(dropzone).toBeNull();
6262
});
6363
});

src/components/Editor/NewInputPanelButton/NewInputPanelButton.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ import React from "react";
22
import Button from "../../Button/Button";
33
import { useDispatch } from "react-redux";
44
import { addFilePanel } from "../EditorSlice";
5+
import { useTranslation } from "react-i18next";
56

67
const NewInputPanelButton = () => {
78
const dispatch = useDispatch();
9+
const { t } = useTranslation();
810

911
const openNewPanel = () => {
1012
dispatch(addFilePanel());
1113
};
1214
return (
1315
<Button
1416
className={"btn--primary"}
15-
buttonText="Add another panel"
17+
buttonText={t("newInputPanelButton.buttonText")}
1618
onClickHandler={openNewPanel}
1719
/>
1820
);

src/components/Modals/ErrorModal.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const ErrorModal = ({ errorType, additionalOnClose }) => {
3030
onRequestClose={closeModal}
3131
className="modal-content"
3232
overlayClassName="modal-overlay"
33-
contentLabel="Error"
33+
contentLabel={t("modal.error.error")}
3434
parentSelector={() =>
3535
document.querySelector("#app") ||
3636
document.querySelector("editor-wc").shadowRoot.querySelector("#wc")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const allowedExtensionsString = (projectType, t, allowedExtensions) => {
2+
const extensionsList = allowedExtensions[projectType];
3+
if (extensionsList.length === 1) {
4+
return `'.${extensionsList[0]}'`;
5+
} else {
6+
return `'.${extensionsList.slice(0, -1).join(`', '.`)}' ${t(
7+
"common.or",
8+
)} '.${extensionsList[extensionsList.length - 1]}'`;
9+
}
10+
};

src/utils/componentNameValidation.js

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { setNameError } from "../redux/EditorSlice";
2+
import { allowedExtensionsString } from "./allowedExtensionsString";
23

34
const allowedExtensions = {
45
python: ["py", "csv", "txt"],
@@ -7,17 +8,6 @@ const allowedExtensions = {
78

89
const reservedFileNames = ["INSTRUCTIONS.md"];
910

10-
const allowedExtensionsString = (projectType, t) => {
11-
const extensionsList = allowedExtensions[projectType];
12-
if (extensionsList.length === 1) {
13-
return `'.${extensionsList[0]}'`;
14-
} else {
15-
return `'.${extensionsList.slice(0, -1).join(`', '.`)}' ${t(
16-
"filePanel.errors.or",
17-
)} '.${extensionsList[extensionsList.length - 1]}'`;
18-
}
19-
};
20-
2111
const isValidFileName = (fileName, projectType, componentNames) => {
2212
const extension = fileName.split(".").slice(1).join(".");
2313
if (
@@ -59,7 +49,11 @@ export const validateFileName = (
5949
dispatch(
6050
setNameError(
6151
t("filePanel.errors.unsupportedExtension", {
62-
allowedExtensions: allowedExtensionsString(projectType, t),
52+
allowedExtensions: allowedExtensionsString(
53+
projectType,
54+
t,
55+
allowedExtensions,
56+
),
6357
}),
6458
),
6559
);

src/utils/i18n.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ i18n
116116
modal: {
117117
close: "Close",
118118
error: {
119+
error: "Error",
119120
heading: "An error has occurred",
120121
externalLink: {
121122
message:
@@ -129,6 +130,7 @@ i18n
129130
characterLimitExplanation:
130131
"Files in the editor are limited to {{maxCharacters}} characters",
131132
viewOnly: "View only",
133+
close: "close",
132134
},
133135
filePanel: {
134136
errors: {
@@ -137,7 +139,6 @@ i18n
137139
containsSpaces: "File names must not contain spaces.",
138140
generalError: "Error",
139141
notUnique: "File names must be unique.",
140-
or: "or",
141142
unsupportedExtension:
142143
"File names must end in {{allowedExtensions}}.",
143144
},
@@ -416,13 +417,37 @@ i18n
416417
loading: "Loading",
417418
failed: "Load failed",
418419
},
420+
imageUploadButton: {
421+
uploadImage: "Upload Image",
422+
uploadAnImage: "Upload an image",
423+
info: "Drag and drop images here, or click to select images from file",
424+
cancel: "Cancel",
425+
save: "Save",
426+
or: "or",
427+
errors: {
428+
error: "Error",
429+
imageNameNotUnique: "Image names must be unique.",
430+
invalidImageExtension: "Image names must end in {{extensions}}.",
431+
},
432+
},
433+
newInputPanelButton: {
434+
buttonText: "Add another panel",
435+
},
436+
button: {
437+
yes: "Yes",
438+
no: "No",
439+
},
440+
common: {
441+
or: "or",
442+
},
419443
},
420444
},
421445
"xx-XX": {
422446
translation: {
423447
modal: {
424448
close: "Schließen",
425449
error: {
450+
error: "Fehler",
426451
heading: "Ein Fehler ist aufgetreten",
427452
externalLink: {
428453
message:
@@ -436,6 +461,7 @@ i18n
436461
characterLimitExplanation:
437462
"Dateien im Editor sind auf {{maxCharacters}} Zeichen begrenzt",
438463
viewOnly: "Nur Ansicht",
464+
close: "schließen",
439465
},
440466
filePanel: {
441467
errors: {
@@ -444,7 +470,6 @@ i18n
444470
containsSpaces: "Dateinamen dürfen keine Leerzeichen enthalten.",
445471
generalError: "Fehler",
446472
notUnique: "Dateinamen müssen eindeutig sein.",
447-
or: "oder",
448473
unsupportedExtension:
449474
"Dateinamen müssen mit {{allowedExtensions}} enden.",
450475
},
@@ -724,6 +749,29 @@ i18n
724749
loading: "Wird geladen",
725750
failed: "Laden fehlgeschlagen",
726751
},
752+
imageUploadButton: {
753+
uploadImage: "Bild hochladen",
754+
uploadAnImage: "Laden Sie ein Bild hoch",
755+
info: "Ziehen Sie Bilder hierher, oder klicken Sie, um Bilder aus der Datei auszuwählen",
756+
cancel: "Stornieren",
757+
save: "Speichern",
758+
errors: {
759+
error: "Fehler",
760+
imageNameNotUnique: "Bildnamen müssen eindeutig sein.",
761+
invalidImageExtension:
762+
"Bildnamen müssen enden auf {{extensions}}.",
763+
},
764+
},
765+
newInputPanelButton: {
766+
buttonText: "Fügen Sie ein weiteres Panel hinzu",
767+
},
768+
button: {
769+
yes: "Ja",
770+
no: "Nein",
771+
},
772+
common: {
773+
or: "oder",
774+
},
727775
},
728776
},
729777
},

0 commit comments

Comments
 (0)