Skip to content

Commit 357890f

Browse files
authored
Merge pull request #1324 from david-roper/playground-translations
Playground translations
2 parents 879da83 + a53b2fc commit 357890f

22 files changed

Lines changed: 292 additions & 125 deletions

apps/playground/src/components/Editor/DeleteFileDialog.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Button, Dialog } from '@douglasneuroinformatics/libui/components';
2+
import { useTranslation } from '@douglasneuroinformatics/libui/hooks';
23

34
import { useAppStore } from '@/store';
45

@@ -10,12 +11,23 @@ export type DeleteFileDialogProps = {
1011

1112
export const DeleteFileDialog = ({ filename, isOpen, setIsOpen }: DeleteFileDialogProps) => {
1213
const deleteFile = useAppStore((store) => store.deleteFile);
14+
const { t } = useTranslation();
1315
return filename ? (
1416
<Dialog open={isOpen} onOpenChange={setIsOpen}>
1517
<Dialog.Content>
1618
<Dialog.Header>
17-
<Dialog.Title>{`Are you sure you want to delete "${filename}"?`}</Dialog.Title>
18-
<Dialog.Description>Once deleted, this file cannot be restored</Dialog.Description>
19+
<Dialog.Title>
20+
{t({
21+
en: `Are you sure you want to delete "${filename}"?`,
22+
fr: `Êtes-vous sûr de vouloir supprimer "${filename}" ?`
23+
})}
24+
</Dialog.Title>
25+
<Dialog.Description>
26+
{t({
27+
en: 'Once deleted, this file cannot be restored',
28+
fr: 'Une fois supprimé, ce fichier ne peut plus être restauré'
29+
})}
30+
</Dialog.Description>
1931
</Dialog.Header>
2032
<Dialog.Footer>
2133
<Button
@@ -25,10 +37,10 @@ export const DeleteFileDialog = ({ filename, isOpen, setIsOpen }: DeleteFileDial
2537
setIsOpen(false);
2638
}}
2739
>
28-
Delete
40+
{t({ en: 'Delete', fr: 'Supprimer' })}
2941
</Button>
3042
<Button type="button" variant="outline" onClick={() => setIsOpen(false)}>
31-
Cancel
43+
{t({ en: 'Cancel', fr: 'Annuler' })}
3244
</Button>
3345
</Dialog.Footer>
3446
</Dialog.Content>

apps/playground/src/components/Editor/Editor.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useRef, useState } from 'react';
22

33
import { extractInputFileExtension } from '@opendatacapture/instrument-bundler';
4+
import { useTranslation } from '@douglasneuroinformatics/libui/hooks';
45
import { Columns3Icon, FilePlusIcon, FileUpIcon } from 'lucide-react';
56
import { motion } from 'motion/react';
67
import { useShallow } from 'zustand/react/shallow';
@@ -29,6 +30,7 @@ export const Editor = () => {
2930

3031
const [isFileUploadDialogOpen, setIsFileUploadDialogOpen] = useState(false);
3132
const [isDeleteFileDialogOpen, setIsDeleteFileDialogOpen] = useState(false);
33+
const { t } = useTranslation();
3234

3335
const addFile = useAppStore((store) => store.addFile);
3436
const addFiles = useAppStore((store) => store.addFiles);
@@ -77,18 +79,22 @@ export const Editor = () => {
7779
return (
7880
<div className="flex h-full w-full flex-col border border-r-0 bg-slate-50 dark:bg-slate-800">
7981
<div className="flex w-full border-b">
80-
<EditorButton icon={<Columns3Icon />} tip="View Files" onClick={() => setIsSidebarOpen(!isSidebarOpen)} />
82+
<EditorButton
83+
icon={<Columns3Icon />}
84+
tip={t({ en: 'View Files', fr: 'Voir les fichiers' })}
85+
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
86+
/>
8187
<EditorButton
8288
icon={<FilePlusIcon />}
83-
tip="Add File"
89+
tip={t({ en: 'Add File', fr: 'Ajouter un fichier' })}
8490
onClick={() => {
8591
setIsSidebarOpen(true);
8692
setIsAddingFile(true);
8793
}}
8894
/>
8995
<EditorButton
9096
icon={<FileUpIcon />}
91-
tip="Upload Files"
97+
tip={t({ en: 'Upload Files', fr: 'Téléverser des fichiers' })}
9298
onClick={() => {
9399
setIsFileUploadDialogOpen(true);
94100
}}
@@ -135,7 +141,9 @@ export const Editor = () => {
135141
{openFilenames.length ? (
136142
<EditorPane ref={editorPaneRef} onEditorMount={() => setIsEditorMounted(true)} />
137143
) : (
138-
<EditorPanePlaceholder>No File Selected</EditorPanePlaceholder>
144+
<EditorPanePlaceholder>
145+
{t({ en: 'No File Selected', fr: 'Aucun fichier sélectionné' })}
146+
</EditorPanePlaceholder>
139147
)}
140148
</div>
141149
<DeleteFileDialog
@@ -157,7 +165,7 @@ export const Editor = () => {
157165
}}
158166
isOpen={isFileUploadDialogOpen}
159167
setIsOpen={setIsFileUploadDialogOpen}
160-
title="Upload Files"
168+
title={t({ en: 'Upload Files', fr: 'Téléverser des fichiers' })}
161169
onSubmit={async (files) => {
162170
const editorFiles = await loadEditorFilesFromNative(files);
163171
addFiles(editorFiles);
@@ -166,7 +174,10 @@ export const Editor = () => {
166174
onValidate={(files: File[]) => {
167175
for (const file of files) {
168176
if (filenames.includes(file.name)) {
169-
return { message: `File already exists: ${file.name}`, result: 'error' };
177+
return {
178+
message: t({ en: `File already exists: ${file.name}`, fr: `Le fichier existe déjà : ${file.name}` }),
179+
result: 'error'
180+
};
170181
}
171182
}
172183
return { result: 'success' };

apps/playground/src/components/Editor/EditorFileButton.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect, useRef, useState } from 'react';
22

3-
import { useOnClickOutside } from '@douglasneuroinformatics/libui/hooks';
3+
import { useOnClickOutside, useTranslation } from '@douglasneuroinformatics/libui/hooks';
44
import { cn } from '@douglasneuroinformatics/libui/utils';
55
import { PencilIcon, TrashIcon } from 'lucide-react';
66

@@ -23,6 +23,7 @@ export const EditorFileButton = ({ filename, isActive, onDelete }: EditorFileBut
2323
const [displayFilename, setDisplayFilename] = useState(filename);
2424
const [isRenaming, setIsRenaming] = useState(false);
2525
const renameFile = useAppStore((store) => store.renameFile);
26+
const { t } = useTranslation();
2627

2728
const rename = () => {
2829
renameFile(filename, displayFilename);
@@ -79,15 +80,15 @@ export const EditorFileButton = ({ filename, isActive, onDelete }: EditorFileBut
7980
}}
8081
>
8182
<TrashIcon />
82-
<span className="ml-2 text-sm">Delete</span>
83+
<span className="ml-2 text-sm">{t({ en: 'Delete', fr: 'Supprimer' })}</span>
8384
</ContextMenu.Item>
8485
<ContextMenu.Item
8586
onSelect={() => {
8687
setIsRenaming(true);
8788
}}
8889
>
8990
<PencilIcon />
90-
<span className="ml-2 text-sm">Rename</span>
91+
<span className="ml-2 text-sm">{t({ en: 'Rename', fr: 'Renommer' })}</span>
9192
</ContextMenu.Item>
9293
</ContextMenu.Content>
9394
</ContextMenu>

apps/playground/src/components/Editor/EditorPane.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
22

3-
import { useTheme } from '@douglasneuroinformatics/libui/hooks';
3+
import { useTheme, useTranslation } from '@douglasneuroinformatics/libui/hooks';
44
import MonacoEditor from '@monaco-editor/react';
55

66
import { useFilesRef } from '@/hooks/useFilesRef';
@@ -29,6 +29,7 @@ export const EditorPane = React.forwardRef<EditorPaneRef, EditorPaneProps>(funct
2929

3030
const [theme] = useTheme();
3131
const [isMounted, setIsMounted] = useState(false);
32+
const { t } = useTranslation();
3233
const { libs } = useRuntime('v1');
3334

3435
const editorRef = useRef<MonacoEditorType | null>(null);
@@ -112,12 +113,21 @@ export const EditorPane = React.forwardRef<EditorPaneRef, EditorPaneProps>(funct
112113
};
113114

114115
if (!defaultFile) {
115-
return <EditorPanePlaceholder>No File Selected</EditorPanePlaceholder>;
116+
return (
117+
<EditorPanePlaceholder>{t({ en: 'No File Selected', fr: 'Aucun fichier sélectionné' })}</EditorPanePlaceholder>
118+
);
116119
}
117120

118121
const fileType = inferFileType(defaultFile.name);
119122
if (!fileType) {
120-
return <EditorPanePlaceholder>{`Error: Invalid file type "${fileType}"`}</EditorPanePlaceholder>;
123+
return (
124+
<EditorPanePlaceholder>
125+
{t({
126+
en: `Error: Invalid file type for "${defaultFile.name}"`,
127+
fr: `Erreur : Type de fichier invalide pour "${defaultFile.name}"`
128+
})}
129+
</EditorPanePlaceholder>
130+
);
121131
} else if (fileType === 'asset') {
122132
if (isBase64EncodedFileType(defaultFile.name)) {
123133
return (
@@ -130,7 +140,11 @@ export const EditorPane = React.forwardRef<EditorPaneRef, EditorPaneProps>(funct
130140
</div>
131141
);
132142
}
133-
return <EditorPanePlaceholder>Cannot Display Binary Asset</EditorPanePlaceholder>;
143+
return (
144+
<EditorPanePlaceholder>
145+
{t({ en: 'Cannot Display Binary Asset', fr: "Impossible d'afficher l'actif binaire" })}
146+
</EditorPanePlaceholder>
147+
);
134148
}
135149

136150
return (

apps/playground/src/components/FileUploadDialog/FileUploadDialog.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useCallback, useEffect, useState } from 'react';
22

33
import { Button, Dialog } from '@douglasneuroinformatics/libui/components';
4+
import { useTranslation } from '@douglasneuroinformatics/libui/hooks';
45
import { CloudUploadIcon } from 'lucide-react';
56
import { useDropzone } from 'react-dropzone';
67
import type { FileRejection } from 'react-dropzone';
@@ -20,11 +21,12 @@ export type FileUploadDialogProps = {
2021
export const FileUploadDialog = ({ accept, isOpen, onSubmit, onValidate, setIsOpen, title }: FileUploadDialogProps) => {
2122
const [files, setFiles] = useState<File[]>([]);
2223
const [errorMessage, setErrorMessage] = useState<null | string>(null);
24+
const { t } = useTranslation();
2325

2426
const handleDrop = useCallback(
2527
(acceptedFiles: File[], rejections: FileRejection[]) => {
2628
for (const { errors, file } of rejections) {
27-
setErrorMessage(`Invalid file type: ${file.name} `);
29+
setErrorMessage(t({ en: `Invalid file type: ${file.name} `, fr: `Type de fichier invalide : ${file.name} ` }));
2830
console.error(errors);
2931
return;
3032
}
@@ -65,9 +67,15 @@ export const FileUploadDialog = ({ accept, isOpen, onSubmit, onValidate, setIsOp
6567
} else if (files.length === 1) {
6668
dropzoneText = files[0]!.name;
6769
} else if (isDragActive) {
68-
dropzoneText = 'Release your cursor to upload file(s)';
70+
dropzoneText = t({
71+
en: 'Release your cursor to upload file(s)',
72+
fr: 'Relâchez votre curseur pour téléverser le(s) fichier(s)'
73+
});
6974
} else {
70-
dropzoneText = 'Click here to upload, or drag and drop files into this area';
75+
dropzoneText = t({
76+
en: 'Click here to upload, or drag and drop files into this area',
77+
fr: 'Cliquez ici pour téléverser, ou glissez et déposez des fichiers dans cette zone'
78+
});
7179
}
7280

7381
return (
@@ -94,7 +102,7 @@ export const FileUploadDialog = ({ accept, isOpen, onSubmit, onValidate, setIsOp
94102
type="button"
95103
onClick={() => void submitFiles(files)}
96104
>
97-
Submit
105+
{t({ en: 'Submit', fr: 'Soumettre' })}
98106
</Button>
99107
</Dialog.Footer>
100108
</Dialog.Content>

apps/playground/src/components/Header/ActionsDropdown/ActionsDropdown.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22

33
import { Button, DropdownMenu } from '@douglasneuroinformatics/libui/components';
4+
import { useTranslation } from '@douglasneuroinformatics/libui/hooks';
45
import { EllipsisVerticalIcon } from 'lucide-react';
56

67
import { useAppStore } from '@/store';
@@ -21,6 +22,7 @@ export const ActionsDropdown = () => {
2122
const [showStorageUsageDialog, setShowStorageUsageDialog] = useState(false);
2223

2324
const selectedInstrument = useAppStore((store) => store.selectedInstrument);
25+
const { t } = useTranslation();
2426

2527
return (
2628
<React.Fragment>
@@ -43,22 +45,22 @@ export const ActionsDropdown = () => {
4345
</DropdownMenu.Item>
4446
<DropdownMenu.Item asChild onSelect={() => setShowLoginDialog(true)}>
4547
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
46-
Login
48+
{t({ en: 'Login', fr: 'Se connecter' })}
4749
</button>
4850
</DropdownMenu.Item>
4951
<DropdownMenu.Item asChild onSelect={() => setShowUploadBundleDialog(true)}>
5052
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
51-
Upload Bundle
53+
{t({ en: 'Upload Bundle', fr: 'Téléverser le paquet' })}
5254
</button>
5355
</DropdownMenu.Item>
5456
<DropdownMenu.Item asChild onSelect={() => setShowUserSettingsDialog(true)}>
5557
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
56-
User Settings
58+
{t({ en: 'User Settings', fr: 'Paramètres utilisateur' })}
5759
</button>
5860
</DropdownMenu.Item>
5961
<DropdownMenu.Item asChild onSelect={() => setShowStorageUsageDialog(true)}>
6062
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
61-
Storage Usage
63+
{t({ en: 'Storage Usage', fr: 'Utilisation du stockage' })}
6264
</button>
6365
</DropdownMenu.Item>
6466
<DropdownMenu.Separator />
@@ -68,15 +70,15 @@ export const ActionsDropdown = () => {
6870
disabled={selectedInstrument.category !== 'Saved'}
6971
type="button"
7072
>
71-
Delete Instrument
73+
{t({ en: 'Delete Instrument', fr: "Supprimer l'instrument" })}
7274
</button>
7375
</DropdownMenu.Item>
7476
<DropdownMenu.Item asChild onSelect={() => setShowRestoreDefaultsDialog(true)}>
7577
<button
7678
className="w-full cursor-pointer text-red-600 disabled:cursor-not-allowed disabled:opacity-50 dark:text-red-400"
7779
type="button"
7880
>
79-
Restore Defaults
81+
{t({ en: 'Restore Defaults', fr: 'Restaurer les paramètres par défaut' })}
8082
</button>
8183
</DropdownMenu.Item>
8284
</DropdownMenu.Content>

apps/playground/src/components/Header/ActionsDropdown/DeleteInstrumentDialog.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Button, Dialog } from '@douglasneuroinformatics/libui/components';
2-
import { useNotificationsStore } from '@douglasneuroinformatics/libui/hooks';
2+
import { useNotificationsStore, useTranslation } from '@douglasneuroinformatics/libui/hooks';
33

44
import { useAppStore } from '@/store';
55

@@ -12,27 +12,36 @@ export const DeleteInstrumentDialog = ({ isOpen, setIsOpen }: DeleteInstrumentDi
1212
const addNotification = useNotificationsStore((store) => store.addNotification);
1313
const removeInstrument = useAppStore((store) => store.removeInstrument);
1414
const selectedInstrument = useAppStore((store) => store.selectedInstrument);
15+
const { t } = useTranslation();
1516

1617
return (
1718
<Dialog open={isOpen} onOpenChange={setIsOpen}>
1819
<Dialog.Content>
1920
<Dialog.Header>
20-
<Dialog.Title>Are you absolutely sure?</Dialog.Title>
21-
<Dialog.Description>This instrument will be deleted from local storage.</Dialog.Description>
21+
<Dialog.Title>{t({ en: 'Are you absolutely sure?', fr: 'Êtes-vous absolument sûr ?' })}</Dialog.Title>
22+
<Dialog.Description>
23+
{t({
24+
en: 'This instrument will be deleted from local storage.',
25+
fr: 'Cet instrument sera supprimé du stockage local.'
26+
})}
27+
</Dialog.Description>
2228
</Dialog.Header>
2329
<Dialog.Footer>
2430
<Button
2531
variant="danger"
2632
onClick={() => {
2733
removeInstrument(selectedInstrument.id);
28-
addNotification({ message: 'This instrument has been deleted', type: 'success' });
34+
addNotification({
35+
message: t({ en: 'This instrument has been deleted', fr: 'Cet instrument a été supprimé' }),
36+
type: 'success'
37+
});
2938
setIsOpen(false);
3039
}}
3140
>
32-
Delete
41+
{t({ en: 'Delete', fr: 'Supprimer' })}
3342
</Button>
3443
<Button type="button" variant="outline" onClick={() => setIsOpen(false)}>
35-
Cancel
44+
{t({ en: 'Cancel', fr: 'Annuler' })}
3645
</Button>
3746
</Dialog.Footer>
3847
</Dialog.Content>

0 commit comments

Comments
 (0)