diff --git a/docs/latest/essentials/translating.md b/docs/latest/essentials/translating.md index 459567a3a..33f3c5391 100644 --- a/docs/latest/essentials/translating.md +++ b/docs/latest/essentials/translating.md @@ -56,7 +56,7 @@ function easily. In most cases we used [High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to -get the `t` tool between OHIF's components. +share the `t` function among OHIF's components. E.g. @@ -79,15 +79,14 @@ export default withTranslation('MyNameSpace')(MyComponent); Also, it's possible to get the `t` tool using [React Hooks](https://react.i18next.com/latest/usetranslation-hook), but it -requires at least React > 16.8. +requires at least React > 16.8 馃槈 #### Using outside of OHIF viewer OHIF Viewer already sets a main [I18nextProvider](https://react.i18next.com/latest/i18nextprovider) connected to -the shared i18n instance from `@ohif/i18n`, all extensions inside the Viewer -will share this same provider at the end, you don't need a provider when -developing a react Extension if you use `@ohif/i18n`; +the shared i18n instance from `@ohif/i18n`, all extensions inside OHIF Viewer +will share this same provider at the end, you don't need to set new providers at all. But, if you need to use it completely outside of OHIF viewer, you can set the I18nextProvider this way: @@ -102,7 +101,7 @@ import App from './App'; ``` After setting `I18nextProvider` in your React App, all translations from -`@ohif/i18n` should be available following [With React](#with-react) usage. +`@ohif/i18n` should be available following the basic [With React](#with-react) usage. --- @@ -115,13 +114,14 @@ E.g. ```js import { t } from '@ohif/i18n'; console.log(t('my translated text')); +console.log(t('$t(Common:Play) my translated text')); ``` --- # Main Concepts While Translating -### - Namespaces +## - Namespaces Namespaces are being used to organize translations in smaller portions, combined semantically or by use. Each `.json` file inside `@ohif/i18n` npm package @@ -129,26 +129,36 @@ becomes a new namespace automatically. - Buttons: All buttons translations - CineDialog: Translations for the toll tips inside the Cine Player Dialog -- common: all common jargons that can be reused like `t('$t(common:image)')` +- Common: all common jargons that can be reused like `t('$t(common:image)')` - Header: translations related to OHIF's Header Top Bar +- MeasurementTable - Translations for the react-viewerbase Measurement Table +- UserPreferencesModal - Translations for the react-viewerbase Preferences Modal -### - Extending Languages in @ohif/i18n +### How to use another NameSpace inside the current NameSpace? +i18next provides a parsing feature able to get translations strings from any NameSpace, + like this following example getting data from `Common` NameSpace: +``` +$t(Common:Reset) +``` -Sometimes, even in the same language, some nouns or jargons can change in -different countries, states or even from Hospital to Hospital, in this cases, we -can extend languages. +## - Extending Languages in @ohif/i18n -To extend a language, create a new folder inside a language with two characters -as name, like the `UK` in the following file tree: +Sometimes, even using the same language, some nouns or jargons can change according to +the country, states or even from Hospital to Hospital. + +In this cases, you don't need to set an entire language again, you can extend languages creating a new folder inside a pre existent language folder and @ohif/i18n will do the hard work. + +This new folder must to be called with a double character name, like the `UK` in the following file tree: Files Tree for Extending Purpouses -All properties inside a Namespace (.json file) will be replaced in the new sub -language, e.g en-US, en-UK, es-AR, es-MX, etc. +All properties inside a Namespace will be merged in the new sub language, e.g `en-US` and `en-UK` will merge the props with `en`. -#### - Extending languages dynamically +This feature is based on i18next's fallback languages tool. -Once you have access to the i18n instance, you can use the +### - Extending languages dynamically + +Once you have access to the i18n instance, you can use the [addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init) method to add and change language resources. @@ -168,45 +178,74 @@ i18next.addResourceBundle('pt-BR', 'Buttons', { To set a brand new language you can do it in two different ways: - Opening a pull request for `@ohif/i18n` and sharing the translation with the - community. Please see [Contributing](#contributing-with-new-languages) section - for further information -- Setting it only in your project or extension + community. 馃槏 Please see [Contributing](#contributing-with-new-languages) section + for further information. + +- Setting it only in your project or extension: -To set it apart of `@ohif/i18n`, follow this snippet: +You'll need a folder structure like the following, which you can load using the `node context` and send it to `addLocales` method. -File: myJsonFileWithLanguage.json // TODO - This example is a working in -progress - -```json -{ - "en": { - "ns": { - "prop1": "value1", - "prop2": "value2", - "prop3": "value3", - "prop4": "value4" - } - } -} +Folder structure: +```bash + |-- ... + |-- src + |-- locales + |-- en + |-- Buttons.json + |-- es + | CO + |-- Buttons.js + |-- Buttons.json + ... ``` +E.g. of `addLocales` usage ```js -import { extendLanguage } from '@ohif/i18n'; -import myJsonFileWithLanguage from './myJsonFileWithLanguage.json'; +import { addLocales } from '@ohif/i18n'; -extendLanguage(myJsonFileWithLanguage); -// TODO - This example is a working in progress +const localesPath = './locales'; +const context = require.context(localesPath, true, /\.json$/); +addLocales(context); ``` +Also, [i18next](https://www.i18next.com/how-to/add-or-load-translations#add-after-init) provides a few methods to deal with languages, you have access to it's instance importing the default of @ohif/i18n; +Fell fre to play around with i18next like this: + +``` +import i18next from '@ohif/i18n'; + +i18next.addResourceBundle('en', 'namespace1', { + key: 'hello from namespace 1' +}); + +``` + +--- + +## language Detections +@ohif/i18n uses [i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector) to manage detections, also exports a method called initI18n that accepts a new detector config as parameter. + +### Changing the language +OHIF Viewer accepts a query param called `lng` in the url to change the language. + +E.g. +``` +https://docs.ohif.org/demo/?lng=es-MX +``` + +### Language Persistence +The user's language preference is kept automatically by the detector and stored at a cookie called 'i18next', and in a localstorage key called 'i18nextLng'. +These names can be changed with a new [Detector Config](https://github.com/i18next/i18next-browser-languageDetector). + + ## Debugging translations -There are two environment variables responsible for debugging the translations: -`REACT_APP_I18N_DEBUG` and `REACT_APP_LANG`. +There is an environment variable responsible for debugging the translations, called `REACT_APP_I18N_DEBUG`. -For debugging, you can run the project as following: +Run the project as following to get full debug information: ```bash -yarn; REACT_APP_I18N_DEBUG=true REACT_APP_LANG=es-MX yarn run dev +REACT_APP_I18N_DEBUG=true yarn run dev ``` ### Contributing with new languages diff --git a/extensions/ohif-i18n/src/debugger.js b/extensions/ohif-i18n/src/debugger.js index 8fd2f6ef6..9b6881ee5 100644 --- a/extensions/ohif-i18n/src/debugger.js +++ b/extensions/ohif-i18n/src/debugger.js @@ -3,6 +3,6 @@ import { debugMode } from './config'; export default (message, level = 'log') => { if (debugMode) { // eslint-disable-next-line - console[level](message); + console[level]('@ohif/i18n: ', message); } }; diff --git a/extensions/ohif-i18n/src/index.js b/extensions/ohif-i18n/src/index.js index f244d5be1..8214b013f 100755 --- a/extensions/ohif-i18n/src/index.js +++ b/extensions/ohif-i18n/src/index.js @@ -69,8 +69,8 @@ function initI18n(detection = detectionOptions) { escapeValue: false, }, detection, - fallbackNS: ['common'], - defaultNS: 'common', + fallbackNS: ['Common'], + defaultNS: 'Common', react: { wait: true, }, @@ -81,7 +81,7 @@ function initI18n(detection = detectionOptions) { }); } -customDebug(`@ohif/i18n version ${pkg.version} loaded.`, 'info'); +customDebug(`version ${pkg.version} loaded.`, 'info'); initI18n(); diff --git a/extensions/ohif-i18n/src/locales/en/Buttons.json b/extensions/ohif-i18n/src/locales/en/Buttons.json index 2725ce65b..b8f722f92 100644 --- a/extensions/ohif-i18n/src/locales/en/Buttons.json +++ b/extensions/ohif-i18n/src/locales/en/Buttons.json @@ -1,11 +1,11 @@ { "Themes": "Themes", - "Previous": "Previous", - "Next": "Next", - "Play": "Play", - "Stop": "Stop", - "Layout": "Layout", - "More": "More", + "Previous": "$t(Common:Previous)", + "Next": "$t(Common:Next)", + "Play": "$t(Common:Play)", + "Stop": "$t(Common:Stop)", + "Layout": "$t(Common:Layout)", + "More": "$t(Common:More)", "Crosshairs": "Crosshairs", "Magnify": "Magnify", "ROI Window": "ROI Window", @@ -31,9 +31,15 @@ "EllipticalRoi": "EllipticalRoi", "CircleRoi": "CircleRoi", "RectangleRoi": "RectangleRoi", - "Reset": "Reset", + "Reset": "$t(Common:Reset)", + "Reset 2": "$t(Common:Reset) 2", "CINE": "CINE", "Acquired": "Acquired", "Sagittal": "Sagittal", - "Axial": "Axial" + "Axial": "Axial", + "Manual": "Manual", + "Manual 2": "Manual 2", + "Save": "Save", + "Reset to Defaults": "$t(Common:Reset) to Defaults", + "Cancel": "Cancel" } diff --git a/extensions/ohif-i18n/src/locales/en/Common.json b/extensions/ohif-i18n/src/locales/en/Common.json new file mode 100755 index 000000000..42c203b0f --- /dev/null +++ b/extensions/ohif-i18n/src/locales/en/Common.json @@ -0,0 +1,10 @@ +{ + "Reset": "Reset", + "Previous": "Previous", + "Next": "Next", + "Play": "Play", + "Stop": "Stop", + "Layout": "Layout", + "More": "More", + "Image": "Image" +} diff --git a/extensions/ohif-i18n/src/locales/en/MeasurementTable.json b/extensions/ohif-i18n/src/locales/en/MeasurementTable.json new file mode 100644 index 000000000..e42616335 --- /dev/null +++ b/extensions/ohif-i18n/src/locales/en/MeasurementTable.json @@ -0,0 +1,9 @@ +{ + "Criteria nonconformities": "Criteria nonconformities", + "Relabel": "Relabel", + "Description": "Description", + "Delete": "Delete", + "Targets": "Targets", + "NonTargets": "NonTargets", + "MAX": "MAX" +} diff --git a/extensions/ohif-i18n/src/locales/en/UserPreferencesModal.json b/extensions/ohif-i18n/src/locales/en/UserPreferencesModal.json new file mode 100644 index 000000000..d7e792d2f --- /dev/null +++ b/extensions/ohif-i18n/src/locales/en/UserPreferencesModal.json @@ -0,0 +1,6 @@ +{ + "User Preferences": "User Preferences", + "Save": "$t(Buttons:Save)", + "Reset to Defaults": "$t(Buttons:Reset to Defaults)", + "Cancel": "$t(Buttons:Cancel)" +} diff --git a/extensions/ohif-i18n/src/locales/en/common.json b/extensions/ohif-i18n/src/locales/en/common.json deleted file mode 100755 index 5fdbf7431..000000000 --- a/extensions/ohif-i18n/src/locales/en/common.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Welcome to React": "Welcome to React and react-i18next" -} diff --git a/extensions/ohif-i18n/src/locales/es/Buttons.json b/extensions/ohif-i18n/src/locales/es/Buttons.json index 870f0cb9e..1b8fd5e60 100644 --- a/extensions/ohif-i18n/src/locales/es/Buttons.json +++ b/extensions/ohif-i18n/src/locales/es/Buttons.json @@ -1,11 +1,11 @@ { "Themes": "Temas", - "Previous": "Anterior", - "Next": "Pr贸ximo", - "Play": "Play", - "Stop": "Stop", - "Layout": "Esquema", - "More": "M谩s", + "Previous": "$t(Common:Previous)", + "Next": "$t(Common:Next)", + "Play": "$t(Common:Play)", + "Stop": "$t(Common:Stop)", + "Layout": "$t(Common:Layout)", + "More": "$t(Common:More)", "Crosshairs": "Cruces", "Magnify": "Lupa", "ROI Window": "Ventana ROI", @@ -32,8 +32,14 @@ "CircleRoi": "CircleRoi", "RectangleRoi": "RectangleRoi", "Reset": "Reiniciar", + "Reset 2": "Reiniciar 2", "CINE": "CINE", "Acquired": "Acquired", "Sagittal": "Sagittal", - "Axial": "Axial" + "Axial": "Axial", + "Manual": "Manual", + "Manual 2": "Manual 2", + "Save": "Guardar", + "Reset to Defaults": "$t(Common:reset) por defectos", + "Cancel": "Cancelar" } diff --git a/extensions/ohif-i18n/src/locales/es/CineDialog.json b/extensions/ohif-i18n/src/locales/es/CineDialog.json index 6073c562e..1126f7ed0 100644 --- a/extensions/ohif-i18n/src/locales/es/CineDialog.json +++ b/extensions/ohif-i18n/src/locales/es/CineDialog.json @@ -1,8 +1,8 @@ { "fps": "fps", - "Skip to first image": "Avanza para la primera imagen", - "Previous image": "Imagen anterior", - "Play / Stop": "Play / Stop", - "Next image": "Pr贸ximo image", - "Skip to last image": "Pular para la ultima imagen" + "Skip to first image": "Avanza para la primera $t(Common:Image)", + "Previous image": "$t(Common:Image) $t(Common:Previous)", + "Play / Stop": "$t(Common:Play) / $t(Common:Stop)", + "Next image": "$t(Common:Play) $t(Common:Image)", + "Skip to last image": "Pular para la ultima $t(Common:Image)" } diff --git a/extensions/ohif-i18n/src/locales/es/Common.json b/extensions/ohif-i18n/src/locales/es/Common.json new file mode 100755 index 000000000..458505e74 --- /dev/null +++ b/extensions/ohif-i18n/src/locales/es/Common.json @@ -0,0 +1,10 @@ +{ + "reset": "Reiniciar", + "Previous": "Anterior", + "Next": "Pr贸ximo", + "Play": "Play", + "Stop": "Stop", + "Layout": "Esquema", + "More": "M谩s", + "Image": "Imagen" +} diff --git a/extensions/ohif-i18n/src/locales/es/MeasurementTable.json b/extensions/ohif-i18n/src/locales/es/MeasurementTable.json new file mode 100644 index 000000000..8e3070696 --- /dev/null +++ b/extensions/ohif-i18n/src/locales/es/MeasurementTable.json @@ -0,0 +1,11 @@ +{ + "Criteria nonconformities": "Criterios de no conformidades", + "Relabel": "Reetiquetar", + "Description": "Descripci贸n", + "Delete": "Borrar", + "Targets": "Objetivos", + "NonTargets": "NonObjetivos", + "MAX": "m谩ximo", + "Chest Wall Posterior": "Pared pectoral posterior", + "Bone Extremity": "Extremidad 贸sea" +} diff --git a/extensions/ohif-i18n/src/locales/es/UserPreferencesModal.json b/extensions/ohif-i18n/src/locales/es/UserPreferencesModal.json new file mode 100644 index 000000000..aa3b4f442 --- /dev/null +++ b/extensions/ohif-i18n/src/locales/es/UserPreferencesModal.json @@ -0,0 +1,6 @@ +{ + "User Preferences": "Preferencias de usuario", + "Save": "$t(Buttons:Save)", + "Reset to Defaults": "$t(Buttons:Reset to Defaults)", + "Cancel": "$t(Buttons:Cancel)" +} diff --git a/extensions/ohif-i18n/src/locales/es/common.json b/extensions/ohif-i18n/src/locales/es/common.json deleted file mode 100755 index ea3a56575..000000000 --- a/extensions/ohif-i18n/src/locales/es/common.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Welcome to React": "Bienvenido a React" -}