From 44f95cb35cba1f29fa4fd7ad5123c87659c7b65f Mon Sep 17 00:00:00 2001 From: oruburos Date: Sun, 28 Jun 2020 15:03:45 +0100 Subject: [PATCH 01/15] Branch with i18n functionality --- translations/locales/en/translations.json | 95 +++++++++++++++++++++++ translations/locales/es/translations.json | 95 +++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 translations/locales/en/translations.json create mode 100644 translations/locales/es/translations.json diff --git a/translations/locales/en/translations.json b/translations/locales/en/translations.json new file mode 100644 index 0000000000..ab94789cd5 --- /dev/null +++ b/translations/locales/en/translations.json @@ -0,0 +1,95 @@ +{ + "About": { + "Contribute": "Contribute", + "NewPj5": "New to p5.js?", + "Report": "Report a bug", + "Learn": "Learn", + "About": "About", + "Resources": "Resources", + "Libraries": "Libraries", + "Forum": "Forum" + }, + "Menu": { + "File": "File", + "New": "New", + "Save": "Save", + "Examples": "Examples", + "Edit": "Edit", + "TidyCode": "Tidy Code", + "Find": "Find", + "FindNext": "Find Next", + "FindPrevious": "Find Previous", + "Sketch": "Sketch", + "AddFile": "Add File", + "AddFolder": "Add Folder", + "Run": "Run", + "Stop": "Stop", + "Help": "Help", + "KeyboardShortcuts": "Keyboard Shortcuts", + "Reference": "Reference", + "About": "About", + "Tidy": "Tidy"}, + "Settings": { + "FindNextMatch": "Find Next Match", + "FindPrevMatch": "Find Previous Match", + "IndentCodeLeft": "Indent Code Left", + "IndentCodeRight": "Indent Code Right", + "CommentLine": "Comment Line", + "StartSketch": "Start Sketch", + "StopSketch": "StopSketch", + "TurnOnAccessibleOutput": "Turn On Accessible Output", + "TurnOffAccessibleOutput": "Turn Off Accessible Output", + "ToogleSidebar": "Toogle Sidebar", + "ToogleConsole": "Toogle Console", + "Preview": "Preview", + "Auto-refresh": "Auto-refresh", + "Console": "Console", + "Settings": "Settings", + "GeneralSettings": "General settings", + "Theme": "Theme", + "Light": "Light", + "Dark": "Dark", + "HighContrast": "High Contrast", + "TextSize": "Text Size", + "Decrease": "Decrease", + "Increase": "Increase", + "IndentationAmount": "Indentation amount", + "Autosave": "Autosave", + "On": "On", + "Off": "Off", + "SketchSettings": "Sketch Settings", + "SecurityProtocol": "Security Protocol", + "ServeOverHTTPS": "Serve over HTTPS", + "Accesibility": "Accesibility", + "LintWarningSound": "Lint Warning Sound", + "PreviewSound": "Preview sound", + "AccessibleTextBasedCanvas": "Accessible text-based canvas", + "UsedScreenReader": "Used with screen reader", + "Plain-text": "Plain-text", + "Table-text": "Table-text", + "Sound": "Sound", + "WordWrap": "Word Wrap", + "LineNumbers": "Line Numbers", + "LangChange": "Language changed" + }, + "Login": { + "Welcome": "Welcome", + "Login": "Login", + "LoginOr": "or", + "SignUp": "Sign Up", + "Email": "email", + "Username": "username", + "LoginGithub": "Login with Github", + "LoginGoogle": "Login with Google", + "DontHaveAccount": "Don't have an account?", + "ForgotPassword": "Forgot your password?", + "ResetPassword": "Reset your password", + "BackEditor": "Back to Editor", + "UsernameSplit": "User Name", + "Password": "Password", + "ConfirmPassword": "Confirm Password" + }, + "Toast": { + "LangChange": "Language changed!!!" + } +} diff --git a/translations/locales/es/translations.json b/translations/locales/es/translations.json new file mode 100644 index 0000000000..4b551f6061 --- /dev/null +++ b/translations/locales/es/translations.json @@ -0,0 +1,95 @@ +{ + "About": { + "Contribute": "Contribuir", + "NewPj5": "¿Empezando a aprender p5.js?", + "Report": "Reporta un error", + "Learn": "Aprende", + "About": "Acerca de", + "Resources": "Recursos", + "Libraries": "Bibliotecas", + "Forum": "Foro" + }, + "Menu": { + "File": "Archivo", + "New": "Nuevo", + "Save": "Guardar", + "Examples": "Ejemplos", + "Edit": "Editar", + "TidyCode": "Ordenar código", + "Find": "Buscar", + "FindNext": "Buscar anterior", + "FindPrevious": "Buscar anterior", + "Sketch": "Bosquejo", + "AddFile": "Agregar archivo", + "AddFolder": "Agregar directorio", + "Run": "Ejecutar", + "Stop": "Detener", + "Help": "Ayuda", + "KeyboardShortcuts": "Atajos", + "Reference": "Referencia", + "About": "Acerca de", + "Tidy": "Ordenar"}, + "Settings": { + "FindNextMatch": "Encontrar siguiente ocurrencia", + "FindPrevMatch": "Encontrar ocurrencia previa", + "IndentCodeLeft": "Indentar codigo a la izquierda", + "IndentCodeRight": "Indentar codigo a la derecha", + "CommentLine": "Comentar linea de codigo", + "StartSketch": "Iniciar bosquejo", + "StopSketch": "Detener bosquejo", + "TurnOnAccessibleOutput": "Activar salida accesible", + "TurnOffAccessibleOutput": "Desactivar salida accesible", + "ToogleSidebar": "Alternar barra de deslizamiento", + "ToogleConsole": "Alternar consola", + "Preview": "Vista previa", + "Auto-refresh": "Auto-refrescar", + "Console": "Consola", + "Settings": "Configuracion", + "GeneralSettings": "Configuracion general", + "Theme": "Modo de visualizacion", + "Light": "Claro", + "Dark": "Oscuro", + "HighContrast": "Alto contraste", + "TextSize": "Tamaño del texto", + "Decrease": "Disminuir", + "Increase": "Aumentar", + "IndentationAmount": "Cantidad de indentacion", + "Autosave": "Grabar automáticamente", + "On": "Activar", + "Off": "Desactivar", + "SketchSettings": "Configuracion del bosquejo", + "SecurityProtocol": "Protocolo de seguridad", + "ServeOverHTTPS": "Usar HTTPS", + "Accesibility": "Accesibilidad", + "LintWarningSound": "Sonido de alarma Lint", + "PreviewSound": "Probar sonido", + "AccessibleTextBasedCanvas": "Lienzo accesible por texto", + "UsedScreenReader": "Uso con screen reader", + "Plain-text": "Texto sin formato", + "Table-text": "Tablero de texto", + "Sound": "Sonido", + "WordWrap": "Ajuste automático de línea", + "LineNumbers": "Numero de línea", + "LangChange": "Lenguaje cambiado" + }, + "Login": { + "Welcome": "Bienvenida", + "Login": "Ingresa", + "LoginOr": "o", + "SignUp": "registráte", + "email": "correo electronico", + "username": "nombre de usuario", + "LoginGithub": "Ingresa con Github", + "LoginGoogle": "Ingresa con Google", + "DontHaveAccount": "No tienes cuenta?", + "ForgotPassword": "Olvidaste tu contrasena?", + "ResetPassword": "Regenera tu contrasena", + "BackEditor": "Regresa al editor", + "UsernameSplit": "Nombre de usuario", + "Password": "Contrasena", + "ConfirmPassword": "Confirma la contrasena" + }, + "Toast": { + "LangChange": "Felicidades, lenguaje modificado!!!" + } +} From fe770f05274b8a239b8263ddc4f5bb85ec1a6759 Mon Sep 17 00:00:00 2001 From: oruburos Date: Sun, 28 Jun 2020 15:20:16 +0100 Subject: [PATCH 02/15] Branch with i18n functionality Adding the i18n file. --- client/i18n.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 client/i18n.js diff --git a/client/i18n.js b/client/i18n.js new file mode 100644 index 0000000000..5a64f03296 --- /dev/null +++ b/client/i18n.js @@ -0,0 +1,30 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import Backend from 'i18next-http-backend'; +import LanguageDetector from 'i18next-browser-languagedetector'; +// not like to use this? +// have a look at the Quick start guide +// for passing in lng and translations on init + +i18n + // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales) + // learn more: https://github.com/i18next/i18next-http-backend + .use(Backend) + // detect user language + // learn more: https://github.com/i18next/i18next-browser-languageDetector + .use(LanguageDetector) + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + // for all options read: https://www.i18next.com/overview/configuration-options + .init({ + fallbackLng: 'en', + debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + } + }); + + +export default i18n; From 0d584f5826ec52b25bf99e2a8c23963bbb2c1749 Mon Sep 17 00:00:00 2001 From: oruburos Date: Sun, 28 Jun 2020 19:50:20 +0100 Subject: [PATCH 03/15] Translation files with new entries i18n functions in several entries. --- client/components/Nav.jsx | 584 +++++++++++++--------- client/i18n.js | 67 ++- client/index.jsx | 7 +- client/modules/IDE/components/Toast.jsx | 4 +- package-lock.json | 68 +++ package.json | 4 + server/server.js | 7 + translations/locales/en/translations.json | 7 +- translations/locales/es/translations.json | 10 +- 9 files changed, 481 insertions(+), 277 deletions(-) diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 22305b3ae4..6c52bd0fa0 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -4,6 +4,8 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router'; import { Link } from 'react-router'; import classNames from 'classnames'; +import { Translation } from 'react-i18next'; +import i18next from 'i18next'; import * as IDEActions from '../modules/IDE/actions/ide'; import * as toastActions from '../modules/IDE/actions/toast'; import * as projectActions from '../modules/IDE/actions/project'; @@ -56,6 +58,10 @@ class Nav extends React.PureComponent { this.handleFocusForHelp = this.handleFocus.bind(this, 'help'); this.toggleDropdownForAccount = this.toggleDropdown.bind(this, 'account'); this.handleFocusForAccount = this.handleFocus.bind(this, 'account'); + this.toggleDropdownForLang = this.toggleDropdown.bind(this, 'lang'); + this.handleFocusForLang = this.handleFocus.bind(this, 'lang'); + this.handleLangSelection = this.handleLangSelection.bind(this); + this.closeDropDown = this.closeDropDown.bind(this); } @@ -95,11 +101,11 @@ class Nav extends React.PureComponent { const { unsavedChanges, warnIfUnsavedChanges } = this.props; if (!unsavedChanges) { this.props.showToast(1500); - this.props.setToastText('Opened new sketch.'); + this.props.setToastText('Toast.OpenedNewSketch'); this.props.newProject(); } else if (warnIfUnsavedChanges && warnIfUnsavedChanges()) { this.props.showToast(1500); - this.props.setToastText('Opened new sketch.'); + this.props.setToastText('Toast.OpenedNewSketch'); // 'Opened new sketch.' this.props.newProject(); } this.setDropdown('none'); @@ -164,6 +170,13 @@ class Nav extends React.PureComponent { this.setDropdown('none'); } + handleLangSelection(event) { + i18next.changeLanguage(event.target.value); + this.props.showToast(1500); + this.props.setToastText('Toast.LangChange'); + this.setDropdown('none'); + } + handleLogout() { this.props.logoutUser(); this.setDropdown('none'); @@ -244,87 +257,89 @@ class Nav extends React.PureComponent { renderProjectMenu(navDropdownState) { return ( -
    -
  • - -
  • -
  • - -
      -
    • - + + {(t, { i18n }) => ( +
        +
      • +
      • - { __process.env.LOGIN_ENABLED && (!this.props.project.owner || this.isUserOwner()) && -
      • - -
      • } - { this.props.project.id && this.props.user.authenticated && -
      • - -
      • } - { this.props.project.id && -
      • +
      • -
      • } - { this.props.project.id && -
      • - -
      • } - { this.props.user.authenticated && -
      • - - Open - -
      • } - {__process.env.UI_COLLECTIONS_ENABLED && +
          +
        • + +
        • + { __process.env.LOGIN_ENABLED && (!this.props.project.owner || this.isUserOwner()) && +
        • + +
        • } + { this.props.project.id && this.props.user.authenticated && +
        • + +
        • } + { this.props.project.id && +
        • + +
        • } + { this.props.project.id && +
        • + +
        • } + { this.props.user.authenticated && +
        • + + {t('Menu.Open')} + +
        • } + {__process.env.UI_COLLECTIONS_ENABLED && this.props.user.authenticated && this.props.project.id &&
        • @@ -334,136 +349,136 @@ class Nav extends React.PureComponent { onBlur={this.handleBlur} onClick={this.setDropdownForNone} > - Add to Collection + {t('Menu.AddToCollection')}
        • } - { __process.env.EXAMPLES_ENABLED && -
        • - - Examples - -
        • } -
        - -
      • - -
          -
        • - -
        • -
        • - + { __process.env.EXAMPLES_ENABLED && +
        • + + {t('Menu.Examples')} + +
        • } +
      • -
      • +
      • -
      • -
      • - -
      • -
      -
    • -
    • - -
        -
      • - -
      • -
      • - -
      • -
      • - +
          +
        • + +
        • +
        • + +
        • +
        • + +
        • +
        • + +
        • +
      • -
      • +
      • -
      • - {/*
      • +
          +
        • + +
        • +
        • + +
        • +
        • + +
        • +
        • + +
        • + {/*
        • */} -
        -
      • -
      • - -
          -
        • +
        +
      • +
      • +
      • -
      • - Reference - -
      • -
      • - + )} + + ); + } + + renderLanguageMenu(navDropdownState) { + return ( + + {(t, { i18n }) => ( +
          +
        • + +
            +
          • + +
          • +
          • + +
          • +
          • + +
          • +
        -
      • -
      + )} + ); } + renderUnauthenticatedUserMenu(navDropdownState) { return ( -
        -
      • - - Log in - -
      • - or -
      • - - Sign up - -
      • -
      + + {(t, { i18n }) => ( +
        +
      • + + {t('Login.Login')} + +
      • + {t('Login.LoginOr')} +
      • + + {t('Login.SignUp')} + +
      • +
      + )} +
      ); } @@ -678,6 +757,10 @@ class Nav extends React.PureComponent { account: classNames({ 'nav__item': true, 'nav__item--open': this.state.dropdownOpen === 'account' + }), + lang: classNames({ + 'nav__item': true, + 'nav__item--open': this.state.dropdownOpen === 'lang' }) }; @@ -685,6 +768,7 @@ class Nav extends React.PureComponent {
      diff --git a/client/i18n.js b/client/i18n.js index 5a64f03296..56ec2821b1 100644 --- a/client/i18n.js +++ b/client/i18n.js @@ -1,30 +1,57 @@ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; +// import detector from 'i18next-browser-languagedetector';// +// import Fetch from 'i18next-fetch-backend'; +// import Fetch from 'i18next-fetch-backend'; import Backend from 'i18next-http-backend'; -import LanguageDetector from 'i18next-browser-languagedetector'; -// not like to use this? -// have a look at the Quick start guide -// for passing in lng and translations on init +// import axios from 'axios'; +// import commonEn from './locales/en/translations.json'; +// import commonEs from './locales/es/translations.json'; + +const fallbackLng = ['en']; +const availableLanguages = ['en', 'es']; +/* const fileTmp = '/locales/en/translations.json'; +axios.get(fileTmp) + .then((response) => { + console.log('Datos que traere'); + console.log(response.data); + console.log(response.status); + console.log(response.statusText); + console.log(response.headers); + console.log(response.config); + }).catch((error) => { + // handle error + console.log('ERROR AXIOS ASA AXIOS'); + console.log(error); + }); */ + + +const options = { + // loadPath: '/locales/{{lng}}/translations.json', + loadPath: '/locales/{{lng}}/translations.json', + // loadPath: fileTmp, + requestOptions: { // used for fetch, can also be a function (payload) => ({ method: 'GET' }) + mode: 'no-cors' + }, + allowMultiLoading: false, // set loadPath: '/locales/resources.json?lng={{lng}}&ns={{ns}}' to adapt to multiLoading +}; i18n - // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales) - // learn more: https://github.com/i18next/i18next-http-backend - .use(Backend) - // detect user language - // learn more: https://github.com/i18next/i18next-browser-languageDetector - .use(LanguageDetector) - // pass the i18n instance to react-i18next. - .use(initReactI18next) - // init i18next - // for all options read: https://www.i18next.com/overview/configuration-options - .init({ - fallbackLng: 'en', + .use(initReactI18next) // pass the i18n instance to react-i18next. + .use(Backend).init({ + lng: 'en', + defaultNS: 'menu', + fallbackLng, // if user computer language is not on the list of available languages, than we will be using the fallback language specified earlier debug: true, - + backend: options, + getAsync: false, + initImmediate: false, + useSuspense: true, + whitelist: availableLanguages, interpolation: { - escapeValue: false, // not needed for react as it escapes by default - } + escapeValue: false + }, + saveMissing: true, }); - export default i18n; diff --git a/client/index.jsx b/client/index.jsx index 09f6eba066..3a39ac8eef 100644 --- a/client/index.jsx +++ b/client/index.jsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { render } from 'react-dom'; import { hot } from 'react-hot-loader/root'; import { Provider } from 'react-redux'; import { Router, browserHistory } from 'react-router'; import configureStore from './store'; import routes from './routes'; +import i18n from './i18n'; require('./styles/main.scss'); @@ -25,6 +26,8 @@ const App = () => ( const HotApp = hot(App); render( - , + Loading translations)}> + + , document.getElementById('root') ); diff --git a/client/modules/IDE/components/Toast.jsx b/client/modules/IDE/components/Toast.jsx index 2b9a0d58bb..58a846b385 100644 --- a/client/modules/IDE/components/Toast.jsx +++ b/client/modules/IDE/components/Toast.jsx @@ -2,15 +2,17 @@ import PropTypes from 'prop-types'; import React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; +import { useTranslation } from 'react-i18next'; import * as ToastActions from '../actions/toast'; import ExitIcon from '../../../images/exit.svg'; function Toast(props) { + const { t } = useTranslation('common'); return (

      - {props.text} + {t(props.text)}

    • @@ -258,7 +271,7 @@ class Nav extends React.PureComponent { } }} > - File + {this.props.t('File')}