feat: Added support for i18n (#2439)
* Added translation for various react components in OHIF-v3 * Added a test language for debugging- test-LNG * Added copy for test language to not get overwritten
This commit is contained in:
parent
fdb6ca41d6
commit
1bf651e763
@ -10,6 +10,10 @@ import {
|
|||||||
useModal,
|
useModal,
|
||||||
} from '@ohif/ui';
|
} from '@ohif/ui';
|
||||||
|
|
||||||
|
import i18n from '@ohif/i18n';
|
||||||
|
|
||||||
|
const { availableLanguages, defaultLanguage, currentLanguage } = i18n;
|
||||||
|
|
||||||
import { useAppConfig } from '@state';
|
import { useAppConfig } from '@state';
|
||||||
|
|
||||||
function Toolbar({ servicesManager }) {
|
function Toolbar({ servicesManager }) {
|
||||||
@ -108,8 +112,12 @@ function ViewerLayout({
|
|||||||
hotkeyDefaults
|
hotkeyDefaults
|
||||||
),
|
),
|
||||||
hotkeyDefinitions,
|
hotkeyDefinitions,
|
||||||
|
currentLanguage: currentLanguage(),
|
||||||
|
availableLanguages,
|
||||||
|
defaultLanguage,
|
||||||
onCancel: hide,
|
onCancel: hide,
|
||||||
onSubmit: ({ hotkeyDefinitions }) => {
|
onSubmit: ({ hotkeyDefinitions, language }) => {
|
||||||
|
i18n.changeLanguage(language.value);
|
||||||
hotkeysManager.setHotkeys(hotkeyDefinitions);
|
hotkeysManager.setHotkeys(hotkeyDefinitions);
|
||||||
hide();
|
hide();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, ButtonGroup, Icon, IconButton } from '@ohif/ui';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Button, ButtonGroup } from '@ohif/ui';
|
||||||
|
|
||||||
function ActionButtons({ onExportClick, onCreateReportClick }) {
|
function ActionButtons({ onExportClick, onCreateReportClick }) {
|
||||||
|
const { t } = useTranslation('MeasurementTable');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ButtonGroup color="black" size="inherit">
|
<ButtonGroup color="black" size="inherit">
|
||||||
<Button className="text-base px-2 py-2" onClick={onExportClick}>
|
<Button className="text-base px-2 py-2" onClick={onExportClick}>
|
||||||
Export
|
{t('Export')}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
@ -17,7 +21,7 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
|
|||||||
color="black"
|
color="black"
|
||||||
onClick={onCreateReportClick}
|
onClick={onCreateReportClick}
|
||||||
>
|
>
|
||||||
Create Report
|
{t('Create Report')}
|
||||||
</Button>
|
</Button>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,18 +15,20 @@ function timeout(delay) {
|
|||||||
* Tests
|
* Tests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const threshold = 2400
|
||||||
|
|
||||||
describe('Queue', () => {
|
describe('Queue', () => {
|
||||||
it('should bind functions to the queue', async () => {
|
it('should bind functions to the queue', async () => {
|
||||||
const queue = new Queue(2);
|
const queue = new Queue(2);
|
||||||
const mockedTimeout = jest.fn(timeout);
|
const mockedTimeout = jest.fn(timeout);
|
||||||
const timer = queue.bind(mockedTimeout);
|
const timer = queue.bind(mockedTimeout);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
timer(1200).then(now => {
|
timer(threshold).then(now => {
|
||||||
const elapsed = now - start;
|
const elapsed = now - start;
|
||||||
expect(elapsed >= 1200 && elapsed < 2400).toBe(true);
|
expect(elapsed >= threshold && elapsed < 2 * threshold).toBe(true);
|
||||||
});
|
});
|
||||||
const end = await timer(1200);
|
const end = await timer(threshold);
|
||||||
expect(end - start > 2400).toBe(true);
|
expect(end - start > 2 * threshold).toBe(true);
|
||||||
expect(mockedTimeout).toBeCalledTimes(2);
|
expect(mockedTimeout).toBeCalledTimes(2);
|
||||||
});
|
});
|
||||||
it('should prevent task execution when queue limit is reached', async () => {
|
it('should prevent task execution when queue limit is reached', async () => {
|
||||||
@ -34,15 +36,15 @@ describe('Queue', () => {
|
|||||||
const mockedTimeout = jest.fn(timeout);
|
const mockedTimeout = jest.fn(timeout);
|
||||||
const timer = queue.bind(mockedTimeout);
|
const timer = queue.bind(mockedTimeout);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const promise = timer(1200).then(time => time - start);
|
const promise = timer(threshold).then(time => time - start);
|
||||||
try {
|
try {
|
||||||
await timer(1200);
|
await timer(threshold);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(Date.now() - start < 1200).toBe(true);
|
expect(Date.now() - start < threshold).toBe(true);
|
||||||
expect(e.message).toBe('Queue limit reached');
|
expect(e.message).toBe('Queue limit reached');
|
||||||
}
|
}
|
||||||
const elapsed = await promise;
|
const elapsed = await promise;
|
||||||
expect(elapsed >= 1200 && elapsed < 2400).toBe(true);
|
expect(elapsed >= threshold && elapsed < 2 * threshold).toBe(true);
|
||||||
expect(mockedTimeout).toBeCalledTimes(1);
|
expect(mockedTimeout).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
it('should safely bind tasks to the queue', async () => {
|
it('should safely bind tasks to the queue', async () => {
|
||||||
@ -51,16 +53,16 @@ describe('Queue', () => {
|
|||||||
const mockedTimeout = jest.fn(timeout);
|
const mockedTimeout = jest.fn(timeout);
|
||||||
const timer = queue.bindSafe(mockedTimeout, mockedErrorHandler);
|
const timer = queue.bindSafe(mockedTimeout, mockedErrorHandler);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const promise = timer(1200).then(time => time - start);
|
const promise = timer(threshold).then(time => time - start);
|
||||||
await timer(1200);
|
await timer(threshold);
|
||||||
expect(Date.now() - start < 1200).toBe(true);
|
expect(Date.now() - start < threshold).toBe(true);
|
||||||
expect(mockedErrorHandler).toBeCalledTimes(1);
|
expect(mockedErrorHandler).toBeCalledTimes(1);
|
||||||
expect(mockedErrorHandler).nthCalledWith(
|
expect(mockedErrorHandler).nthCalledWith(
|
||||||
1,
|
1,
|
||||||
expect.objectContaining({ message: 'Queue limit reached' })
|
expect.objectContaining({ message: 'Queue limit reached' })
|
||||||
);
|
);
|
||||||
const elapsed = await promise;
|
const elapsed = await promise;
|
||||||
expect(elapsed >= 1200 && elapsed < 2400).toBe(true);
|
expect(elapsed >= threshold && elapsed < 2 * threshold).toBe(true);
|
||||||
expect(mockedTimeout).toBeCalledTimes(1);
|
expect(mockedTimeout).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
cp -r src/locales/test-LNG src/temp
|
||||||
rm -rf src/locales/
|
rm -rf src/locales/
|
||||||
mkdir -p src/locales/
|
mkdir -p src/locales/
|
||||||
cd src/locales/
|
cd src/locales/
|
||||||
npx locize --config-path ../../.locize download --ver latest
|
npx locize --config-path ../../.locize download --ver latest
|
||||||
cd ../../
|
cd ../../
|
||||||
node ./writeLocaleIndexFiles.js
|
node ./writeLocaleIndexFiles.js
|
||||||
|
cp -r src/temp src/locales/test-LNG
|
||||||
|
|||||||
@ -5,5 +5,7 @@
|
|||||||
"MAX": "Máximo",
|
"MAX": "Máximo",
|
||||||
"NonTargets": "No objetivos",
|
"NonTargets": "No objetivos",
|
||||||
"Relabel": "Re-etiquetar",
|
"Relabel": "Re-etiquetar",
|
||||||
"Targets": "Objetivos"
|
"Targets": "Objetivos",
|
||||||
|
"Export": "Exportar",
|
||||||
|
"Create Report": "Crear reporte"
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ import nl from './nl/';
|
|||||||
import pt_BR from './pt-BR/';
|
import pt_BR from './pt-BR/';
|
||||||
import vi from './vi/';
|
import vi from './vi/';
|
||||||
import zh from './zh/';
|
import zh from './zh/';
|
||||||
|
import test_lng from './test-LNG/';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...ar,
|
...ar,
|
||||||
@ -18,4 +19,5 @@ export default {
|
|||||||
...pt_BR,
|
...pt_BR,
|
||||||
...vi,
|
...vi,
|
||||||
...zh,
|
...zh,
|
||||||
|
...test_lng
|
||||||
};
|
};
|
||||||
|
|||||||
4
platform/i18n/src/locales/pt-BR/MeasurementTable.json
Normal file
4
platform/i18n/src/locales/pt-BR/MeasurementTable.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Export": "Exportar",
|
||||||
|
"Create Report": "Criar relatório"
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import Common from './Common.json';
|
|||||||
import DatePicker from './DatePicker.json';
|
import DatePicker from './DatePicker.json';
|
||||||
import Header from './Header.json';
|
import Header from './Header.json';
|
||||||
import UserPreferencesModal from './UserPreferencesModal.json';
|
import UserPreferencesModal from './UserPreferencesModal.json';
|
||||||
|
import MeasurementTable from './MeasurementTable.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
@ -15,5 +16,6 @@ export default {
|
|||||||
DatePicker,
|
DatePicker,
|
||||||
Header,
|
Header,
|
||||||
UserPreferencesModal,
|
UserPreferencesModal,
|
||||||
|
MeasurementTable,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
14
platform/i18n/src/locales/test-LNG/AboutModal.json
Normal file
14
platform/i18n/src/locales/test-LNG/AboutModal.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Browser": "Browser",
|
||||||
|
"Build Number": "Build Number",
|
||||||
|
"Latest Master Commits": "Latest Master Commits",
|
||||||
|
"More details": "More details",
|
||||||
|
"Name": "Name",
|
||||||
|
"OHIF Viewer - About": "OHIF Viewer - About",
|
||||||
|
"OS": "OS",
|
||||||
|
"Report an issue": "Report an issue",
|
||||||
|
"Repository URL": "Repository URL",
|
||||||
|
"Value": "Value",
|
||||||
|
"Version Information": "Version Information",
|
||||||
|
"Visit the forum": "Visit the forum"
|
||||||
|
}
|
||||||
54
platform/i18n/src/locales/test-LNG/Buttons.json
Normal file
54
platform/i18n/src/locales/test-LNG/Buttons.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"Acquired": "Test Acquired",
|
||||||
|
"Angle": "Test Angle",
|
||||||
|
"Axial": "Test Axial",
|
||||||
|
"Bidirectional": "Test Bidirectional",
|
||||||
|
"Brush": "Test Brush",
|
||||||
|
"CINE": "Test CINE",
|
||||||
|
"Cancel": "Test Cancel",
|
||||||
|
"Circle": "Test Circle",
|
||||||
|
"Clear": "Test Clear",
|
||||||
|
"Coronal": "Test Coronal",
|
||||||
|
"Crosshairs": "Test Crosshairs",
|
||||||
|
"Download": "Test Download",
|
||||||
|
"Ellipse": "Test Ellipse",
|
||||||
|
"Elliptical": "Test Elliptical",
|
||||||
|
"Flip H": "Test Flip H",
|
||||||
|
"Flip V": "Test Flip V",
|
||||||
|
"Freehand": "Test Freehand",
|
||||||
|
"Invert": "Test Invert",
|
||||||
|
"Layout": "Test $t(Common:Layout)",
|
||||||
|
"Length": "Test Length",
|
||||||
|
"Levels": "Test Levels",
|
||||||
|
"Magnify": "Test Magnify",
|
||||||
|
"Manual": "Test Manual",
|
||||||
|
"Measurements": "Test Measurements",
|
||||||
|
"More": "Test $t(Common:More)",
|
||||||
|
"Next": "Test $t(Common:Next)",
|
||||||
|
"Pan": "Test Pan",
|
||||||
|
"Play": "Test $t(Common:Play)",
|
||||||
|
"Previous": "Test $t(Common:Previous)",
|
||||||
|
"Probe": "Test Probe",
|
||||||
|
"ROI Window": "Test ROI Window",
|
||||||
|
"Rectangle": "Test Rectangle",
|
||||||
|
"Reset": "Test $t(Common:Reset)",
|
||||||
|
"Reset to Defaults": "Test $t(Common:Reset) to Defaults",
|
||||||
|
"Rotate Right": "Test Rotate Right",
|
||||||
|
"Sagittal": "Test Sagittal",
|
||||||
|
"Save": "Test Save",
|
||||||
|
"Stack Scroll": "Test Stack Scroll",
|
||||||
|
"Stop": "Test $t(Common:Stop)",
|
||||||
|
"Themes": "Test Themes",
|
||||||
|
"Zoom": "Test Zoom",
|
||||||
|
"Grid Layout": "Test Grid Layout",
|
||||||
|
"W/L Presets": "Test W/L Presets",
|
||||||
|
"More Measure Tools": "Test More Measure Tools",
|
||||||
|
"More Tools": "Test More Tools",
|
||||||
|
"Capture": "Test Capture",
|
||||||
|
"Annotation": "Test Annotation",
|
||||||
|
"Soft Tissue": "Test Soft Tissue",
|
||||||
|
"Lung": "Test Lung",
|
||||||
|
"Liver": "Test Liver",
|
||||||
|
"Bone": "Test Bone",
|
||||||
|
"Cine": "Test Cine"
|
||||||
|
}
|
||||||
8
platform/i18n/src/locales/test-LNG/CineDialog.json
Normal file
8
platform/i18n/src/locales/test-LNG/CineDialog.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Next image": "$t(Common:Next) $t(Common:Image)",
|
||||||
|
"Play / Stop": "$t(Common:Play) / $t(Common:Stop)",
|
||||||
|
"Previous image": "$t(Common:Previous) $t(Common:Image)",
|
||||||
|
"Skip to first image": "Skip to first $t(Common:Image)",
|
||||||
|
"Skip to last image": "Skip to last $t(Common:Image)",
|
||||||
|
"fps": "fps"
|
||||||
|
}
|
||||||
16
platform/i18n/src/locales/test-LNG/Common.json
Normal file
16
platform/i18n/src/locales/test-LNG/Common.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"Close": "Test Close",
|
||||||
|
"Image": "Test Image",
|
||||||
|
"Layout": "Test Layout",
|
||||||
|
"Measurements": "Test Measurements",
|
||||||
|
"More": "Test More",
|
||||||
|
"Next": "Test Next",
|
||||||
|
"Play": "Test Play",
|
||||||
|
"Previous": "Test Previous",
|
||||||
|
"Reset": "Test Reset",
|
||||||
|
"RowsPerPage": "Test rows per page",
|
||||||
|
"Series": "Test Series",
|
||||||
|
"Show": "Test Show",
|
||||||
|
"Stop": "Test Stop",
|
||||||
|
"StudyDate": "Test Study Date"
|
||||||
|
}
|
||||||
5
platform/i18n/src/locales/test-LNG/DatePicker.json
Normal file
5
platform/i18n/src/locales/test-LNG/DatePicker.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Clear dates": "Clear dates",
|
||||||
|
"End Date": "End Date",
|
||||||
|
"Start Date": "Start Date"
|
||||||
|
}
|
||||||
6
platform/i18n/src/locales/test-LNG/Header.json
Normal file
6
platform/i18n/src/locales/test-LNG/Header.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"About": "Test About",
|
||||||
|
"INVESTIGATIONAL USE ONLY": "Test Investigational",
|
||||||
|
"Options": "Test Options",
|
||||||
|
"Preferences": "Test Preferences"
|
||||||
|
}
|
||||||
12
platform/i18n/src/locales/test-LNG/MeasurementTable.json
Normal file
12
platform/i18n/src/locales/test-LNG/MeasurementTable.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"Measurements": "Test Measurements",
|
||||||
|
"No tracked measurements": "Test No tracked measurements",
|
||||||
|
"Create Report": "Test Create Report",
|
||||||
|
"Export": "Test Export",
|
||||||
|
"Delete": "Delete",
|
||||||
|
"Description": "Description",
|
||||||
|
"MAX": "MAX",
|
||||||
|
"NonTargets": "NonTargets",
|
||||||
|
"Relabel": "Relabel",
|
||||||
|
"Targets": "Targets"
|
||||||
|
}
|
||||||
13
platform/i18n/src/locales/test-LNG/Modals.json
Normal file
13
platform/i18n/src/locales/test-LNG/Modals.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Download High Quality Image": "Test Download High Quality Image",
|
||||||
|
"Cancel": "Test Cancel",
|
||||||
|
"Download": "Test Download",
|
||||||
|
"File Name": "Test File Name",
|
||||||
|
"Active viewport has no displayed image": "Test Active viewport has no displayed image",
|
||||||
|
"Image preview": "Test Image preview",
|
||||||
|
"Show Annotations": "Test Show Annotations",
|
||||||
|
"File Type": "Test File Type",
|
||||||
|
"Image height (px)": "Test Image height (px)",
|
||||||
|
"Image width (px)": "Test Image width (px)",
|
||||||
|
"Please specify the dimensions, filename, and desired type for the output image.": "Test Please specify the dimensions, filename, and desired type for the output image."
|
||||||
|
}
|
||||||
3
platform/i18n/src/locales/test-LNG/Modes.json
Normal file
3
platform/i18n/src/locales/test-LNG/Modes.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"Basic Viewer": "Test Basic Viewer"
|
||||||
|
}
|
||||||
8
platform/i18n/src/locales/test-LNG/PatientInfo.json
Normal file
8
platform/i18n/src/locales/test-LNG/PatientInfo.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Age": "Test Age",
|
||||||
|
"Sex": "Test Sex",
|
||||||
|
"MRN": "Test MRN",
|
||||||
|
"Thickness": "Test Thickness",
|
||||||
|
"Spacing": "Test Spacing",
|
||||||
|
"Scanner": "Test Scanner"
|
||||||
|
}
|
||||||
4
platform/i18n/src/locales/test-LNG/SidePanel.json
Normal file
4
platform/i18n/src/locales/test-LNG/SidePanel.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Studies": "Test Studies",
|
||||||
|
"Measurements": "Test Measurements"
|
||||||
|
}
|
||||||
5
platform/i18n/src/locales/test-LNG/StudyBrowser.json
Normal file
5
platform/i18n/src/locales/test-LNG/StudyBrowser.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Primary": "Test Primary",
|
||||||
|
"Recent": "Test Recent",
|
||||||
|
"All": "Test All"
|
||||||
|
}
|
||||||
21
platform/i18n/src/locales/test-LNG/StudyList.json
Normal file
21
platform/i18n/src/locales/test-LNG/StudyList.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"AccessionNumber": "Test Accession #",
|
||||||
|
"Accession": "Test Accession",
|
||||||
|
"Next >": "Test Next >",
|
||||||
|
"< Previous": "Test < Previous",
|
||||||
|
"Results per page": "Test Results per page",
|
||||||
|
"Empty": "Test Empty",
|
||||||
|
"MRN": "Test MRN",
|
||||||
|
"Modality": "Test Modality",
|
||||||
|
"PatientName": "Test PatientName",
|
||||||
|
"Patient Name": "Test Patient Name",
|
||||||
|
"Description": "Test Description",
|
||||||
|
"StudyDate": "Test Study Date",
|
||||||
|
"Study date": "Test Study Date",
|
||||||
|
"Studies": "Test Studies",
|
||||||
|
"Series": "Test Series",
|
||||||
|
"Instances": "Test Instances",
|
||||||
|
"Study List": "Test Study List",
|
||||||
|
"Study list": "Test Study list",
|
||||||
|
"Filter list to 100 studies or less to enable sorting": "Test Filter list to 100 studies or less to enable sorting"
|
||||||
|
}
|
||||||
3
platform/i18n/src/locales/test-LNG/ToolTip.json
Normal file
3
platform/i18n/src/locales/test-LNG/ToolTip.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"Zoom": "toolTip1"
|
||||||
|
}
|
||||||
14
platform/i18n/src/locales/test-LNG/UserPreferencesModal.json
Normal file
14
platform/i18n/src/locales/test-LNG/UserPreferencesModal.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Cancel": "$t(Buttons:Cancel)",
|
||||||
|
"No hotkeys found": "No hotkeys are configured for this application. Hotkeys can be configured in the application's app-config.js file.",
|
||||||
|
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
|
||||||
|
"ResetDefaultMessage": "Preferences successfully reset to default. <br /> You must <strong>Save</strong> to perform this action.",
|
||||||
|
"Save": "$t(Buttons:Save)",
|
||||||
|
"SaveMessage": "Test Preferences saved",
|
||||||
|
"User Preferences": "Test User Preferences",
|
||||||
|
"Function": "Test function",
|
||||||
|
"Shortcut": "Test shortcut",
|
||||||
|
"Language": "Test language",
|
||||||
|
"Hotkeys": "Test hotkeys",
|
||||||
|
"General": "Test general"
|
||||||
|
}
|
||||||
14
platform/i18n/src/locales/test-LNG/ViewportDownloadForm.json
Normal file
14
platform/i18n/src/locales/test-LNG/ViewportDownloadForm.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"emptyFilenameError": "The file name cannot be empty.",
|
||||||
|
"fileType": "File Type",
|
||||||
|
"filename": "File Name",
|
||||||
|
"formTitle": "Please specify the dimensions, filename, and desired type for the output image.",
|
||||||
|
"imageHeight": "Image height (px)",
|
||||||
|
"imagePreview": "Image Preview",
|
||||||
|
"imageWidth": "Image width (px)",
|
||||||
|
"keepAspectRatio": "Keep aspect ratio",
|
||||||
|
"loadingPreview": "Loading Image Preview...",
|
||||||
|
"minHeightError": "The minimum valid height is 100px.",
|
||||||
|
"minWidthError": "The minimum valid width is 100px.",
|
||||||
|
"showAnnotations": "Show Annotations"
|
||||||
|
}
|
||||||
37
platform/i18n/src/locales/test-LNG/index.js
Normal file
37
platform/i18n/src/locales/test-LNG/index.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import AboutModal from './AboutModal.json';
|
||||||
|
import Buttons from './Buttons.json';
|
||||||
|
import CineDialog from './CineDialog.json';
|
||||||
|
import Common from './Common.json';
|
||||||
|
import DatePicker from './DatePicker.json';
|
||||||
|
import Header from './Header.json';
|
||||||
|
import MeasurementTable from './MeasurementTable.json';
|
||||||
|
import StudyList from './StudyList.json';
|
||||||
|
import UserPreferencesModal from './UserPreferencesModal.json';
|
||||||
|
import ViewportDownloadForm from './ViewportDownloadForm.json';
|
||||||
|
import ToolTip from './ToolTip.json';
|
||||||
|
import StudyBrowser from './StudyBrowser.json';
|
||||||
|
import SidePanel from './SidePanel.json';
|
||||||
|
import PatientInfo from './PatientInfo.json';
|
||||||
|
import Modes from './Modes.json';
|
||||||
|
import Modals from './Modals.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'test-LNG': {
|
||||||
|
AboutModal,
|
||||||
|
Buttons,
|
||||||
|
CineDialog,
|
||||||
|
Common,
|
||||||
|
DatePicker,
|
||||||
|
Header,
|
||||||
|
MeasurementTable,
|
||||||
|
StudyList,
|
||||||
|
UserPreferencesModal,
|
||||||
|
ViewportDownloadForm,
|
||||||
|
ToolTip,
|
||||||
|
StudyBrowser,
|
||||||
|
PatientInfo,
|
||||||
|
Modes,
|
||||||
|
SidePanel,
|
||||||
|
Modals
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -54,6 +54,7 @@ const languagesMap = {
|
|||||||
zh: 'Chinese',
|
zh: 'Chinese',
|
||||||
'zh-CN': 'Chinese (China)',
|
'zh-CN': 'Chinese (China)',
|
||||||
'zh-TW': 'Chinese (Taiwan)',
|
'zh-TW': 'Chinese (Taiwan)',
|
||||||
|
'test-LNG': 'Test Language',
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLanguageLabel = (language) => {
|
const getLanguageLabel = (language) => {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import { NavBar, Svg, Icon, IconButton, Dropdown } from '@ohif/ui';
|
import { NavBar, Svg, Icon, IconButton, Dropdown } from '@ohif/ui';
|
||||||
|
|
||||||
function Header({ children, menuOptions, isReturnEnabled, isSticky, WhiteLabeling }) {
|
function Header({ children, menuOptions, isReturnEnabled, isSticky, WhiteLabeling }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation('Header');
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
// TODO: this should be passed in as a prop instead and the react-router-dom
|
// TODO: this should be passed in as a prop instead and the react-router-dom
|
||||||
@ -39,7 +39,7 @@ function Header({ children, menuOptions, isReturnEnabled, isSticky, WhiteLabelin
|
|||||||
<div className="flex items-center">{children}</div>
|
<div className="flex items-center">{children}</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="mr-3 text-lg text-common-light">
|
<span className="mr-3 text-lg text-common-light">
|
||||||
{t('Header:INVESTIGATIONAL USE ONLY')}
|
{t('INVESTIGATIONAL USE ONLY')}
|
||||||
</span>
|
</span>
|
||||||
<Dropdown id="options" showDropdownIcon={false} list={menuOptions}>
|
<Dropdown id="options" showDropdownIcon={false} list={menuOptions}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@ -2,12 +2,16 @@ import React, { useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { HotkeyField, Typography } from '@ohif/ui';
|
import { HotkeyField, Typography } from '@ohif/ui';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Move these configs and utils to core? */
|
/* TODO: Move these configs and utils to core? */
|
||||||
import { MODIFIER_KEYS } from './hotkeysConfig';
|
import { MODIFIER_KEYS } from './hotkeysConfig';
|
||||||
import { validate, splitHotkeyDefinitionsAndCreateTuples } from './utils';
|
import { validate, splitHotkeyDefinitionsAndCreateTuples } from './utils';
|
||||||
|
|
||||||
const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErrors, onChange }) => {
|
const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErrors, onChange }) => {
|
||||||
|
const { t } = useTranslation('UserPreferencesModal');
|
||||||
|
|
||||||
const visibleHotkeys = Object.keys(hotkeyDefinitions)
|
const visibleHotkeys = Object.keys(hotkeyDefinitions)
|
||||||
.filter(key => hotkeyDefinitions[key].isEditable)
|
.filter(key => hotkeyDefinitions[key].isEditable)
|
||||||
.reduce((obj, key) => {
|
.reduce((obj, key) => {
|
||||||
@ -57,7 +61,7 @@ const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErr
|
|||||||
variant='subtitle'
|
variant='subtitle'
|
||||||
className={classNames('pr-6 w-full text-right text-primary-light', !isFirst && 'hidden')}
|
className={classNames('pr-6 w-full text-right text-primary-light', !isFirst && 'hidden')}
|
||||||
>
|
>
|
||||||
Function
|
{t('Function')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant='subtitle'
|
variant='subtitle'
|
||||||
@ -70,7 +74,7 @@ const HotkeysPreferences = ({ disabled, hotkeyDefinitions, errors: controlledErr
|
|||||||
variant='subtitle'
|
variant='subtitle'
|
||||||
className={classNames('pr-6 pl-0 text-left text-primary-light', !isFirst && 'hidden')}
|
className={classNames('pr-6 pl-0 text-left text-primary-light', !isFirst && 'hidden')}
|
||||||
>
|
>
|
||||||
Shortcut
|
{t('Shortcut')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className={classNames('flex flex-col w-32', isFirst && 'mt-5')}>
|
<div className={classNames('flex flex-col w-32', isFirst && 'mt-5')}>
|
||||||
<HotkeyField
|
<HotkeyField
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Icon } from '../';
|
import { Icon } from '../';
|
||||||
|
|
||||||
@ -21,6 +22,8 @@ const InputLabelWrapper = ({
|
|||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("StudyList")
|
||||||
|
|
||||||
const onClickHandler = e => {
|
const onClickHandler = e => {
|
||||||
if (!isSortable) {
|
if (!isSortable) {
|
||||||
return;
|
return;
|
||||||
@ -38,7 +41,7 @@ const InputLabelWrapper = ({
|
|||||||
onKeyDown={onClickHandler}
|
onKeyDown={onClickHandler}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
>
|
>
|
||||||
{label}
|
{t(label)}
|
||||||
{isSortable && (
|
{isSortable && (
|
||||||
<Icon
|
<Icon
|
||||||
name={sortIconMap[sortDirection]}
|
name={sortIconMap[sortDirection]}
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import MeasurementItem from './MeasurementItem';
|
import MeasurementItem from './MeasurementItem';
|
||||||
|
|
||||||
const MeasurementTable = ({ data, title, amount, onClick, onEdit }) => {
|
const MeasurementTable = ({ data, title, amount, onClick, onEdit }) => {
|
||||||
|
const { t } = useTranslation("MeasurementTable")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between px-2 py-1 bg-secondary-main">
|
<div className="flex justify-between px-2 py-1 bg-secondary-main">
|
||||||
<span className="text-base font-bold tracking-widest text-white uppercase">
|
<span className="text-base font-bold tracking-widest text-white uppercase">
|
||||||
{title}
|
{t(title)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-base font-bold text-white">{amount}</span>
|
<span className="text-base font-bold text-white">{amount}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -31,7 +34,7 @@ const MeasurementTable = ({ data, title, amount, onClick, onEdit }) => {
|
|||||||
<div className="w-6 py-1 text-base text-center transition duration-300 bg-primary-dark text-primary-light group-hover:bg-secondary-main"></div>
|
<div className="w-6 py-1 text-base text-center transition duration-300 bg-primary-dark text-primary-light group-hover:bg-secondary-main"></div>
|
||||||
<div className="flex items-center justify-between flex-1 px-2 py-4">
|
<div className="flex items-center justify-between flex-1 px-2 py-4">
|
||||||
<span className="flex items-center flex-1 mb-1 text-base text-primary-light">
|
<span className="flex items-center flex-1 mb-1 text-base text-primary-light">
|
||||||
No tracked measurements
|
{t('No tracked measurements')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Button, Icon } from '../';
|
import { Button, Icon } from '../';
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ const SidePanel = ({
|
|||||||
defaultComponentOpen,
|
defaultComponentOpen,
|
||||||
childComponents,
|
childComponents,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('SidePanel');
|
||||||
|
|
||||||
const [componentOpen, setComponentOpen] = useState(defaultComponentOpen);
|
const [componentOpen, setComponentOpen] = useState(defaultComponentOpen);
|
||||||
|
|
||||||
const openStatus = componentOpen ? 'open' : 'closed';
|
const openStatus = componentOpen ? 'open' : 'closed';
|
||||||
@ -101,7 +104,7 @@ const SidePanel = ({
|
|||||||
className="text-primary-active"
|
className="text-primary-active"
|
||||||
/>
|
/>
|
||||||
<span className="mt-2 text-white text-xs">
|
<span className="mt-2 text-white text-xs">
|
||||||
{childComponent.iconLabel}
|
{t(childComponent.iconLabel)}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@ -139,7 +142,7 @@ const SidePanel = ({
|
|||||||
style={{ ...position[side] }}
|
style={{ ...position[side] }}
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 text-primary-active">
|
<span className="flex-1 text-primary-active">
|
||||||
{childComponent.label}
|
{t(childComponent.label)}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import OutsideClickHandler from 'react-outside-click-handler';
|
import OutsideClickHandler from 'react-outside-click-handler';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Icon, Tooltip, ListMenu } from '@ohif/ui';
|
import { Icon, Tooltip, ListMenu } from '@ohif/ui';
|
||||||
|
|
||||||
@ -83,6 +84,8 @@ const SplitButton = ({
|
|||||||
renderer,
|
renderer,
|
||||||
onInteraction,
|
onInteraction,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('Buttons');
|
||||||
|
|
||||||
const { primaryToolId, toggles } = bState;
|
const { primaryToolId, toggles } = bState;
|
||||||
/* Bubbles up individual item clicks */
|
/* Bubbles up individual item clicks */
|
||||||
const getSplitButtonItems = items =>
|
const getSplitButtonItems = items =>
|
||||||
@ -221,14 +224,14 @@ const SplitButton = ({
|
|||||||
className={classes.Content({ ...state })}
|
className={classes.Content({ ...state })}
|
||||||
data-cy={`${groupId}-list-menu`}
|
data-cy={`${groupId}-list-menu`}
|
||||||
>
|
>
|
||||||
<ListMenu items={state.items} renderer={renderer} />
|
<ListMenu items={state.items} renderer={(args) => renderer({ ...args, t })} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</OutsideClickHandler>
|
</OutsideClickHandler>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DefaultListItemRenderer = ({ icon, label, isActive }) => (
|
const DefaultListItemRenderer = ({ icon, label, isActive, t }) => (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex flex-row items-center p-3 h-8 w-full hover:bg-primary-dark',
|
'flex flex-row items-center p-3 h-8 w-full hover:bg-primary-dark',
|
||||||
@ -239,7 +242,7 @@ const DefaultListItemRenderer = ({ icon, label, isActive }) => (
|
|||||||
<Icon name={icon} className="w-5 h-5 text-common-bright" />
|
<Icon name={icon} className="w-5 h-5 text-common-bright" />
|
||||||
</span>
|
</span>
|
||||||
<span className="mr-5 text-base whitespace-pre text-common-bright">
|
<span className="mr-5 text-base whitespace-pre text-common-bright">
|
||||||
{label}
|
{t(label)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { ButtonGroup, Button, StudyItem, ThumbnailList } from '../';
|
import { ButtonGroup, Button, StudyItem, ThumbnailList } from '../';
|
||||||
|
|
||||||
@ -29,6 +30,8 @@ const StudyBrowser = ({
|
|||||||
onClickUntrack,
|
onClickUntrack,
|
||||||
activeDisplaySetInstanceUID,
|
activeDisplaySetInstanceUID,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("StudyBrowser")
|
||||||
|
|
||||||
const getTabContent = () => {
|
const getTabContent = () => {
|
||||||
const tabData = tabs.find(tab => tab.name === activeTabName);
|
const tabData = tabs.find(tab => tab.name === activeTabName);
|
||||||
|
|
||||||
@ -96,7 +99,7 @@ const StudyBrowser = ({
|
|||||||
}}
|
}}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
>
|
>
|
||||||
{label}
|
{t(label)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Table, TableHead, TableBody, TableRow, TableCell } from '../';
|
import { Table, TableHead, TableBody, TableRow, TableCell } from '../';
|
||||||
|
|
||||||
@ -8,6 +9,9 @@ const StudyListExpandedRow = ({
|
|||||||
seriesTableDataSource,
|
seriesTableDataSource,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('StudyList');
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full bg-black py-4 pl-12 pr-2">
|
<div className="w-full bg-black py-4 pl-12 pr-2">
|
||||||
<div className="block">{children}</div>
|
<div className="block">{children}</div>
|
||||||
@ -18,7 +22,7 @@ const StudyListExpandedRow = ({
|
|||||||
{Object.keys(seriesTableColumns).map(columnKey => {
|
{Object.keys(seriesTableColumns).map(columnKey => {
|
||||||
return (
|
return (
|
||||||
<TableCell key={columnKey}>
|
<TableCell key={columnKey}>
|
||||||
{seriesTableColumns[columnKey]}
|
{t(seriesTableColumns[columnKey])}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Button, Icon, Typography, InputGroup } from '../../components';
|
import { Button, Icon, Typography, InputGroup } from '../../components';
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ const StudyListFilter = ({
|
|||||||
isFiltering,
|
isFiltering,
|
||||||
numOfStudies,
|
numOfStudies,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("StudyList")
|
||||||
const { sortBy, sortDirection } = filterValues;
|
const { sortBy, sortDirection } = filterValues;
|
||||||
const filterSorting = { sortBy, sortDirection };
|
const filterSorting = { sortBy, sortDirection };
|
||||||
const setFilterSorting = sortingValues => {
|
const setFilterSorting = sortingValues => {
|
||||||
@ -29,7 +31,7 @@ const StudyListFilter = ({
|
|||||||
<div className="flex flex-row justify-between px-12 mb-5">
|
<div className="flex flex-row justify-between px-12 mb-5">
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<Typography variant="h4" className="mr-6 text-primary-light">
|
<Typography variant="h4" className="mr-6 text-primary-light">
|
||||||
Study list
|
{t('Study list')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
@ -42,7 +44,7 @@ const StudyListFilter = ({
|
|||||||
startIcon={<Icon name="cancel" />}
|
startIcon={<Icon name="cancel" />}
|
||||||
onClick={clearFilters}
|
onClick={clearFilters}
|
||||||
>
|
>
|
||||||
Clear filters
|
{t('Clear filters')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Typography variant="h4" className="mr-2" data-cy={"num-studies"}>
|
<Typography variant="h4" className="mr-2" data-cy={"num-studies"}>
|
||||||
@ -52,7 +54,7 @@ const StudyListFilter = ({
|
|||||||
variant="h6"
|
variant="h6"
|
||||||
className="self-end pb-1 text-common-light"
|
className="self-end pb-1 text-common-light"
|
||||||
>
|
>
|
||||||
Studies
|
{t('Studies')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +79,7 @@ const StudyListFilter = ({
|
|||||||
<div className="container m-auto">
|
<div className="container m-auto">
|
||||||
<div className="py-1 text-base text-center rounded-b bg-primary-main">
|
<div className="py-1 text-base text-center rounded-b bg-primary-main">
|
||||||
<p className="text-white">
|
<p className="text-white">
|
||||||
Filter list to 100 studies or less to enable sorting
|
{t('Filter list to 100 studies or less to enable sorting')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, ButtonGroup, Typography, Select } from '../';
|
import { Button, ButtonGroup, Typography, Select } from '../';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const StudyListPagination = ({
|
const StudyListPagination = ({
|
||||||
onChangePage,
|
onChangePage,
|
||||||
@ -8,6 +9,8 @@ const StudyListPagination = ({
|
|||||||
perPage,
|
perPage,
|
||||||
onChangePerPage,
|
onChangePerPage,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("StudyList")
|
||||||
|
|
||||||
const navigateToPage = page => {
|
const navigateToPage = page => {
|
||||||
const toPage = page < 1 ? 1 : page;
|
const toPage = page < 1 ? 1 : page;
|
||||||
onChangePage(toPage);
|
onChangePage(toPage);
|
||||||
@ -42,7 +45,7 @@ const StudyListPagination = ({
|
|||||||
onChange={onSelectedRange}
|
onChange={onSelectedRange}
|
||||||
/>
|
/>
|
||||||
<Typography className="text-base opacity-60">
|
<Typography className="text-base opacity-60">
|
||||||
Results per page
|
{t('Results per page')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
@ -64,14 +67,14 @@ const StudyListPagination = ({
|
|||||||
className="border-primary-main py-2 px-2 text-base"
|
className="border-primary-main py-2 px-2 text-base"
|
||||||
color="white"
|
color="white"
|
||||||
onClick={() => navigateToPage(currentPage - 1)}
|
onClick={() => navigateToPage(currentPage - 1)}
|
||||||
>{`< Previous`}</Button>
|
>{t(`< Previous`)}</Button>
|
||||||
<Button
|
<Button
|
||||||
size="initial"
|
size="initial"
|
||||||
className="border-primary-main py-2 px-4 text-base"
|
className="border-primary-main py-2 px-4 text-base"
|
||||||
color="white"
|
color="white"
|
||||||
onClick={() => navigateToPage(currentPage + 1)}
|
onClick={() => navigateToPage(currentPage + 1)}
|
||||||
>
|
>
|
||||||
{`Next >`}
|
{t(`Next >`)}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import './tooltip.css';
|
import './tooltip.css';
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ const Tooltip = ({
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
}) => {
|
}) => {
|
||||||
const [isActive, setIsActive] = useState(false);
|
const [isActive, setIsActive] = useState(false);
|
||||||
|
const { t } = useTranslation('Buttons');
|
||||||
|
|
||||||
const handleMouseOver = () => {
|
const handleMouseOver = () => {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
@ -72,7 +74,7 @@ const Tooltip = ({
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{content}
|
{typeof content === 'string' ? t(content) : content}
|
||||||
<svg
|
<svg
|
||||||
className="absolute h-4 text-primary-dark stroke-secondary-main"
|
className="absolute h-4 text-primary-dark stroke-secondary-main"
|
||||||
style={arrowPositionStyle[position]}
|
style={arrowPositionStyle[position]}
|
||||||
|
|||||||
@ -66,10 +66,10 @@ const UserPreferences = ({ availableLanguages, defaultLanguage, currentLanguage,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
<Section title="General">
|
<Section title={t('General')}>
|
||||||
<div className="flex flex-row justify-center items-center w-72">
|
<div className="flex flex-row justify-center items-center w-72">
|
||||||
<Typography variant="subtitle" className="mr-5 text-right h-full">
|
<Typography variant="subtitle" className="mr-5 text-right h-full">
|
||||||
Language
|
{t('Language')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Select
|
<Select
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
@ -79,7 +79,7 @@ const UserPreferences = ({ availableLanguages, defaultLanguage, currentLanguage,
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<Section title="Hotkeys">
|
<Section title={t('Hotkeys')}>
|
||||||
<HotkeysPreferences
|
<HotkeysPreferences
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
hotkeyDefinitions={state.hotkeyDefinitions}
|
hotkeyDefinitions={state.hotkeyDefinitions}
|
||||||
@ -135,7 +135,7 @@ UserPreferences.defaultProps = {
|
|||||||
onCancel: noop,
|
onCancel: noop,
|
||||||
onSubmit: noop,
|
onSubmit: noop,
|
||||||
onReset: noop,
|
onReset: noop,
|
||||||
disabled: true
|
disabled: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserPreferences;
|
export default UserPreferences;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Icon, ButtonGroup, Button, Tooltip, CinePlayer } from '../';
|
import { Icon, ButtonGroup, Button, Tooltip, CinePlayer } from '../';
|
||||||
import useOnClickOutside from '../../utils/useOnClickOutside';
|
import useOnClickOutside from '../../utils/useOnClickOutside';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const classes = {
|
const classes = {
|
||||||
infoHeader: 'text-base text-primary-light',
|
infoHeader: 'text-base text-primary-light',
|
||||||
@ -369,6 +370,8 @@ function PatientInfo({
|
|||||||
isOpen,
|
isOpen,
|
||||||
showPatientInfoRef,
|
showPatientInfoRef,
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation("PatientInfo")
|
||||||
|
|
||||||
while (patientAge.charAt(0) === '0') {
|
while (patientAge.charAt(0) === '0') {
|
||||||
patientAge = patientAge.substr(1);
|
patientAge = patientAge.substr(1);
|
||||||
}
|
}
|
||||||
@ -394,7 +397,7 @@ function PatientInfo({
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex pb-4 mt-4 mb-4 border-b border-secondary-main">
|
<div className="flex pb-4 mt-4 mb-4 border-b border-secondary-main">
|
||||||
<div className={classnames(classes.firstRow)}>
|
<div className={classnames(classes.firstRow)}>
|
||||||
<span className={classnames(classes.infoHeader)}>Sex</span>
|
<span className={classnames(classes.infoHeader)}>{t('Sex')}</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
title={patientSex}
|
title={patientSex}
|
||||||
@ -403,7 +406,7 @@ function PatientInfo({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>Age</span>
|
<span className={classnames(classes.infoHeader)}>{t('Age')}</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
title={patientAge}
|
title={patientAge}
|
||||||
@ -412,7 +415,7 @@ function PatientInfo({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>MRN</span>
|
<span className={classnames(classes.infoHeader)}>{t('MRN')}</span>
|
||||||
<span className={classnames(classes.infoText)} title={MRN}>
|
<span className={classnames(classes.infoText)} title={MRN}>
|
||||||
{MRN}
|
{MRN}
|
||||||
</span>
|
</span>
|
||||||
@ -421,7 +424,7 @@ function PatientInfo({
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className={classnames(classes.firstRow)}>
|
<div className={classnames(classes.firstRow)}>
|
||||||
<span className={classnames(classes.infoHeader)}>
|
<span className={classnames(classes.infoHeader)}>
|
||||||
Thickness
|
{t('Thickness')}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
@ -432,7 +435,7 @@ function PatientInfo({
|
|||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>
|
<span className={classnames(classes.infoHeader)}>
|
||||||
Spacing
|
{t('Spacing')}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
@ -443,7 +446,7 @@ function PatientInfo({
|
|||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>
|
<span className={classnames(classes.infoHeader)}>
|
||||||
Scanner
|
{t('Scanner')}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import React, {
|
|||||||
createRef,
|
createRef,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ const ViewportDownloadForm = ({
|
|||||||
maximumSize,
|
maximumSize,
|
||||||
canvasClass,
|
canvasClass,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('Modals');
|
||||||
|
|
||||||
const [filename, setFilename] = useState(DEFAULT_FILENAME);
|
const [filename, setFilename] = useState(DEFAULT_FILENAME);
|
||||||
const [fileType, setFileType] = useState(['jpg']);
|
const [fileType, setFileType] = useState(['jpg']);
|
||||||
|
|
||||||
@ -272,8 +275,7 @@ const ViewportDownloadForm = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Please specify the dimensions, filename, and desired type for the output
|
{t('Please specify the dimensions, filename, and desired type for the output image.')}
|
||||||
image.
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<div className="flex flex-col mt-6">
|
<div className="flex flex-col mt-6">
|
||||||
@ -282,7 +284,7 @@ const ViewportDownloadForm = ({
|
|||||||
data-cy="file-name"
|
data-cy="file-name"
|
||||||
value={filename}
|
value={filename}
|
||||||
onChange={evt => setFilename(evt.target.value)}
|
onChange={evt => setFilename(evt.target.value)}
|
||||||
label="File Name"
|
label={t("File Name")}
|
||||||
/>
|
/>
|
||||||
{renderErrorHandler('filename')}
|
{renderErrorHandler('filename')}
|
||||||
</div>
|
</div>
|
||||||
@ -294,7 +296,7 @@ const ViewportDownloadForm = ({
|
|||||||
type="number"
|
type="number"
|
||||||
min={minimumSize}
|
min={minimumSize}
|
||||||
max={maximumSize}
|
max={maximumSize}
|
||||||
label="Image width (px)"
|
label={t("Image width (px)")}
|
||||||
value={dimensions.width}
|
value={dimensions.width}
|
||||||
onChange={evt => onDimensionsChange(evt.target.value, 'width')}
|
onChange={evt => onDimensionsChange(evt.target.value, 'width')}
|
||||||
data-cy="image-width"
|
data-cy="image-width"
|
||||||
@ -306,7 +308,7 @@ const ViewportDownloadForm = ({
|
|||||||
type="number"
|
type="number"
|
||||||
min={minimumSize}
|
min={minimumSize}
|
||||||
max={maximumSize}
|
max={maximumSize}
|
||||||
label="Image height (px)"
|
label={t("Image height (px)")}
|
||||||
value={dimensions.height}
|
value={dimensions.height}
|
||||||
onChange={evt => onDimensionsChange(evt.target.value, 'height')}
|
onChange={evt => onDimensionsChange(evt.target.value, 'height')}
|
||||||
data-cy="image-height"
|
data-cy="image-height"
|
||||||
@ -335,7 +337,7 @@ const ViewportDownloadForm = ({
|
|||||||
<div>
|
<div>
|
||||||
<InputLabelWrapper
|
<InputLabelWrapper
|
||||||
sortDirection="none"
|
sortDirection="none"
|
||||||
label="File Type"
|
label={t("File Type")}
|
||||||
isSortable={false}
|
isSortable={false}
|
||||||
onLabelClick={() => {}}
|
onLabelClick={() => {}}
|
||||||
>
|
>
|
||||||
@ -363,7 +365,7 @@ const ViewportDownloadForm = ({
|
|||||||
checked={showAnnotations}
|
checked={showAnnotations}
|
||||||
onChange={event => setShowAnnotations(event.target.checked)}
|
onChange={event => setShowAnnotations(event.target.checked)}
|
||||||
/>
|
/>
|
||||||
<Typography>Show Annotations</Typography>
|
<Typography>{t("Show Annotations")}</Typography>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -375,7 +377,7 @@ const ViewportDownloadForm = ({
|
|||||||
className="p-4 rounded bg-secondary-dark border-secondary-primary"
|
className="p-4 rounded bg-secondary-dark border-secondary-primary"
|
||||||
data-cy="image-preview"
|
data-cy="image-preview"
|
||||||
>
|
>
|
||||||
<Typography variant="h5">Image preview</Typography>
|
<Typography variant="h5">{t("Image preview")}</Typography>
|
||||||
{activeViewport && (<div
|
{activeViewport && (<div
|
||||||
className="mx-auto my-0"
|
className="mx-auto my-0"
|
||||||
style={{
|
style={{
|
||||||
@ -397,7 +399,7 @@ const ViewportDownloadForm = ({
|
|||||||
</div>)}
|
</div>)}
|
||||||
{!activeViewport &&
|
{!activeViewport &&
|
||||||
<Typography className="mt-4">
|
<Typography className="mt-4">
|
||||||
Active viewport has no displayed image
|
{t("Active viewport has no displayed image")}
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -405,7 +407,7 @@ const ViewportDownloadForm = ({
|
|||||||
|
|
||||||
<div className="flex justify-end mt-4">
|
<div className="flex justify-end mt-4">
|
||||||
<Button data-cy="cancel-btn" variant="outlined" onClick={onClose}>
|
<Button data-cy="cancel-btn" variant="outlined" onClick={onClose}>
|
||||||
Cancel
|
{t("Cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
@ -414,7 +416,7 @@ const ViewportDownloadForm = ({
|
|||||||
color="primary"
|
color="primary"
|
||||||
data-cy="download-btn"
|
data-cy="download-btn"
|
||||||
>
|
>
|
||||||
Download
|
{t("Download")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import React, {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const ModalContext = createContext(null);
|
const ModalContext = createContext(null);
|
||||||
const { Provider } = ModalContext;
|
const { Provider } = ModalContext;
|
||||||
@ -36,6 +37,8 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
title: null,
|
title: null,
|
||||||
customClassName: '',
|
customClassName: '',
|
||||||
};
|
};
|
||||||
|
const { t } = useTranslation('Modals');
|
||||||
|
|
||||||
|
|
||||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||||
|
|
||||||
@ -86,7 +89,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
className={classNames(customClassName, ModalContent.className)}
|
className={classNames(customClassName, ModalContent.className)}
|
||||||
shouldCloseOnEsc={shouldCloseOnEsc}
|
shouldCloseOnEsc={shouldCloseOnEsc}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={title}
|
title={t(title)}
|
||||||
closeButton={closeButton}
|
closeButton={closeButton}
|
||||||
onClose={hide}
|
onClose={hide}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -352,7 +352,7 @@ function WorkList({
|
|||||||
className={classnames('font-bold', { 'ml-2': !isFirst })}
|
className={classnames('font-bold', { 'ml-2': !isFirst })}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
>
|
>
|
||||||
{mode.displayName}
|
{t(`Modes:${mode.displayName}`)}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user