diff --git a/package.json b/package.json index 29e775d09..9fb3da250 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "dicom-parser": "^1.8.3", "dicomweb-client": "^0.4.2", "hammerjs": "^2.0.8", + "i18next": "^15.1.3", "lodash.isequal": "4.5.0", "moment": "^2.24.0", "ohif-core": "0.5.5", @@ -88,6 +89,7 @@ "react-bootstrap-modal": "^4.2.0", "react-dnd": "^7.0.2", "react-dnd-html5-backend": "^7.0.2", + "react-i18next": "^10.11.0", "react-redux": "^6.0.0", "react-resize-detector": "^3.4.0", "react-router": "^4.3.1", diff --git a/src/App.js b/src/App.js index 7d3718fd3..8141279fc 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,7 @@ import { BrowserRouter as Router } from 'react-router-dom'; import { Provider } from 'react-redux'; import { createStore, combineReducers } from 'redux'; import PropTypes from 'prop-types'; +import { I18nextProvider } from 'react-i18next'; import OHIF from 'ohif-core'; import './config'; import ui from './redux/ui.js'; @@ -21,6 +22,7 @@ import { } from './utils/index.js'; import setupTools from './setupTools'; import ConnectedToolContextMenu from './connectedComponents/ConnectedToolContextMenu'; +import i18n from './i18n'; const { ExtensionManager } = OHIF.extensions; const { reducers, localStorage } = OHIF.redux; @@ -107,15 +109,19 @@ class App extends Component { if (userManager) { return ( - - - - - - - - - + + + + + + + + + + + ); } diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 010baa830..0dd96b529 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Link, withRouter } from 'react-router-dom'; import { Dropdown } from 'react-viewerbase'; +import { withTranslation } from 'react-i18next'; import './Header.css'; import OHIFLogo from '../OHIFLogo/OHIFLogo.js'; import ConnectedUserPreferencesModal from '../../connectedComponents/ConnectedUserPreferencesModal.js'; @@ -69,7 +70,9 @@ class Header extends Component {
- INVESTIGATIONAL USE ONLY + + INVESTIGATIONAL USE ONLY {this.props.t('header_test')} +
@@ -78,4 +81,4 @@ class Header extends Component { } } -export default withRouter(Header); +export default withTranslation('Header')(withRouter(Header)); diff --git a/src/i18n.js b/src/i18n.js new file mode 100755 index 000000000..b2fa0104e --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,73 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +const currentLanguage = process.env.APP_LANG || 'en-US'; + +function getDefaultLanguage() { + const mainLanguage = currentLanguage.match(/(.*.)(-)/); + return mainLanguage !== null ? mainLanguage[1] : null; +} + +function getCleanKeyForNameSpaces(key) { + const regexKeyCleaner = new RegExp( + `^(\\.\\/${currentLanguage}\\/)(.*.)(\\.json)$` + ); + const cleanedKey = key.match(regexKeyCleaner); + return cleanedKey !== null ? cleanedKey[2] : null; +} + +function getCleanKeyForDefaults(key) { + const regexKeyCleaner = new RegExp( + `^(\\.\\/${getDefaultLanguage()}\\/)(.*.)(\\.json)$` + ); + const cleanedKey = key.match(regexKeyCleaner); + return cleanedKey !== null ? cleanedKey[2] : null; +} + +function getLocales() { + const currentLangList = {}; + const defaultLangList = {}; + const locales = {}; + const context = require.context(`./locales`, true, /\.json$/); + + context.keys().forEach(key => { + const isTheSameOfCurrentLang = key.indexOf(currentLanguage) >= 0; + const isADefaultLang = key.indexOf(`${getDefaultLanguage()}/`) >= 0; + + if (isTheSameOfCurrentLang) { + currentLangList[getCleanKeyForNameSpaces(key)] = context(key); + } + + if (isADefaultLang) { + defaultLangList[getCleanKeyForDefaults(key)] = context(key); + } + }); + + locales[currentLanguage] = { ...currentLangList }; + locales[getDefaultLanguage()] = { ...defaultLangList }; + return locales; +} + +console.log(getLocales(), ' > ', getDefaultLanguage()); + +// console.log(getLocales(), currentLanguage, getDefaultLanguage()); + +i18n.use(initReactI18next).init({ + // resources: getLocales(), + resources: getLocales(), + fallbackLng: getDefaultLanguage(), + lng: currentLanguage, + debug: true, + + // have a common namespace used around the full app + keySeparator: false, // uses content as keys + interpolation: { + escapeValue: false, + }, + + react: { + wait: true, + }, +}); + +export default i18n; diff --git a/src/locales/en-UK/Header.json b/src/locales/en-UK/Header.json new file mode 100644 index 000000000..d4909843d --- /dev/null +++ b/src/locales/en-UK/Header.json @@ -0,0 +1,3 @@ +{ + "header_test": "Joao Grandao" +} diff --git a/src/locales/en-UK/common.json b/src/locales/en-UK/common.json new file mode 100755 index 000000000..f47a451d8 --- /dev/null +++ b/src/locales/en-UK/common.json @@ -0,0 +1,6 @@ +{ + "common": { + "Welcome to React": "Welcome to React and react-i18next", + "welcomeMsg": "welcome" + } +} diff --git a/src/locales/en-US/Header.json b/src/locales/en-US/Header.json new file mode 100644 index 000000000..383d28867 --- /dev/null +++ b/src/locales/en-US/Header.json @@ -0,0 +1,3 @@ +{ + "header_test": "$t(common:test)" +} diff --git a/src/locales/en-US/common.json b/src/locales/en-US/common.json new file mode 100755 index 000000000..1d6ab1537 --- /dev/null +++ b/src/locales/en-US/common.json @@ -0,0 +1,3 @@ +{ + "tests___": "test common" +} diff --git a/src/locales/en/Header.json b/src/locales/en/Header.json new file mode 100644 index 000000000..383d28867 --- /dev/null +++ b/src/locales/en/Header.json @@ -0,0 +1,3 @@ +{ + "header_test": "$t(common:test)" +} diff --git a/src/locales/en/common.json b/src/locales/en/common.json new file mode 100755 index 000000000..6b89c3168 --- /dev/null +++ b/src/locales/en/common.json @@ -0,0 +1,5 @@ +{ + "Welcome to React": "Welcome to React and react-i18next", + "welcomeMsg": "welcome", + "test": "Coffee time" +} diff --git a/yarn.lock b/yarn.lock index 7b88a30c5..b5373f36e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6159,6 +6159,13 @@ html-minifier@^3.2.3: relateurl "0.2.x" uglify-js "3.4.x" +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-tags@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" @@ -6294,6 +6301,13 @@ husky@1.3.x: run-node "^1.0.0" slash "^2.0.0" +i18next@^15.1.3: + version "15.1.3" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-15.1.3.tgz#f1984cbee0e3cb00cff9008b037264289ce8840a" + integrity sha512-hN2DZLoRSY2h/RYeNqth5XxV4N1ekKGSJDCGhFmmuXkOCAfK5CkUG4VBv9OBXrvf93xApv0KKBVrb0zJP31EKg== + dependencies: + "@babel/runtime" "^7.3.1" + iconv-lite@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" @@ -11592,6 +11606,14 @@ react-error-overlay@^5.1.4: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991" integrity sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg== +react-i18next@^10.11.0: + version "10.11.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-10.11.0.tgz#a6854e556d3aff9f5f161b4aa871d43cfff6bd9b" + integrity sha512-jmxLZK8mf+KxG3RUIiu/COperTq1c7+iHNsna7LODOYEYaoj6EXFuchOytnB80GoUOb0JC1csT37Zp+U5nPQqQ== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2, react-is@^16.8.4, react-is@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -14541,6 +14563,11 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + vtk.js@^8.3.11: version "8.4.9" resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.4.9.tgz#139d3cf2f36139e836fc1cf360b47e0d05f0b3be"