chore: update French i18n (#5561)
This commit is contained in:
1 parent
ba140a05f7
commit
181c0f32c5
185 files changed
+2779
-374
No files matched your search
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { AboutModal } from '@ohif/ui-next';
|
||||
import detect from 'browser-detect';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function AboutModalDefault() {
|
||||
const { t } = useTranslation('AboutModal');
|
||||
const { os, version, name } = detect();
|
||||
const browser = `${name[0].toUpperCase()}${name.substr(1)} ${version}`;
|
||||
const versionNumber = process.env.VERSION_NUMBER;
|
||||
@@ -18,11 +20,11 @@ function AboutModalDefault() {
|
||||
|
||||
<AboutModal.Body>
|
||||
<AboutModal.DetailItem
|
||||
label="Commit Hash"
|
||||
label={t('Commit Hash')}
|
||||
value={commitHash}
|
||||
/>
|
||||
<AboutModal.DetailItem
|
||||
label="Current Browser & OS"
|
||||
label={t('Current Browser & OS')}
|
||||
value={`${browser}, ${os}`}
|
||||
/>
|
||||
<AboutModal.SocialItem
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
function waitForElement(selector, maxAttempts = 20, interval = 25) {
|
||||
return new Promise(resolve => {
|
||||
let attempts = 0;
|
||||
@@ -23,8 +25,8 @@ export default {
|
||||
steps: [
|
||||
{
|
||||
id: 'scroll',
|
||||
title: 'Scrolling Through Images',
|
||||
text: 'You can scroll through the images using the mouse wheel or scrollbar.',
|
||||
title: i18n.t('Onboarding:Scrolling Through Images'),
|
||||
text: i18n.t('Onboarding:You can scroll through the images using the mouse wheel or scrollbar.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'top',
|
||||
@@ -37,8 +39,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'zoom',
|
||||
title: 'Zooming In and Out',
|
||||
text: 'You can zoom the images using the right click.',
|
||||
title: i18n.t('Onboarding:Zooming In and Out'),
|
||||
text: i18n.t('Onboarding:You can zoom the images using the right click.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'left',
|
||||
@@ -51,8 +53,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'pan',
|
||||
title: 'Panning the Image',
|
||||
text: 'You can pan the images using the middle click.',
|
||||
title: i18n.t('Onboarding:Panning the Image'),
|
||||
text: i18n.t('Onboarding:You can pan the images using the middle click.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'top',
|
||||
@@ -65,8 +67,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'windowing',
|
||||
title: 'Adjusting Window Level',
|
||||
text: 'You can modify the window level using the left click.',
|
||||
title: i18n.t('Onboarding:Adjusting Window Level'),
|
||||
text: i18n.t('Onboarding:You can modify the window level using the left click.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'left',
|
||||
@@ -79,8 +81,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'length',
|
||||
title: 'Using the Measurement Tools',
|
||||
text: 'You can measure the length of a region using the Length tool.',
|
||||
title: i18n.t('Onboarding:Using the Measurement Tools'),
|
||||
text: i18n.t('Onboarding:You can measure the length of a region using the Length tool.'),
|
||||
attachTo: {
|
||||
element: '[data-cy="MeasurementTools-split-button-primary"]',
|
||||
on: 'bottom',
|
||||
@@ -90,12 +92,12 @@ export default {
|
||||
event: 'click',
|
||||
},
|
||||
beforeShowPromise: () =>
|
||||
waitForElement('[data-cy="MeasurementTools-split-button-primary]'),
|
||||
waitForElement('[data-cy="MeasurementTools-split-button-primary"]'),
|
||||
},
|
||||
{
|
||||
id: 'drawAnnotation',
|
||||
title: 'Drawing Length Annotations',
|
||||
text: 'Use the length tool on the viewport to measure the length of a region.',
|
||||
title: i18n.t('Onboarding:Drawing Length Annotations'),
|
||||
text: i18n.t('Onboarding:Use the length tool on the viewport to measure the length of a region.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'right',
|
||||
@@ -108,8 +110,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'trackMeasurement',
|
||||
title: 'Tracking Measurements in the Panel',
|
||||
text: 'Click yes to track the measurements in the measurement panel.',
|
||||
title: i18n.t('Onboarding:Tracking Measurements in the Panel'),
|
||||
text: i18n.t('Onboarding:Click yes to track the measurements in the measurement panel.'),
|
||||
attachTo: {
|
||||
element: '[data-cy="prompt-begin-tracking-yes-btn"]',
|
||||
on: 'bottom',
|
||||
@@ -122,8 +124,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'openMeasurementPanel',
|
||||
title: 'Opening the Measurements Panel',
|
||||
text: 'Click the measurements button to open the measurements panel.',
|
||||
title: i18n.t('Onboarding:Opening the Measurements Panel'),
|
||||
text: i18n.t('Onboarding:Click the measurements button to open the measurements panel.'),
|
||||
attachTo: {
|
||||
element: '#trackedMeasurements-btn',
|
||||
on: 'left-start',
|
||||
@@ -136,8 +138,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'scrollAwayFromMeasurement',
|
||||
title: 'Scrolling Away from a Measurement',
|
||||
text: 'Scroll the images using the mouse wheel away from the measurement.',
|
||||
title: i18n.t('Onboarding:Scrolling Away from a Measurement'),
|
||||
text: i18n.t('Onboarding:Scroll the images using the mouse wheel away from the measurement.'),
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'left',
|
||||
@@ -150,8 +152,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'jumpToMeasurement',
|
||||
title: 'Jumping to Measurements in the Panel',
|
||||
text: 'Click the measurement in the measurement panel to jump to it.',
|
||||
title: i18n.t('Onboarding:Jumping to Measurements in the Panel'),
|
||||
text: i18n.t('Onboarding:Click the measurement in the measurement panel to jump to it.'),
|
||||
attachTo: {
|
||||
element: '[data-cy="data-row"]',
|
||||
on: 'left-start',
|
||||
@@ -164,8 +166,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'changeLayout',
|
||||
title: 'Changing Layout',
|
||||
text: 'You can change the layout of the viewer using the layout button.',
|
||||
title: i18n.t('Onboarding:Changing Layout'),
|
||||
text: i18n.t('Onboarding:You can change the layout of the viewer using the layout button.'),
|
||||
attachTo: {
|
||||
element: '[data-cy="Layout"]',
|
||||
on: 'bottom',
|
||||
@@ -178,8 +180,8 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'selectLayout',
|
||||
title: 'Selecting the MPR Layout',
|
||||
text: 'Select the MPR layout to view the images in MPR mode.',
|
||||
title: i18n.t('Onboarding:Selecting the MPR Layout'),
|
||||
text: i18n.t('Onboarding:Select the MPR layout to view the images in MPR mode.'),
|
||||
attachTo: {
|
||||
element: '[data-cy="MPR"]',
|
||||
on: 'left-start',
|
||||
@@ -196,7 +198,7 @@ export default {
|
||||
defaultStepOptions: {
|
||||
buttons: [
|
||||
{
|
||||
text: 'Skip all',
|
||||
text: i18n.t('Onboarding:Skip all'),
|
||||
action() {
|
||||
this.complete();
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
import { useSystem, hotkeys as hotkeysModule } from '@ohif/core';
|
||||
import { UserPreferencesModal, FooterAction } from '@ohif/ui-next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -19,14 +19,40 @@ interface HotkeyDefinitions {
|
||||
|
||||
function UserPreferencesModalDefault({ hide }: { hide: () => void }) {
|
||||
const { hotkeysManager } = useSystem();
|
||||
const { t } = useTranslation('UserPreferencesModal');
|
||||
const { t, i18n: i18nextInstance } = useTranslation('UserPreferencesModal');
|
||||
|
||||
const { hotkeyDefinitions = {}, hotkeyDefaults = {} } = hotkeysManager;
|
||||
|
||||
const fallbackHotkeyDefinitions = useMemo(
|
||||
() =>
|
||||
hotkeysManager.getValidHotkeyDefinitions(
|
||||
hotkeysModule.defaults.hotkeyBindings
|
||||
) as HotkeyDefinitions,
|
||||
[hotkeysManager]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!Object.keys(hotkeyDefaults).length) {
|
||||
hotkeysManager.setDefaultHotKeys(hotkeysModule.defaults.hotkeyBindings);
|
||||
}
|
||||
|
||||
if (!Object.keys(hotkeyDefinitions).length) {
|
||||
hotkeysManager.setHotkeys(fallbackHotkeyDefinitions);
|
||||
}
|
||||
}, [hotkeysManager, hotkeyDefaults, hotkeyDefinitions, fallbackHotkeyDefinitions]);
|
||||
|
||||
const resolvedHotkeyDefaults = Object.keys(hotkeyDefaults).length
|
||||
? (hotkeyDefaults as HotkeyDefinitions)
|
||||
: fallbackHotkeyDefinitions;
|
||||
|
||||
const initialHotkeyDefinitions = Object.keys(hotkeyDefinitions).length
|
||||
? (hotkeyDefinitions as HotkeyDefinitions)
|
||||
: resolvedHotkeyDefaults;
|
||||
|
||||
const currentLanguage = currentLanguageFn();
|
||||
|
||||
const [state, setState] = useState({
|
||||
hotkeyDefinitions: hotkeyDefinitions as HotkeyDefinitions,
|
||||
hotkeyDefinitions: initialHotkeyDefinitions,
|
||||
languageValue: currentLanguage.value,
|
||||
});
|
||||
|
||||
@@ -51,12 +77,52 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) {
|
||||
setState(state => ({
|
||||
...state,
|
||||
languageValue: defaultLanguage.value,
|
||||
hotkeyDefinitions: hotkeyDefaults as HotkeyDefinitions,
|
||||
hotkeyDefinitions: resolvedHotkeyDefaults,
|
||||
}));
|
||||
|
||||
hotkeysManager.restoreDefaultBindings();
|
||||
};
|
||||
|
||||
const displayNames = React.useMemo(() => {
|
||||
if (typeof Intl === 'undefined' || typeof Intl.DisplayNames !== 'function') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const locales = [state.languageValue, currentLanguage.value, i18nextInstance.language, 'en'];
|
||||
const uniqueLocales = Array.from(new Set(locales.filter(Boolean)));
|
||||
|
||||
try {
|
||||
return new Intl.DisplayNames(uniqueLocales, { type: 'language', fallback: 'none' });
|
||||
} catch (error) {
|
||||
console.warn('Intl.DisplayNames not supported for locales', uniqueLocales, error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [state.languageValue, currentLanguage.value, i18nextInstance.language]);
|
||||
|
||||
const getLanguageLabel = React.useCallback(
|
||||
(languageValue: string, fallbackLabel: string) => {
|
||||
const translationKey = `LanguageName.${languageValue}`;
|
||||
if (i18nextInstance.exists(translationKey, { ns: 'UserPreferencesModal' })) {
|
||||
return t(translationKey);
|
||||
}
|
||||
|
||||
if (displayNames) {
|
||||
try {
|
||||
const localized = displayNames.of(languageValue);
|
||||
if (localized && localized.toLowerCase() !== languageValue.toLowerCase()) {
|
||||
return localized.charAt(0).toUpperCase() + localized.slice(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug(`Unable to resolve display name for ${languageValue}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackLabel;
|
||||
},
|
||||
[displayNames, i18nextInstance, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<UserPreferencesModal>
|
||||
<UserPreferencesModal.Body>
|
||||
@@ -79,7 +145,7 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) {
|
||||
key={lang.value}
|
||||
value={lang.value}
|
||||
>
|
||||
{lang.label}
|
||||
{getLanguageLabel(lang.value, lang.label)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -120,6 +186,9 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) {
|
||||
onClick={() => {
|
||||
if (state.languageValue !== currentLanguage.value) {
|
||||
i18n.changeLanguage(state.languageValue);
|
||||
// Force page reload after language change to ensure all translations are applied
|
||||
window.location.reload();
|
||||
return; // Exit early since we're reloading
|
||||
}
|
||||
hotkeysManager.setHotkeys(state.hotkeyDefinitions);
|
||||
hotkeysModule.stopRecord();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Types } from '@ohif/core';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const defaultDisplaySetSelector = {
|
||||
studyMatchingRules: [
|
||||
@@ -113,8 +114,8 @@ const priorViewport1 = {
|
||||
*/
|
||||
const hpMNCompare: Types.HangingProtocol.Protocol = {
|
||||
id: '@ohif/hpCompare',
|
||||
description: 'Compare two studies in various layouts',
|
||||
name: 'Compare Two Studies',
|
||||
description: i18n.t('Hps:Compare two studies in various layouts'),
|
||||
name: i18n.t('Hps:Compare Two Studies'),
|
||||
numberOfPriorsReferenced: 1,
|
||||
protocolMatchingRules: [
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
RMLOPrior,
|
||||
LMLOPrior,
|
||||
} from './utils/mammoDisplaySetSelector';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const rightDisplayArea = {
|
||||
storeAsInitialCamera: true,
|
||||
@@ -30,7 +31,7 @@ const leftDisplayArea = {
|
||||
const hpMammography = {
|
||||
id: '@ohif/hpMammo',
|
||||
hasUpdatedPriorsInformation: false,
|
||||
name: 'Mammography Breast Screening',
|
||||
name: i18n.t('Hps:Mammography Breast Screening'),
|
||||
protocolMatchingRules: [
|
||||
{
|
||||
id: 'Mammography',
|
||||
|
||||
Reference in new issue
Block a user