diff --git a/docs/latest/essentials/translating.md b/docs/latest/essentials/translating.md index f14e2353c..459567a3a 100644 --- a/docs/latest/essentials/translating.md +++ b/docs/latest/essentials/translating.md @@ -1,154 +1,215 @@ # Translating -OHIF supports internationalization using [i18next](https://www.i18next.com/) through the npm package [ohif-i18n](https://www.npmjs.com/package/ohif-i18n), where is the main instance of i18n containing several languages and tools. + +OHIF supports internationalization using [i18next](https://www.i18next.com/) +through the npm package [@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n), +where is the main instance of i18n containing several languages and tools. ### Installing + ```bash -yarn add ohif-i18n +yarn add @ohif/i18n # OR -npm install --save ohif-i18n +npm install --save @ohif/i18n ``` ### How it works -After installing `ohif-i18n` npm package, the translation function [t](https://www.i18next.com/overview/api#t) can be used [with](#with-react) or [without](#without-react) React. -A translation will occur every time a text match happens in a [t](https://www.i18next.com/overview/api#t) function. +After installing `@ohif/i18n` npm package, the translation function +[t](https://www.i18next.com/overview/api#t) can be used [with](#with-react) or +[without](#without-react) React. -The [t](https://www.i18next.com/overview/api#t) function is responsible for getting translations using all the power of i18next. +A translation will occur every time a text match happens in a +[t](https://www.i18next.com/overview/api#t) function. + +The [t](https://www.i18next.com/overview/api#t) function is responsible for +getting translations using all the power of i18next. E.g. Before: -````html -
my translated text
-```` -After: -````html -
{t('my translated text')}
-```` -If the translation.json file contains a key that matches the HTML content e.g. `my translated text`, it will be replaced automatically by the [t](https://www.i18next.com/overview/api#t) function. +```html +
my translated text
+``` + +After: + +```html +
{t('my translated text')}
+``` + +If the translation.json file contains a key that matches the HTML content e.g. +`my translated text`, it will be replaced automatically by the +[t](https://www.i18next.com/overview/api#t) function. --- + #### With React -This section will introduce you to [react-i18next](https://react.i18next.com/) basics and show how to implement the [t](https://www.i18next.com/overview/api#t) function easily. + +This section will introduce you to [react-i18next](https://react.i18next.com/) +basics and show how to implement the [t](https://www.i18next.com/overview/api#t) +function easily. ##### Using HOCs -In most cases we used [High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to get the `t` tool between OHIF's components. -E.g. - -```js -import React from 'react'; -import { withTranslation } from 'ohif-i18n'; - -function MyComponent({ t, i18n }) { - return

{t('my translated text')}

-} - -export default withTranslation('MyNameSpace')(MyComponent); -``` -> Important: if you are using React outside the OHIF Viewer, check the [I18nextProvider](#using-outside-of-ohif-viewer) section, `withTranslation` HOC doesnt works without a I18nextProvider - -##### Using Hooks -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. - - -#### 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`; - -But, if you need to use it completely outside of OHIF viewer, you can set the I18nextProvider this way: - -```js -import i18n, { I18nextProvider } from 'ohif-i18n'; -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. - ----- - -#### Without React -When needed, you can also use available translations *without React*. +In most cases we used +[High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to +get the `t` tool between OHIF's components. E.g. ```js -import { t } from 'ohif-i18n'; -console.log( t('my translated text') ); +import React from 'react'; +import { withTranslation } from '@ohif/i18n'; + +function MyComponent({ t, i18n }) { + return

{t('my translated text')}

; +} + +export default withTranslation('MyNameSpace')(MyComponent); +``` + +> Important: if you are using React outside the OHIF Viewer, check the +> [I18nextProvider](#using-outside-of-ohif-viewer) section, `withTranslation` +> HOC doesnt works without a I18nextProvider + +##### Using Hooks + +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. + +#### 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`; + +But, if you need to use it completely outside of OHIF viewer, you can set the +I18nextProvider this way: + +```js +import i18n, { I18nextProvider } from '@ohif/i18n'; +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. + +--- + +#### Without React + +When needed, you can also use available translations _without React_. + +E.g. + +```js +import { t } from '@ohif/i18n'; +console.log(t('my translated text')); ``` --- # Main Concepts While Translating -### - Namespaces -Namespaces are being used to organize translations in smaller portions, combined semantically or by use. -Each `.json` file inside `ohif-i18n` npm package becomes a new namespace automatically. +### - Namespaces +Namespaces are being used to organize translations in smaller portions, combined +semantically or by use. Each `.json` file inside `@ohif/i18n` npm package +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)')` - Header: translations related to OHIF's Header Top Bar -### - Extending Languages in ohif-i18n -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 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. + +To extend a language, create a new folder inside a language with two characters +as 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 (.json file) will be replaced in the new sub +language, e.g en-US, en-UK, es-AR, es-MX, etc. #### - 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. +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. E.g. + ```js -import { i18n } from 'ohif-i18n'; +import { i18n } from '@ohif/i18n'; i18next.addResourceBundle('pt-BR', 'Buttons', { - 'Angle': 'Ângulo' + Angle: 'Ângulo', }); ``` ------- + +--- ### How to set a whole new language + 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 -To set it apart of `ohif-i18n`, follow this snippet: +- 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 + +To set it apart of `@ohif/i18n`, follow this snippet: + +File: myJsonFileWithLanguage.json // TODO - This example is a working in +progress -File: myJsonFileWithLanguage.json // TODO - This example is a working in progress ```json { - "prop1": "value1", - "prop2": "value2", - "prop3": "value3", - "prop4": "value4" + "en": { + "ns": { + "prop1": "value1", + "prop2": "value2", + "prop3": "value3", + "prop4": "value4" + } + } } ``` ```js -import { extendLanguage } from 'ohif-i18n'; +import { extendLanguage } from '@ohif/i18n'; import myJsonFileWithLanguage from './myJsonFileWithLanguage.json'; extendLanguage(myJsonFileWithLanguage); // TODO - This example is a working in progress ``` +## Debugging translations -#Debugging translations -#TODO - WIP +There are two environment variables responsible for debugging the translations: +`REACT_APP_I18N_DEBUG` and `REACT_APP_LANG`. + +For debugging, you can run the project as following: + +```bash +yarn; REACT_APP_I18N_DEBUG=true REACT_APP_LANG=es-MX yarn run dev +``` ### Contributing with new languages -Contributions of any kind are welcome! Please check the [instructions](https://docs.ohif.org/contributing.html). +Contributions of any kind are welcome! Please check the +[instructions](https://docs.ohif.org/contributing.html). diff --git a/extensions/ohif-i18n/package.json b/extensions/ohif-i18n/package.json index 90059c7f8..8bdc9a1af 100644 --- a/extensions/ohif-i18n/package.json +++ b/extensions/ohif-i18n/package.json @@ -17,9 +17,9 @@ "prepublishOnly": "npm run build" }, "peerDependencies": { + "i18next": "^15.1.3", "react": "^16.0.0", "react-dom": "^16.0.0", - "i18next": "^15.1.3", "react-i18next": "^10.11.0" }, "devDependencies": { @@ -51,7 +51,8 @@ "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-peer-deps-external": "^2.2.0", "rollup-plugin-postcss": "^2.0.3", - "rollup-plugin-url": "^2.1.0" + "rollup-plugin-url": "^2.1.0", + "i18next-browser-languagedetector": "^3.0.1" }, "husky": { "hooks": { diff --git a/extensions/ohif-i18n/src/index.js b/extensions/ohif-i18n/src/index.js index 41d7362b8..69b2b9f9d 100755 --- a/extensions/ohif-i18n/src/index.js +++ b/extensions/ohif-i18n/src/index.js @@ -1,5 +1,6 @@ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; +import LngDetector from 'i18next-browser-languagedetector'; const currentLanguage = process.env.REACT_APP_LANG || 'en-US'; const debugMode = !!( @@ -63,6 +64,7 @@ function addLocales(context) { let translate; i18n + .use(LngDetector) .use(initReactI18next) .init({ resources: getLocales(), diff --git a/extensions/ohif-i18n/src/locales/en/Header.json b/extensions/ohif-i18n/src/locales/en/Header.json index eefaf4542..c373fcb67 100644 --- a/extensions/ohif-i18n/src/locales/en/Header.json +++ b/extensions/ohif-i18n/src/locales/en/Header.json @@ -1,5 +1,5 @@ { - "INVESTIGATIONAL USE ONLY": "Preferences", + "INVESTIGATIONAL USE ONLY": "INVESTIGATIONAL USE ONLY", "Options": "Options", "About": "About", "Preferences": "Preferences", diff --git a/extensions/ohif-i18n/yarn.lock b/extensions/ohif-i18n/yarn.lock index 6514cfc50..91116e353 100644 --- a/extensions/ohif-i18n/yarn.lock +++ b/extensions/ohif-i18n/yarn.lock @@ -2404,6 +2404,11 @@ husky@^1.3.1: run-node "^1.0.0" slash "^2.0.0" +i18next-browser-languagedetector@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-3.0.1.tgz#a47c43176e8412c91e808afb7c6eb5367649aa8e" + integrity sha512-WFjPLNPWl62uu07AHY2g+KsC9qz0tyMq+OZEB/H7N58YKL/JLiCz9U709gaR20Mule/Ppn+uyfVx5REJJjn1HA== + i18next@^15.1.3: version "15.1.3" resolved "https://registry.yarnpkg.com/i18next/-/i18next-15.1.3.tgz#f1984cbee0e3cb00cff9008b037264289ce8840a" diff --git a/extensions/ohif-vtk-extension/package.json b/extensions/ohif-vtk-extension/package.json index ea8ccfcbd..492862ec7 100644 --- a/extensions/ohif-vtk-extension/package.json +++ b/extensions/ohif-vtk-extension/package.json @@ -41,7 +41,6 @@ "@babel/plugin-transform-runtime": "^7.2.0", "@babel/preset-env": "^7.2.3", "@babel/preset-react": "^7.0.0", - "@ohif/i18n": "0.0.1-rc-001", "babel-eslint": "^10.0.1", "cross-env": "^5.2.0", "eslint": "5.13.0", @@ -58,6 +57,7 @@ "rollup": "^1.1.2", "rollup-plugin-babel": "^4.2.0", "rollup-plugin-commonjs": "^9.2.0", + "rollup-plugin-copy": "^2.0.1", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-peer-deps-external": "^2.2.0", @@ -93,7 +93,6 @@ }, "dependencies": { "@babel/runtime": "^7.2.0", - "@ohif/extension-vtk": "file:.yalc/@ohif/extension-vtk", "classnames": "^2.2.6", "react-vtkjs-viewport": "^0.0.7" } diff --git a/extensions/ohif-vtk-extension/rollup.config.js b/extensions/ohif-vtk-extension/rollup.config.js index dcbddc988..66a4b2b0d 100644 --- a/extensions/ohif-vtk-extension/rollup.config.js +++ b/extensions/ohif-vtk-extension/rollup.config.js @@ -6,6 +6,7 @@ import pkg from './package.json'; import postcss from 'rollup-plugin-postcss'; import resolve from 'rollup-plugin-node-resolve'; import url from 'rollup-plugin-url'; +import copy from 'rollup-plugin-copy'; // Deal with https://github.com/rollup/rollup-plugin-commonjs/issues/297 @@ -24,7 +25,8 @@ const globals = { dcmjs: 'dcmjs', 'dicom-parser': 'dicomParser', 'ohif-core': 'OHIF', - hammerjs: 'Hammer' + hammerjs: 'Hammer', + '@ohif/i18n': 'i18n' }; export default { @@ -56,6 +58,10 @@ export default { externalHelpers: true, runtimeHelpers: true }), + copy({ + targets: ['src/locales'], + outputFolder: 'dist', + }), resolve(), commonjs({ include: ['node_modules/**', '.yalc/**'], diff --git a/extensions/ohif-vtk-extension/yarn.lock b/extensions/ohif-vtk-extension/yarn.lock index 26c0bb834..a109a0e37 100644 --- a/extensions/ohif-vtk-extension/yarn.lock +++ b/extensions/ohif-vtk-extension/yarn.lock @@ -727,21 +727,6 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@ohif/extension-vtk@file:.yalc/@ohif/extension-vtk": - version "0.0.3-9acf58f8" - dependencies: - "@babel/runtime" "^7.2.0" - classnames "^2.2.6" - react-vtkjs-viewport "^0.0.7" - -"@ohif/i18n@0.0.1-rc-001": - version "0.0.1-rc-001" - resolved "https://registry.yarnpkg.com/@ohif/i18n/-/i18n-0.0.1-rc-001.tgz#47286daa1411efb5f0219f0310484c8919bfe607" - integrity sha512-CJ9wqd15DpJ6adVMyXkyqQqcRZgSnMWJ7L3WyUpMqf66CvAFKzCUuckyiFeCupIq8ZGv7w4jaADSYKrLcpviUw== - dependencies: - "@babel/runtime" "^7.2.0" - classnames "^2.2.6" - "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -3009,7 +2994,7 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@^7.0.0: +fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -3978,6 +3963,13 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== + dependencies: + isobject "^4.0.0" + is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" @@ -4091,6 +4083,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -7056,6 +7053,15 @@ rollup-plugin-commonjs@^9.2.0: resolve "^1.8.1" rollup-pluginutils "^2.3.3" +rollup-plugin-copy@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-2.0.1.tgz#2a45e45be84a0d4054d1774604044e7f7581fdae" + integrity sha512-IE+Ob5k1Pi/DLi8SlFcf9jN1m2p0ovIwMuz0GM5NeQeV14puW/bCUpmXlqVtQVy+ErXOhZEeBaqinJ0xnJdxKg== + dependencies: + chalk "^2.4.2" + fs-extra "^7.0.1" + is-plain-object "^3.0.0" + rollup-plugin-node-builtins@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz#24a1fed4a43257b6b64371d8abc6ce1ab14597e9" diff --git a/package.json b/package.json index aecf4cab9..5eb2012f2 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "@babel/runtime": "^7.2.0", "@ohif/extension-cornerstone": "0.0.34", "@ohif/extension-dicom-microscopy": "0.0.6", - "@ohif/extension-vtk": "0.0.2", - "@ohif/i18n": "file:.yalc/@ohif/i18n", + "@ohif/extension-vtk": "file:.yalc/@ohif/extension-vtk", + "@ohif/i18n": "0.0.1-rc-001", "classnames": "^2.2.6", "cornerstone-core": "^2.2.8", "cornerstone-math": "^0.1.8", diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 8b0c2d7cf..1fe1adcc0 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Link, withRouter } from 'react-router-dom'; import { Dropdown } from 'react-viewerbase'; -import i18n from '@ohif/i18n'; import { withTranslation } from 'react-i18next'; import './Header.css'; import OHIFLogo from '../OHIFLogo/OHIFLogo.js'; @@ -43,21 +42,13 @@ class Header extends Component { { title: t('About'), icon: { - name: 'info' + name: 'info', }, link: 'http://ohif.org', }, ]; } - changeLanguage(language) { - i18n.init({ - fallbackLng: language.substring(0, 2), - lng: language, - }); - this.loadOptions(); - } - render() { const { t } = this.props; return ( @@ -89,30 +80,6 @@ class Header extends Component {
{t('INVESTIGATIONAL USE ONLY')} - - - -
diff --git a/yarn.lock b/yarn.lock index 2397e59bc..09b838efb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1033,10 +1033,8 @@ classnames "^2.2.6" dicom-microscopy-viewer "^0.4.3" -"@ohif/extension-vtk@0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@ohif/extension-vtk/-/extension-vtk-0.0.2.tgz#99f0ea796ddae14d7e64cff958c0656b5d964f4e" - integrity sha512-WDuQVwK4RlHgXwbmKIECZrP90x8cVOwESQz8bJOeNG2eVPDBVlNWx/zOXShAhn6J62QG6Sn1RnHQWLedgGnhyA== +"@ohif/extension-vtk@file:.yalc/@ohif/extension-vtk": + version "0.0.3-3bee9176" dependencies: "@babel/runtime" "^7.2.0" classnames "^2.2.6" @@ -1050,12 +1048,6 @@ "@babel/runtime" "^7.2.0" classnames "^2.2.6" -"@ohif/i18n@file:.yalc/@ohif/i18n": - version "0.0.2-a44efe70" - dependencies: - "@babel/runtime" "^7.2.0" - classnames "^2.2.6" - "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -11797,7 +11789,7 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0: react-lifecycles-compat "^3.0.4" "react-viewerbase@file:.yalc/react-viewerbase": - version "0.2.17-bffd4836" + version "0.2.17-0cc70fd6" dependencies: "@babel/runtime" "7.2.0" "@ohif/i18n" "0.0.1-rc-001"