feat(i18n): enhanced i18n support (#3761)
This commit is contained in:
parent
5a5c4f58af
commit
d14a8f0199
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { WrappedPanelStudyBrowser, PanelMeasurementTable } from './Panels';
|
||||
import i18n from 'i18next';
|
||||
|
||||
// TODO:
|
||||
// - No loading UI exists yet
|
||||
@ -22,7 +23,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
|
||||
name: 'seriesList',
|
||||
iconName: 'tab-studies',
|
||||
iconLabel: 'Studies',
|
||||
label: 'Studies',
|
||||
label: i18n.t('SidePanel:Studies'),
|
||||
component: WrappedPanelStudyBrowser.bind(null, {
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
@ -33,8 +34,8 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
|
||||
name: 'measure',
|
||||
iconName: 'tab-linear',
|
||||
iconLabel: 'Measure',
|
||||
label: 'Measurements',
|
||||
secondaryLabel: 'Measurements',
|
||||
label: i18n.t('SidePanel:Measurements'),
|
||||
secondaryLabel: i18n.t('SidePanel:Measurements'),
|
||||
component: wrappedMeasurementPanel,
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { ButtonEnums } from '@ohif/ui';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const RESPONSE = {
|
||||
NO_NEVER: -1,
|
||||
@ -26,24 +27,24 @@ function promptBeginTracking({ servicesManager, extensionManager }, ctx, evt) {
|
||||
|
||||
function _askTrackMeasurements(uiViewportDialogService, viewportId) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const message = 'Track measurements for this series?';
|
||||
const message = i18n.t('MeasurementTable:Track measurements for this series?');
|
||||
const actions = [
|
||||
{
|
||||
id: 'prompt-begin-tracking-cancel',
|
||||
type: ButtonEnums.type.secondary,
|
||||
text: 'No',
|
||||
text: i18n.t('Common:No'),
|
||||
value: RESPONSE.CANCEL,
|
||||
},
|
||||
{
|
||||
id: 'prompt-begin-tracking-no-do-not-ask-again',
|
||||
type: ButtonEnums.type.secondary,
|
||||
text: 'No, do not ask again',
|
||||
text: i18n.t('MeasurementTable:No, do not ask again'),
|
||||
value: RESPONSE.NO_NEVER,
|
||||
},
|
||||
{
|
||||
id: 'prompt-begin-tracking-yes',
|
||||
type: ButtonEnums.type.primary,
|
||||
text: 'Yes',
|
||||
text: i18n.t('Common:Yes'),
|
||||
value: RESPONSE.SET_STUDY_AND_SERIES,
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import i18n from 'i18next';
|
||||
|
||||
const RESPONSE = {
|
||||
NO_NEVER: -1,
|
||||
CANCEL: 0,
|
||||
@ -32,17 +34,17 @@ function promptTrackNewStudy({ servicesManager, extensionManager }, ctx, evt) {
|
||||
|
||||
function _askTrackMeasurements(UIViewportDialogService, viewportId) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const message = 'Track measurements for this series?';
|
||||
const message = i18n.t('MeasurementTable:Track measurements for this series?');
|
||||
const actions = [
|
||||
{ type: 'cancel', text: 'No', value: RESPONSE.CANCEL },
|
||||
{ type: 'cancel', text: i18n.t('MeasurementTable:No'), value: RESPONSE.CANCEL },
|
||||
{
|
||||
type: 'secondary',
|
||||
text: 'No, do not ask again for this series',
|
||||
text: i18n.t('MeasurementTable:No, do not ask again'),
|
||||
value: RESPONSE.NO_NOT_FOR_SERIES,
|
||||
},
|
||||
{
|
||||
type: 'primary',
|
||||
text: 'Yes',
|
||||
text: i18n.t('MeasurementTable:Yes'),
|
||||
value: RESPONSE.SET_STUDY_AND_SERIES,
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Types } from '@ohif/core';
|
||||
import { PanelMeasurementTableTracking, PanelStudyBrowserTracking } from './panels';
|
||||
import i18n from 'i18next';
|
||||
|
||||
// TODO:
|
||||
// - No loading UI exists yet
|
||||
@ -11,7 +12,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }):
|
||||
name: 'seriesList',
|
||||
iconName: 'tab-studies',
|
||||
iconLabel: 'Studies',
|
||||
label: 'Studies',
|
||||
label: i18n.t('SidePanel:Studies'),
|
||||
component: PanelStudyBrowserTracking.bind(null, {
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
@ -23,7 +24,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }):
|
||||
name: 'trackedMeasurements',
|
||||
iconName: 'tab-linear',
|
||||
iconLabel: 'Measure',
|
||||
label: 'Measurements',
|
||||
label: i18n.t('SidePanel:Measurements'),
|
||||
component: PanelMeasurementTableTracking.bind(null, {
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
|
||||
@ -222,10 +222,10 @@ function TrackedCornerstoneViewport(props) {
|
||||
patientAge: PatientAge || '',
|
||||
MRN: PatientID || '',
|
||||
thickness: SliceThickness ? `${parseFloat(SliceThickness).toFixed(2)}` : '',
|
||||
thicknessUnits: 'mm',
|
||||
thicknessUnits: t('mm'),
|
||||
spacing:
|
||||
SpacingBetweenSlices !== undefined
|
||||
? `${parseFloat(SpacingBetweenSlices).toFixed(2)}mm`
|
||||
? `${parseFloat(SpacingBetweenSlices).toFixed(2)}${t('mm')}`
|
||||
: '',
|
||||
scanner: ManufacturerModelName || '',
|
||||
},
|
||||
@ -312,6 +312,7 @@ function _getNextMeasurementUID(
|
||||
}
|
||||
|
||||
function _getStatusComponent(isTracked) {
|
||||
const { t } = useTranslation('TrackedCornerstoneViewport');
|
||||
const trackedIcon = isTracked ? 'status-tracked' : 'status-untracked';
|
||||
|
||||
return (
|
||||
@ -330,15 +331,11 @@ function _getStatusComponent(isTracked) {
|
||||
<span className="text-common-light text-base">
|
||||
{isTracked ? (
|
||||
<>
|
||||
Series is
|
||||
<span className="font-bold text-white"> tracked</span> and can be viewed <br />{' '}
|
||||
in the measurement panel
|
||||
{t('Series is tracked and can be viewed in the measurement panel')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Measurements for
|
||||
<span className="font-bold text-white"> untracked </span>
|
||||
series <br /> will not be shown in the <br /> measurements panel
|
||||
{t('Measurements for untracked series will not be shown in the measurements panel')}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import toolbarButtons from './toolbarButtons.js';
|
||||
import { hotkeys } from '@ohif/core';
|
||||
import { id } from './id';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const configs = {
|
||||
Length: {},
|
||||
@ -45,7 +46,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
return {
|
||||
id,
|
||||
routeName: 'dev',
|
||||
displayName: 'Basic Dev Viewer',
|
||||
displayName: i18n.t('Modes:Basic Dev Viewer'),
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
|
||||
@ -4,6 +4,7 @@ import { id } from './id';
|
||||
import initToolGroups from './initToolGroups';
|
||||
import moreTools from './moreTools';
|
||||
import moreToolsMpr from './moreToolsMpr';
|
||||
import i18n from 'i18next';
|
||||
|
||||
// Allow this mode by excluding non-imaging modalities such as SR, SEG
|
||||
// Also, SM is not a simple imaging modalities, so exclude it.
|
||||
@ -63,7 +64,7 @@ function modeFactory() {
|
||||
// We should not be.
|
||||
id,
|
||||
routeName: 'basic-test',
|
||||
displayName: 'Basic Test Mode',
|
||||
displayName: i18n.t('Modes:Basic Test Mode'),
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
|
||||
@ -4,6 +4,7 @@ import { id } from './id';
|
||||
import initToolGroups from './initToolGroups';
|
||||
import moreTools from './moreTools';
|
||||
import moreToolsMpr from './moreToolsMpr';
|
||||
import i18n from 'i18next';
|
||||
|
||||
// Allow this mode by excluding non-imaging modalities such as SR, SEG
|
||||
// Also, SM is not a simple imaging modalities, so exclude it.
|
||||
@ -69,7 +70,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
// We should not be.
|
||||
id,
|
||||
routeName: 'viewer',
|
||||
displayName: 'Basic Viewer',
|
||||
displayName: i18n.t('Modes:Basic Viewer'),
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { hotkeys } from '@ohif/core';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { id } from './id';
|
||||
import toolbarButtons from './toolbarButtons';
|
||||
@ -41,7 +42,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
// We should not be.
|
||||
id,
|
||||
routeName: 'microscopy',
|
||||
displayName: 'Microscopy',
|
||||
displayName: i18n.t('Modes:Microscopy'),
|
||||
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
|
||||
@ -2,6 +2,7 @@ import { hotkeys } from '@ohif/core';
|
||||
import { id } from './id';
|
||||
import toolbarButtons from './toolbarButtons';
|
||||
import initToolGroups from './initToolGroups';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const ohif = {
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
@ -44,7 +45,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
* Mode name, which is displayed in the viewer's UI in the workList, for the
|
||||
* user to select the mode.
|
||||
*/
|
||||
displayName: 'Segmentation',
|
||||
displayName: i18n.t('Modes:Segmentation'),
|
||||
/**
|
||||
* Runs when the Mode Route is mounted to the DOM. Usually used to initialize
|
||||
* Services and other resources.
|
||||
|
||||
@ -4,6 +4,7 @@ import { id } from './id.js';
|
||||
import initToolGroups, { toolGroupIds } from './initToolGroups.js';
|
||||
import setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';
|
||||
import setFusionActiveVolume from './utils/setFusionActiveVolume.js';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const { MetadataProvider } = classes;
|
||||
|
||||
@ -38,7 +39,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
// We should not be.
|
||||
id,
|
||||
routeName: 'tmtv',
|
||||
displayName: 'Total Metabolic Tumor Volume',
|
||||
displayName: i18n.t('Modes:Total Metabolic Tumor Volume'),
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
|
||||
@ -390,7 +390,7 @@ function WorkList({
|
||||
onClick={() => {}}
|
||||
data-cy={`mode-${mode.routeName}-${studyInstanceUid}`}
|
||||
>
|
||||
{t(`Modes:${mode.displayName}`)}
|
||||
{mode.displayName}
|
||||
</LegacyButton>
|
||||
</Link>
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import moment from 'moment';
|
||||
import i18n from 'i18next';
|
||||
|
||||
/**
|
||||
* Format date
|
||||
@ -7,7 +8,7 @@ import moment from 'moment';
|
||||
* @param {string} format Desired date format
|
||||
* @returns {string} Formatted date
|
||||
*/
|
||||
export default (date, format = 'DD-MMM-YYYY') => {
|
||||
export default (date, format = i18n.t('Common:localDateFormat','DD-MMM-YYYY')) => {
|
||||
// moment(undefined) returns the current date, so return the empty string instead
|
||||
return date ? moment(date).format(format) : '';
|
||||
};
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
"Layout": "Layout",
|
||||
"LOAD": "LOAD",
|
||||
"Measurements": "Measurements",
|
||||
"mm": "mm",
|
||||
"More": "More",
|
||||
"Next": "Next",
|
||||
"No": "No",
|
||||
"NoStudyDate": "No Study Date",
|
||||
"Play": "Play",
|
||||
"Previous": "Previous",
|
||||
@ -14,5 +16,6 @@
|
||||
"Series": "Series",
|
||||
"Show": "Show",
|
||||
"Stop": "Stop",
|
||||
"StudyDate": "Study Date"
|
||||
"StudyDate": "Study Date",
|
||||
"Yes": "Yes"
|
||||
}
|
||||
|
||||
8
platform/i18n/src/locales/en-US/ErrorBoundary.json
Normal file
8
platform/i18n/src/locales/en-US/ErrorBoundary.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Context": "Context",
|
||||
"Error Message": "Error Message",
|
||||
"Something went wrong": "Something went wrong",
|
||||
"in": "in",
|
||||
"Sorry, something went wrong there. Try again.": "Sorry, something went wrong there. Try again.",
|
||||
"Stack Trace": "Stack Trace"
|
||||
}
|
||||
6
platform/i18n/src/locales/en-US/HotkeysValidators.json
Normal file
6
platform/i18n/src/locales/en-US/HotkeysValidators.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Field can't be empty": "Field can't be empty",
|
||||
"Hotkey is already in use": "\"{{action}}\" is already using the \"{{pressedKeys}}\" shortcut.",
|
||||
"It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut": "It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut",
|
||||
"Shortcut combination is not allowed": "{{pressedKeys}} shortcut combination is not allowed"
|
||||
}
|
||||
@ -3,8 +3,10 @@
|
||||
"Delete": "Delete",
|
||||
"Description": "Description",
|
||||
"MAX": "MAX",
|
||||
"Measurements": "Measurements",
|
||||
"No, do not ask again": "No, do not ask again",
|
||||
"NonTargets": "NonTargets",
|
||||
"Relabel": "Relabel",
|
||||
"Targets": "Targets",
|
||||
"Measurements": "Measurements"
|
||||
"Track measurements for this series?": "Track measurements for this series?"
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"Display Set Messages": "Display Set Messages",
|
||||
"1": "No valid instances found in series.",
|
||||
"2": "Display set has missing position information.",
|
||||
"3": "Display set is not a reconstructable 3D volume.",
|
||||
|
||||
8
platform/i18n/src/locales/en-US/Modes.json
Normal file
8
platform/i18n/src/locales/en-US/Modes.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Basic Dev Viewer": "Basic Dev Viewer",
|
||||
"Basic Test Mode": "Basic Test Mode",
|
||||
"Basic Viewer": "Basic Viewer",
|
||||
"Microscopy": "Microscopy",
|
||||
"Segmentation": "Segmentation",
|
||||
"Total Metabolic Tumor Volume": "Total Metabolic Tumor Volume"
|
||||
}
|
||||
18
platform/i18n/src/locales/en-US/SegmentationTable.json
Normal file
18
platform/i18n/src/locales/en-US/SegmentationTable.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Active": "Active",
|
||||
"Add new segmentation": "Add new segmentation",
|
||||
"Add segment": "Add segment",
|
||||
"Add segmentation": "Add segmentation",
|
||||
"Delete": "Delete",
|
||||
"Display inactive segmentations": "Display inactive segmentations",
|
||||
"Export DICOM SEG": "Export DICOM SEG",
|
||||
"Download DICOM SEG": "Download DICOM SEG",
|
||||
"Download DICOM RTSTRUCT": "Download DICOM RTSTRUCT",
|
||||
"Fill": "Fill",
|
||||
"Inactive segmentations": "Inactive segmentations",
|
||||
"Opacity": "Opacity",
|
||||
"Outline": "Outline",
|
||||
"Rename": "Rename",
|
||||
"Segmentation": "Segmentation",
|
||||
"Size": "Size"
|
||||
}
|
||||
3
platform/i18n/src/locales/en-US/StudyItem.json
Normal file
3
platform/i18n/src/locales/en-US/StudyItem.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Tracked series": "{{trackedSeries}} Tracked series"
|
||||
}
|
||||
@ -3,16 +3,18 @@
|
||||
"ClearFilters": "Clear Filters",
|
||||
"Description": "Description",
|
||||
"Empty": "Empty",
|
||||
"Filter list to 100 studies or less to enable sorting": "Filter the list to 100 studies or less to enable sorting",
|
||||
"Instances": "Instances",
|
||||
"MRN": "MRN",
|
||||
"NumOfStudiesHiggerThan100Message": "Filter the list to 100 studies or less to enable sorting",
|
||||
"Modality": "Modality",
|
||||
"MRN": "MRN",
|
||||
"Next": "Next >",
|
||||
"No studies available": "No studies available",
|
||||
"Number of studies": "Number of studies",
|
||||
"Page": "Page",
|
||||
"PatientName": "Patient Name",
|
||||
"Previous": "< Back",
|
||||
"Page": "Page",
|
||||
"Next": "Next >",
|
||||
"Results per page": "Results per page",
|
||||
"Number of studies": "Studies",
|
||||
"StudyDate": "Study Date",
|
||||
"StudyList": "Study List"
|
||||
"StudyList": "Study List",
|
||||
"Upload": "Upload"
|
||||
}
|
||||
|
||||
5
platform/i18n/src/locales/en-US/ThumbnailTracked.json
Normal file
5
platform/i18n/src/locales/en-US/ThumbnailTracked.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"Series is tracked": "Series is tracked",
|
||||
"Series is untracked": "Series is untracked",
|
||||
"Viewport": "Viewport"
|
||||
}
|
||||
4
platform/i18n/src/locales/en-US/TooltipClipboard.json
Normal file
4
platform/i18n/src/locales/en-US/TooltipClipboard.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Copied": "Copied",
|
||||
"Failed to copy": "Failed to copy"
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Series is tracked and can be viewed in the measurement panel":
|
||||
"Series is tracked and can be viewed in the measurement panel",
|
||||
"Measurements for untracked series will not be shown in the measurements panel":
|
||||
"Measurements for untracked series will not be shown in the measurements panel"
|
||||
}
|
||||
@ -4,11 +4,19 @@ import CineDialog from './CineDialog.json';
|
||||
import Common from './Common.json';
|
||||
import DataSourceConfiguration from './DataSourceConfiguration.json';
|
||||
import DatePicker from './DatePicker.json';
|
||||
import ErrorBoundary from './ErrorBoundary.json';
|
||||
import Header from './Header.json';
|
||||
import HotkeysValidators from './HotkeysValidators.json';
|
||||
import MeasurementTable from './MeasurementTable.json';
|
||||
import Modes from './Modes.json';
|
||||
import SegmentationTable from './SegmentationTable.json';
|
||||
import SidePanel from './SidePanel.json';
|
||||
import StudyBrowser from './StudyBrowser.json';
|
||||
import StudyItem from './StudyItem.json';
|
||||
import StudyList from './StudyList.json';
|
||||
import TooltipClipboard from './TooltipClipboard.json';
|
||||
import ThumbnailTracked from './ThumbnailTracked.json';
|
||||
import TrackedCornerstoneViewport from './TrackedCornerstoneViewport.json';
|
||||
import UserPreferencesModal from './UserPreferencesModal.json';
|
||||
import ViewportDownloadForm from './ViewportDownloadForm.json';
|
||||
import Messages from './Messages.json';
|
||||
@ -21,11 +29,19 @@ export default {
|
||||
Common,
|
||||
DataSourceConfiguration,
|
||||
DatePicker,
|
||||
ErrorBoundary,
|
||||
Header,
|
||||
HotkeysValidators,
|
||||
MeasurementTable,
|
||||
Modes,
|
||||
SegmentationTable,
|
||||
SidePanel,
|
||||
StudyBrowser,
|
||||
StudyItem,
|
||||
StudyList,
|
||||
TooltipClipboard,
|
||||
ThumbnailTracked,
|
||||
TrackedCornerstoneViewport,
|
||||
UserPreferencesModal,
|
||||
ViewportDownloadForm,
|
||||
Messages,
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
"ClearFilters": "Limpiar filtros",
|
||||
"Description": "Descripción",
|
||||
"Empty": "vacío",
|
||||
"Filter list to 100 studies or less to enable sorting": "Filtre la lista a 100 estudios o menos para habilitar la clasificación",
|
||||
"Instances": "Instancias",
|
||||
"MRN": "MRN",
|
||||
"NumOfStudiesHiggerThan100Message": "Filtre la lista a 100 estudios o menos para habilitar la clasificación",
|
||||
"Modality": "Modalidad",
|
||||
"PatientName": "Nombre paciente",
|
||||
"Previous": "< Anterior",
|
||||
|
||||
@ -3,8 +3,10 @@
|
||||
"Image": "Test Image",
|
||||
"Layout": "Test Layout",
|
||||
"Measurements": "Test Measurements",
|
||||
"mm": "Test mm",
|
||||
"More": "Test More",
|
||||
"Next": "Test Next",
|
||||
"No": "Test No",
|
||||
"Play": "Test Play",
|
||||
"Previous": "Test Previous",
|
||||
"Reset": "Test Reset",
|
||||
@ -12,5 +14,6 @@
|
||||
"Series": "Test Series",
|
||||
"Show": "Test Show",
|
||||
"Stop": "Test Stop",
|
||||
"StudyDate": "Test Study Date"
|
||||
"StudyDate": "Test Study Date",
|
||||
"Yes": "Test Yes"
|
||||
}
|
||||
|
||||
8
platform/i18n/src/locales/test-LNG/ErrorBoundary.json
Normal file
8
platform/i18n/src/locales/test-LNG/ErrorBoundary.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Context": "Test Context",
|
||||
"Error Message": "Test Error Message",
|
||||
"Something went wrong": "Test Something went wrong",
|
||||
"in": "in",
|
||||
"Sorry, something went wrong there. Try again.": "Test Sorry, something went wrong there. Try again.",
|
||||
"Stack Trace": "Test Stack Trace"
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Field can't be empty": "Test Field can't be empty",
|
||||
"Hotkey is already in use": "Test \"{{action}}\" is already using the \"{{pressedKeys}}\" shortcut.",
|
||||
"It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut": "Test It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut",
|
||||
"Shortcut combination is not allowed": "Test {{pressedKeys}} shortcut combination is not allowed"
|
||||
}
|
||||
@ -6,7 +6,9 @@
|
||||
"Delete": "Delete",
|
||||
"Description": "Description",
|
||||
"MAX": "MAX",
|
||||
"No, do not ask again": "Test No, do not ask again",
|
||||
"NonTargets": "NonTargets",
|
||||
"Relabel": "Relabel",
|
||||
"Targets": "Targets"
|
||||
"Targets": "Targets",
|
||||
"Track measurements for this series?": "Test Track measurements for this series?"
|
||||
}
|
||||
|
||||
16
platform/i18n/src/locales/test-LNG/Messages.json
Normal file
16
platform/i18n/src/locales/test-LNG/Messages.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"Display Set Messages": "Test Display Set Messages",
|
||||
"1": "Test No valid instances found in series.",
|
||||
"2": "Test Display set has missing position information.",
|
||||
"3": "Test Display set is not a reconstructable 3D volume.",
|
||||
"4": "Test Multi frame display sets do not have pixel measurement information.",
|
||||
"5": "Test Multi frame display sets do not have orientation information.",
|
||||
"6": "Test Multi frame display sets do not have position information.",
|
||||
"7": "Test Display set has missing frames.",
|
||||
"8": "Test Display set has irregular spacing.",
|
||||
"9": "Test Display set has inconsistent dimensions between frames.",
|
||||
"10": "Test Display set has frames with inconsistent number of components.",
|
||||
"11": "Test Display set has frames with inconsistent orientations.",
|
||||
"12": "Test Display set has inconsistent position information.",
|
||||
"13": "Test Unsupported display set."
|
||||
}
|
||||
@ -1,3 +1,8 @@
|
||||
{
|
||||
"Basic Viewer": "Test Basic Viewer"
|
||||
"Basic Dev Viewer": "Test Basic Dev Viewer",
|
||||
"Basic Test Mode": "Test Basic Test Mode",
|
||||
"Basic Viewer": "Test Basic Viewer",
|
||||
"Microscopy": "Test Microscopy",
|
||||
"Segmentation": "Test Segmentation",
|
||||
"Total Metabolic Tumor Volume": "Test Total Metabolic Tumor Volume"
|
||||
}
|
||||
|
||||
18
platform/i18n/src/locales/test-LNG/SegmentationTable.json
Normal file
18
platform/i18n/src/locales/test-LNG/SegmentationTable.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Active": "Test Active",
|
||||
"Add new segmentation": "Test Add new segmentation",
|
||||
"Add segment": "Test Add segment",
|
||||
"Add segmentation": "Test Add segmentation",
|
||||
"Delete": "Test Delete",
|
||||
"Display inactive segmentations": "Test Display inactive segmentations",
|
||||
"Export DICOM SEG": "Test Export DICOM SEG",
|
||||
"Download DICOM SEG": "Test Download DICOM SEG",
|
||||
"Download DICOM RTSTRUCT": "Test Download DICOM RTSTRUCT",
|
||||
"Fill": "Test Fill",
|
||||
"Inactive segmentations": "Test Inactive segmentations",
|
||||
"Opacity": "Test Opacity",
|
||||
"Outline": "Test Outline",
|
||||
"Rename": "Test Rename",
|
||||
"Segmentation": "Test Segmentation",
|
||||
"Size": "Test Size"
|
||||
}
|
||||
3
platform/i18n/src/locales/test-LNG/StudyItem.json
Normal file
3
platform/i18n/src/locales/test-LNG/StudyItem.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Tracked series": "{{trackedSeries}} Test Tracked series"
|
||||
}
|
||||
@ -1,21 +1,23 @@
|
||||
{
|
||||
"Previous": "Test < Previous",
|
||||
"AccessionNumber": "Test Accession #",
|
||||
"Accession": "Test Accession",
|
||||
"Next >": "Test Next >",
|
||||
"< Previous": "Test < Previous",
|
||||
"Results per page": "Test Results per page",
|
||||
"Description": "Test Description",
|
||||
"Empty": "Test Empty",
|
||||
"Filter list to 100 studies or less to enable sorting": "Test Filter list to 100 studies or less to enable sorting",
|
||||
"Instances": "Test Instances",
|
||||
"MRN": "Test MRN",
|
||||
"Modality": "Test Modality",
|
||||
"Next": "Test Next >",
|
||||
"No studies available": "Test No studies available",
|
||||
"Number of studies": "Test Number of studies",
|
||||
"PatientName": "Test PatientName",
|
||||
"Patient Name": "Test Patient Name",
|
||||
"Description": "Test Description",
|
||||
"Results per page": "Test Results per page",
|
||||
"StudyDate": "Test Study Date",
|
||||
"Study date": "Test Study Date",
|
||||
"Number of 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"
|
||||
"Upload": "Test Upload"
|
||||
}
|
||||
|
||||
5
platform/i18n/src/locales/test-LNG/ThumbnailTracked.json
Normal file
5
platform/i18n/src/locales/test-LNG/ThumbnailTracked.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"Series is tracked": "Test Series is tracked",
|
||||
"Series is untracked": "Test Series is untracked",
|
||||
"Viewport": "Test Viewport"
|
||||
}
|
||||
4
platform/i18n/src/locales/test-LNG/TooltipClipboard.json
Normal file
4
platform/i18n/src/locales/test-LNG/TooltipClipboard.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Copied": "Test Copied",
|
||||
"Failed to copy": "Test Failed to copy"
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Series is tracked and can be viewed in the measurement panel":
|
||||
"Test Series is tracked and can be viewed in the measurement panel",
|
||||
"Measurements for untracked series will not be shown in the measurements panel":
|
||||
"Test Measurements for untracked series will not be shown in the measurements panel"
|
||||
}
|
||||
@ -3,17 +3,25 @@ import Buttons from './Buttons.json';
|
||||
import CineDialog from './CineDialog.json';
|
||||
import Common from './Common.json';
|
||||
import DatePicker from './DatePicker.json';
|
||||
import ErrorBoundary from './ErrorBoundary.json';
|
||||
import Header from './Header.json';
|
||||
import HotkeysValidators from './HotkeysValidators.json';
|
||||
import MeasurementTable from './MeasurementTable.json';
|
||||
import Messages from './Messages.json';
|
||||
import Modals from './Modals.json';
|
||||
import Modes from './Modes.json';
|
||||
import PatientInfo from './PatientInfo.json';
|
||||
import SegmentationTable from './SegmentationTable.json';
|
||||
import SidePanel from './SidePanel.json';
|
||||
import StudyBrowser from './StudyBrowser.json';
|
||||
import StudyItem from './StudyItem.json';
|
||||
import StudyList from './StudyList.json';
|
||||
import ToolTip from './ToolTip.json';
|
||||
import TooltipClipboard from './TooltipClipboard.json';
|
||||
import ThumbnailTracked from './ThumbnailTracked.json';
|
||||
import TrackedCornerstoneViewport from './TrackedCornerstoneViewport.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': {
|
||||
@ -22,16 +30,24 @@ export default {
|
||||
CineDialog,
|
||||
Common,
|
||||
DatePicker,
|
||||
ErrorBoundary,
|
||||
Header,
|
||||
HotkeysValidators,
|
||||
MeasurementTable,
|
||||
Messages,
|
||||
Modals,
|
||||
Modes,
|
||||
PatientInfo,
|
||||
SegmentationTable,
|
||||
SidePanel,
|
||||
StudyBrowser,
|
||||
StudyItem,
|
||||
StudyList,
|
||||
ToolTip,
|
||||
TooltipClipboard,
|
||||
ThumbnailTracked,
|
||||
TrackedCornerstoneViewport,
|
||||
UserPreferencesModal,
|
||||
ViewportDownloadForm,
|
||||
ToolTip,
|
||||
StudyBrowser,
|
||||
PatientInfo,
|
||||
Modes,
|
||||
SidePanel,
|
||||
Modals,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"Empty": "无",
|
||||
"Filter list to 100 studies or less to enable sorting": "将检查列表过滤到 100 个或更少以启用排序",
|
||||
"Modality": "成像设备",
|
||||
"PatientName": "患者姓名",
|
||||
"StudyDate": "检查日期",
|
||||
@ -14,14 +15,13 @@
|
||||
"Instances": "图像数",
|
||||
"Accession": "检查号",
|
||||
"Results per page": "每页条数",
|
||||
"< Previous": "上一页",
|
||||
"Next >": "下一页",
|
||||
"Previous": "上一页",
|
||||
"Next": "下一页",
|
||||
"Page": "页码",
|
||||
"Start Date": "开始日期",
|
||||
"Series": "序列",
|
||||
"No studies available": "没有数据",
|
||||
"Loading...": "加载中...",
|
||||
"Select...": "选择...",
|
||||
"InstitutionName": "检查机构",
|
||||
"Filter list to 100 studies or less to enable sorting": "将检查列表过滤到 100 个或更少以启用排序"
|
||||
"InstitutionName": "检查机构"
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ const DisplaySetMessageListTooltip = ({ messages, id }): React.ReactNode => {
|
||||
marginTop: '12px',
|
||||
}}
|
||||
>
|
||||
Display Set Messages
|
||||
{t('Display Set Messages')}
|
||||
</div>
|
||||
<ol
|
||||
style={{
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Icon from '../Icon';
|
||||
import Typography from '../Typography';
|
||||
|
||||
// TODO: Add loading spinner to OHIF + use it here.
|
||||
const EmptyStudies = ({ className }) => {
|
||||
const { t } = useTranslation('StudyList');
|
||||
return (
|
||||
<div className={classnames('inline-flex flex-col items-center', className)}>
|
||||
<Icon
|
||||
@ -17,7 +19,7 @@ const EmptyStudies = ({ className }) => {
|
||||
className="text-primary-light"
|
||||
variant="h5"
|
||||
>
|
||||
{'No studies available'}
|
||||
{t('No studies available')}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import Modal from '../Modal';
|
||||
import Icon from '../Icon';
|
||||
@ -9,9 +11,10 @@ import IconButton from '../IconButton';
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const DefaultFallback = ({ error, context, resetErrorBoundary, fallbackRoute }) => {
|
||||
const { t } = useTranslation('ErrorBoundary');
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const title = `Something went wrong${!isProduction && ` in ${context}`}.`;
|
||||
const subtitle = `Sorry, something went wrong there. Try again.`;
|
||||
const title = `${t('Something went wrong')}${!isProduction && ` ${t('in')} ${context}`}.`;
|
||||
const subtitle = t('Sorry, something went wrong there. Try again.');
|
||||
return (
|
||||
<div
|
||||
className="ErrorFallback bg-primary-dark h-full w-full"
|
||||
@ -21,8 +24,8 @@ const DefaultFallback = ({ error, context, resetErrorBoundary, fallbackRoute })
|
||||
<p className="text-primary-light text-base">{subtitle}</p>
|
||||
{!isProduction && (
|
||||
<div className="bg-secondary-dark mt-5 space-y-2 rounded-md p-5 font-mono">
|
||||
<p className="text-primary-light">Context: {context}</p>
|
||||
<p className="text-primary-light">Error Message: {error.message}</p>
|
||||
<p className="text-primary-light">{t('Context')}: {context}</p>
|
||||
<p className="text-primary-light">{t('Error Message')}: {error.message}</p>
|
||||
|
||||
<IconButton
|
||||
variant="contained"
|
||||
@ -32,7 +35,7 @@ const DefaultFallback = ({ error, context, resetErrorBoundary, fallbackRoute })
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
>
|
||||
<React.Fragment>
|
||||
<div>{'Stack Trace'}</div>
|
||||
<div>{t('Stack Trace')}</div>
|
||||
<Icon
|
||||
width="15px"
|
||||
height="15px"
|
||||
@ -83,7 +86,7 @@ const ErrorBoundary = ({
|
||||
closeButton
|
||||
shouldCloseOnEsc
|
||||
isOpen={isOpen}
|
||||
title={'Something went wrong'}
|
||||
title={i18n.t('ErrorBoundary:Something went wrong')}
|
||||
onClose={() => {
|
||||
setIsOpen(false);
|
||||
if (fallbackRoute && typeof window !== 'undefined') {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { MODIFIER_KEYS, DISALLOWED_COMBINATIONS } from './hotkeysConfig';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const formatPressedKeys = pressedKeysArray => pressedKeysArray.join('+');
|
||||
|
||||
@ -20,8 +21,8 @@ const findConflictingCommand = (hotkeys, currentCommandName, pressedKeys) => {
|
||||
};
|
||||
|
||||
const ERROR_MESSAGES = {
|
||||
MODIFIER: "It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut",
|
||||
EMPTY: "Field can't be empty.",
|
||||
MODIFIER: i18n.t('HotkeysValidators:It\'s not possible to define only modifier keys (ctrl, alt and shift) as a shortcut'),
|
||||
EMPTY: i18n.t('HotkeysValidators:Field can\'t be empty'),
|
||||
};
|
||||
|
||||
// VALIDATORS
|
||||
@ -46,7 +47,7 @@ const conflictingValidator = ({ commandName, pressedKeys, hotkeys }) => {
|
||||
|
||||
if (conflictingCommand) {
|
||||
return {
|
||||
error: `"${conflictingCommand.label}" is already using the "${pressedKeys}" shortcut.`,
|
||||
error: i18n.t('HotkeysValidators:Hotkey is already in use', {action: conflictingCommand.label, pressedKeys: pressedKeys }),
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -62,7 +63,7 @@ const disallowedValidator = ({ pressedKeys = [] }) => {
|
||||
|
||||
if (hasDisallowedCombinations) {
|
||||
return {
|
||||
error: `"${formatPressedKeys(pressedKeys)}" shortcut combination is not allowed`,
|
||||
error: i18n.t('HotkeysValidators:Shortcut combination is not allowed', {pressedKeys: formatPressedKeys(pressedKeys)}),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import Icon from '../Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function AddSegmentRow({ onClick }) {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
return (
|
||||
<div
|
||||
className="flex hover:cursor-pointer"
|
||||
@ -13,7 +15,7 @@ function AddSegmentRow({ onClick }) {
|
||||
<div className="grid h-[28px] w-[28px] place-items-center">
|
||||
<Icon name="icon-add" />
|
||||
</div>
|
||||
<span className="text-[13px]">Add Segment</span>
|
||||
<span className="text-[13px]">{t('Add segment')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import Icon from '../Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function NoSegmentationRow({ onSegmentationAdd }) {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
return (
|
||||
<div
|
||||
className="group"
|
||||
@ -11,7 +13,7 @@ function NoSegmentationRow({ onSegmentationAdd }) {
|
||||
<div className="grid h-[28px] w-[28px] place-items-center">
|
||||
<Icon name="icon-add" />
|
||||
</div>
|
||||
<span className="text-[13px]">Add Segmentation</span>
|
||||
<span className="text-[13px]">{t('Add segmentation')}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import InputRange from '../InputRange';
|
||||
import CheckBox from '../CheckBox';
|
||||
import InputNumber from '../InputNumber';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const getRoundedValue = value => {
|
||||
return Math.round(value * 100) / 100;
|
||||
@ -17,19 +18,20 @@ const ActiveSegmentationConfig = ({
|
||||
setRenderFill,
|
||||
setFillAlpha,
|
||||
}) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
return (
|
||||
<div className="flex justify-between px-3 pt-[13px] text-[12px]">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="mb-[12px] text-white">Active</div>
|
||||
<div className="mb-[12px] text-white">{t('Active')}</div>
|
||||
<CheckBox
|
||||
label="Outline"
|
||||
label={t('Outline')}
|
||||
checked={config.renderOutline}
|
||||
labelClassName="text-[12px] pl-1 pt-1"
|
||||
className="mb-[9px]"
|
||||
onChange={setRenderOutline}
|
||||
/>
|
||||
<CheckBox
|
||||
label="Fill"
|
||||
label={t('Fill')}
|
||||
checked={config.renderFill}
|
||||
labelClassName="text-[12px] pl-1 pt-1"
|
||||
className="mb-[9px]"
|
||||
@ -38,7 +40,7 @@ const ActiveSegmentationConfig = ({
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex flex-col items-center">
|
||||
<div className="mb-[12px] text-[10px] text-[#b3b3b3]">Opacity</div>
|
||||
<div className="mb-[12px] text-[10px] text-[#b3b3b3]">{t('Opacity')}</div>
|
||||
<InputRange
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
@ -64,7 +66,7 @@ const ActiveSegmentationConfig = ({
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="mb-[12px] text-[10px] text-[#b3b3b3]">Size</div>
|
||||
<div className="mb-[12px] text-[10px] text-[#b3b3b3]">{t('Size')}</div>
|
||||
<InputNumber
|
||||
value={config.outlineWidthActive}
|
||||
onChange={setOutlineWidthActive}
|
||||
@ -82,10 +84,11 @@ const InactiveSegmentationConfig = ({
|
||||
setRenderInactiveSegmentations,
|
||||
setFillAlphaInactive,
|
||||
}) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
return (
|
||||
<div className="px-3">
|
||||
<CheckBox
|
||||
label="Display Inactive Segmentations"
|
||||
label={t('Display inactive segmentations')}
|
||||
checked={config.renderInactiveSegmentations}
|
||||
labelClassName="text-[12px]"
|
||||
className="mb-[9px]"
|
||||
@ -93,7 +96,7 @@ const InactiveSegmentationConfig = ({
|
||||
/>
|
||||
|
||||
<div className="flex items-center space-x-2 pl-4">
|
||||
<span className="text-[10px] text-[#b3b3b3]">Opacity</span>
|
||||
<span className="text-[10px] text-[#b3b3b3]">{t('Opacity')}</span>
|
||||
<InputRange
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
@ -120,6 +123,7 @@ const SegmentationConfig = ({
|
||||
setRenderInactiveSegmentations,
|
||||
setRenderOutline,
|
||||
}) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
const { initialConfig } = segmentationConfig;
|
||||
const [isMinimized, setIsMinimized] = useState(true);
|
||||
return (
|
||||
@ -144,7 +148,7 @@ const SegmentationConfig = ({
|
||||
'rotate-90 transform': !isMinimized,
|
||||
})}
|
||||
/>
|
||||
<span className="text-[12px] font-[300] text-[#d8d8d8]">{'Inactive Segmentations'}</span>
|
||||
<span className="text-[12px] font-[300] text-[#d8d8d8]">{t('Inactive segmentations')}</span>
|
||||
</div>
|
||||
{!isMinimized && (
|
||||
<InactiveSegmentationConfig
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Select, Icon, Dropdown } from '../../components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function SegmentationDropDownRow({
|
||||
segmentations = [],
|
||||
@ -23,6 +24,7 @@ function SegmentationDropDownRow({
|
||||
value: s.id,
|
||||
label: s.label,
|
||||
}));
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
|
||||
if (!activeSegmentation) {
|
||||
return null;
|
||||
@ -46,7 +48,7 @@ function SegmentationDropDownRow({
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Add New Segmentation',
|
||||
title: t('Add new segmentation'),
|
||||
onClick: () => {
|
||||
onSegmentationAdd();
|
||||
},
|
||||
@ -56,7 +58,7 @@ function SegmentationDropDownRow({
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Rename',
|
||||
title: t('Rename'),
|
||||
onClick: () => {
|
||||
onSegmentationEdit(activeSegmentation.id);
|
||||
},
|
||||
@ -64,7 +66,7 @@ function SegmentationDropDownRow({
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'Delete',
|
||||
title: t('Delete'),
|
||||
onClick: () => {
|
||||
onSegmentationDelete(activeSegmentation.id);
|
||||
},
|
||||
@ -72,7 +74,7 @@ function SegmentationDropDownRow({
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Export DICOM SEG',
|
||||
title: t('Export DICOM SEG'),
|
||||
onClick: () => {
|
||||
storeSegmentation(activeSegmentation.id);
|
||||
},
|
||||
@ -81,13 +83,13 @@ function SegmentationDropDownRow({
|
||||
: []),
|
||||
...[
|
||||
{
|
||||
title: 'Download DICOM SEG',
|
||||
title: t('Download DICOM SEG'),
|
||||
onClick: () => {
|
||||
onSegmentationDownload(activeSegmentation.id);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Download DICOM RTSTRUCT',
|
||||
title: t('Download DICOM RTSTRUCT'),
|
||||
onClick: () => {
|
||||
onSegmentationDownloadRTSS(activeSegmentation.id);
|
||||
},
|
||||
|
||||
@ -6,6 +6,7 @@ import SegmentationDropDownRow from './SegmentationDropDownRow';
|
||||
import NoSegmentationRow from './NoSegmentationRow';
|
||||
import AddSegmentRow from './AddSegmentRow';
|
||||
import SegmentationGroupSegment from './SegmentationGroupSegment';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const SegmentationGroupTable = ({
|
||||
segmentations,
|
||||
@ -70,11 +71,12 @@ const SegmentationGroupTable = ({
|
||||
const activeSegmentation = segmentations?.find(
|
||||
segmentation => segmentation.id === activeSegmentationId
|
||||
);
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-col bg-black text-[13px] font-[300]">
|
||||
<PanelSection
|
||||
title="Segmentation"
|
||||
title={t('Segmentation')}
|
||||
actionIcons={
|
||||
activeSegmentation && [
|
||||
{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { CSSProperties, useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Icon from '../Icon';
|
||||
import Tooltip from '../Tooltip';
|
||||
@ -131,8 +130,6 @@ const getTabIconClassNames = (numTabs: number, isActiveTab: boolean) => {
|
||||
};
|
||||
|
||||
const SidePanel = ({ side, className, activeTabIndex: activeTabIndexProp, tabs, onOpen }) => {
|
||||
const { t } = useTranslation('SidePanel');
|
||||
|
||||
const [panelOpen, setPanelOpen] = useState(activeTabIndexProp !== null);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Icon from '../Icon';
|
||||
|
||||
@ -16,6 +17,7 @@ const StudyItem = ({
|
||||
isActive,
|
||||
onClick,
|
||||
}) => {
|
||||
const { t } = useTranslation('StudyItem');
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
@ -57,7 +59,7 @@ const StudyItem = ({
|
||||
name="tracked"
|
||||
className="text-primary-light mr-2 w-4"
|
||||
/>
|
||||
{trackedSeries} Tracked Series
|
||||
{t('Tracked series', {trackedSeries: trackedSeries})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -48,7 +48,7 @@ const StudyListFilter = ({
|
||||
onClick={onUploadClick}
|
||||
>
|
||||
<Icon name="icon-upload"></Icon>
|
||||
<span>Upload</span>
|
||||
<span>{t('Upload')}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -67,6 +67,12 @@ const StudyListFilter = ({
|
||||
{t('ClearFilters')}
|
||||
</LegacyButton>
|
||||
)}
|
||||
<Typography
|
||||
variant="h6"
|
||||
className="text-primary-light"
|
||||
>
|
||||
{`${t('Number of studies')}: `}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
className="mr-2"
|
||||
@ -74,12 +80,6 @@ const StudyListFilter = ({
|
||||
>
|
||||
{numOfStudies > 100 ? '>100' : numOfStudies}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
className="text-primary-light"
|
||||
>
|
||||
{t('Number of studies')}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,7 +99,7 @@ const StudyListFilter = ({
|
||||
{numOfStudies > 100 && (
|
||||
<div className="container m-auto">
|
||||
<div className="bg-primary-main rounded-b py-1 text-center text-base">
|
||||
<p className="text-white">{t('NumOfStudiesHiggerThan100Message')}</p>
|
||||
<p className="text-white">{t('Filter list to 100 studies or less to enable sorting')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -6,8 +6,9 @@ import Icon from '../Icon';
|
||||
import Thumbnail from '../Thumbnail';
|
||||
import Tooltip from '../Tooltip';
|
||||
import { StringNumber } from '../../types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const ThumbnailTracked = ({
|
||||
function ThumbnailTracked ({
|
||||
displaySetInstanceUID,
|
||||
className,
|
||||
imageSrc,
|
||||
@ -24,7 +25,8 @@ const ThumbnailTracked = ({
|
||||
viewportIdentificator,
|
||||
isTracked,
|
||||
isActive,
|
||||
}) => {
|
||||
}) {
|
||||
const { t } = useTranslation('ThumbnailTracked');
|
||||
const trackedIcon = isTracked ? 'circled-checkmark' : 'dotted-circle';
|
||||
const viewportIdentificatorLabel = viewportIdentificator.join(', ');
|
||||
const renderViewportLabels = () => {
|
||||
@ -42,7 +44,7 @@ const ThumbnailTracked = ({
|
||||
position="right"
|
||||
content={
|
||||
<div className="max-w-40 text-left">
|
||||
Series is displayed <br /> in viewport {viewportIdentificatorLabel}
|
||||
{`${t('Viewport')}: ${viewportIdentificatorLabel}`}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@ -85,13 +87,11 @@ const ThumbnailTracked = ({
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col">
|
||||
<span>
|
||||
Series is
|
||||
<span className="text-white">{isTracked ? ' tracked' : ' untracked'}</span>
|
||||
<span className="text-white">{isTracked ? t('Series is tracked') : t('Series is untracked')}</span>
|
||||
</span>
|
||||
{!!viewportIdentificator.length && (
|
||||
<span>
|
||||
in viewport
|
||||
<span className="ml-1 text-white">{viewportIdentificatorLabel}</span>
|
||||
{`${t('Viewport')}: ${viewportIdentificatorLabel}`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
@ -9,6 +10,8 @@ const DELAY_TO_HIDE = 10; // it needs at least a little delay to prevent tooltip
|
||||
const DELAY_TO_HIDE_AFTER_COPYING = 1000;
|
||||
|
||||
const TooltipClipboard = ({ children, text }) => {
|
||||
const { t } = useTranslation('TooltipClipboard');
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [message, setMessage] = useState(null);
|
||||
const [isCopying, setIsCopying] = useState(false);
|
||||
@ -21,10 +24,10 @@ const TooltipClipboard = ({ children, text }) => {
|
||||
setIsCopying(true);
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setMessage('Copied!');
|
||||
setMessage(t('Copied'));
|
||||
} catch (err) {
|
||||
console.error('Failed to copy: ', err);
|
||||
setMessage('Failed to copy!');
|
||||
setMessage(t('Failed to copy'));
|
||||
} finally {
|
||||
refreshElementPosition();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user