From 58b77faa88232cfead7631273210e1be4ef2212a Mon Sep 17 00:00:00 2001 From: Krishna Chaudhary Date: Wed, 12 Aug 2026 01:35:15 +0530 Subject: [PATCH] Add i18n support for login and navbar --- labellab-client/package.json | 2 + .../src/__tests__/components/login.test.js | 19 ++++++---- labellab-client/src/components/login/index.js | 38 +++++++++++-------- .../src/components/navbar/index.js | 11 +++--- labellab-client/src/i18n/index.js | 22 +++++++++++ labellab-client/src/i18n/locales/en.json | 21 ++++++++++ labellab-client/src/i18n/locales/hi.json | 21 ++++++++++ labellab-client/src/index.js | 1 + 8 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 labellab-client/src/i18n/index.js create mode 100644 labellab-client/src/i18n/locales/en.json create mode 100644 labellab-client/src/i18n/locales/hi.json diff --git a/labellab-client/package.json b/labellab-client/package.json index 3557dac2b..c09512f03 100644 --- a/labellab-client/package.json +++ b/labellab-client/package.json @@ -36,6 +36,7 @@ "fs-extra": "^8.1.0", "html-webpack-plugin": "4.0.0-beta.5", "http-proxy-middleware": "^0.19.1", + "i18next": "^19.9.2", "identity-obj-proxy": "3.0.0", "immutability-helper": "^3.0.0", "jest": "^24.9.0", @@ -66,6 +67,7 @@ "react-google-login": "^5.1.20", "react-google-maps": "^9.4.5", "react-hot-keys": "^1.2.2", + "react-i18next": "^11.18.6", "react-image-crop": "^8.6.4", "react-image-lightbox": "^5.1.1", "react-leaflet": "^2.2.0", diff --git a/labellab-client/src/__tests__/components/login.test.js b/labellab-client/src/__tests__/components/login.test.js index 1f6113191..64af978a7 100644 --- a/labellab-client/src/__tests__/components/login.test.js +++ b/labellab-client/src/__tests__/components/login.test.js @@ -2,20 +2,23 @@ import React from 'react' import Enzyme, { shallow } from 'enzyme' import Adapter from 'enzyme-adapter-react-16'; import { testStore, findByTestAttr } from '../../utils/TestUtils' -import LoginIndex from '../../components/login/index' +import { LoginIndex } from '../../components/login/index' Enzyme.configure({ adapter: new Adapter(), disableLifecycleMethods: true }); -const setUp = (initialState={}, props={}) => { - const store = testStore(initialState); - const wrapper = shallow() - .childAt(0) - .dive(); - return wrapper; -}; +const setUp = (initialState = {}, props = {}) => { + const store = testStore(initialState) + return shallow( + key} + {...props} + /> +) +} describe('Login Component', () => { diff --git a/labellab-client/src/components/login/index.js b/labellab-client/src/components/login/index.js index 34eb05984..ce791180e 100644 --- a/labellab-client/src/components/login/index.js +++ b/labellab-client/src/components/login/index.js @@ -1,6 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' +import { withTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { Input, @@ -56,11 +57,11 @@ class LoginIndex extends Component { errors.email = value.length ? isEmail(value) ? '' - : 'E-mail address is invalid!' - : 'E-mail address is required!' + : t('login.emailInvalid') + : t('login.emailRequired') break case 'password': - errors.password = value.length ? '' : 'Password is required!' + errors.password = value.length ? '' : t('login.passwordRequired') break default: break @@ -81,7 +82,7 @@ class LoginIndex extends Component { } else { let errors = { ...this.state.errors } - errors.password = password.length ? '' : 'Password is required!' + errors.password = password.length ? '' : t('login.passwordRequired') errors.email = email.length ? isEmail(email) ? '' @@ -116,31 +117,32 @@ class LoginIndex extends Component { statusText, registerStatusText, authError, + t, } = this.props const { email, password, errors } = this.state return (
{isAuthenticating ? ( - + ) : null}
LabelLab - Login to your account + {t('login.title')}
- + - + )}
- Forgot password? + {t('login.forgotPassword')}
- +
- Don't have an account?  - Create an account + {t('login.noAccount')}  + {t('login.createAccount')}
- Or + {t('login.or')}
@@ -238,4 +240,8 @@ const mapActionToProps = (dispatch) => { } } -export default connect(mapStateToProps, mapActionToProps)(LoginIndex) +export { LoginIndex } + +export default withTranslation()( + connect(mapStateToProps, mapActionToProps)(LoginIndex) +) diff --git a/labellab-client/src/components/navbar/index.js b/labellab-client/src/components/navbar/index.js index abebfec3b..2ad392752 100644 --- a/labellab-client/src/components/navbar/index.js +++ b/labellab-client/src/components/navbar/index.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import { Image, Header, Dropdown } from 'semantic-ui-react' import { Link } from 'react-router-dom' import { connect } from 'react-redux' +import { withTranslation } from 'react-i18next' import PropTypes from 'prop-types' import './css/navbar.css' import Searchbar from './searchbar' @@ -35,7 +36,7 @@ class Navbar extends Component { content={user.username} /> {isfetching ? ( -

LOADING

+

{this.props.t('navbar.loading')}

) : user ? ( - Profile + {this.props.t('navbar.profile')} - Logout + {this.props.t('navbar.logout')}
  • -

    Logout

    +

    {this.props.t('navbar.logout')}

  • @@ -83,4 +84,4 @@ const mapStateToProps = state => { } } -export default connect(mapStateToProps, null)(Navbar) +export default withTranslation()(connect(mapStateToProps, null)(Navbar)) diff --git a/labellab-client/src/i18n/index.js b/labellab-client/src/i18n/index.js new file mode 100644 index 000000000..d0aaec028 --- /dev/null +++ b/labellab-client/src/i18n/index.js @@ -0,0 +1,22 @@ +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' +import en from './locales/en.json' +import hi from './locales/hi.json' + +i18n.use(initReactI18next).init({ + resources: { + en: { + translation: en + }, + hi: { + translation: hi + } + }, + lng: 'en', + fallbackLng: 'en', + interpolation: { + escapeValue: false + } +}) + +export default i18n diff --git a/labellab-client/src/i18n/locales/en.json b/labellab-client/src/i18n/locales/en.json new file mode 100644 index 000000000..c85138a89 --- /dev/null +++ b/labellab-client/src/i18n/locales/en.json @@ -0,0 +1,21 @@ +{ + "login": { + "authenticating": "Authenticating...", + "title": "Login to your account", + "email": "E-mail address", + "password": "Password", + "forgotPassword": "Forgot password?", + "loginButton": "Log in", + "noAccount": "Don't have an account?", + "createAccount": "Create an account", + "or": "Or", + "emailRequired": "E-mail address is required!", + "emailInvalid": "E-mail address is invalid!", + "passwordRequired": "Password is required!" + }, + "navbar": { + "profile": "Profile", + "logout": "Logout", + "loading": "LOADING" + } +} \ No newline at end of file diff --git a/labellab-client/src/i18n/locales/hi.json b/labellab-client/src/i18n/locales/hi.json new file mode 100644 index 000000000..2893757e1 --- /dev/null +++ b/labellab-client/src/i18n/locales/hi.json @@ -0,0 +1,21 @@ +{ + "login": { + "authenticating": "प्रमाणीकरण हो रहा है...", + "title": "अपने खाते में लॉग इन करें", + "email": "ई-मेल पता", + "password": "पासवर्ड", + "forgotPassword": "पासवर्ड भूल गए?", + "loginButton": "लॉग इन करें", + "noAccount": "खाता नहीं है?", + "createAccount": "खाता बनाएं", + "or": "या", + "emailRequired": "ई-मेल पता आवश्यक है!", + "emailInvalid": "ई-मेल पता अमान्य है!", + "passwordRequired": "पासवर्ड आवश्यक है!" + }, + "navbar": { + "profile": "प्रोफ़ाइल", + "logout": "लॉग आउट", + "loading": "लोड हो रहा है" + } +} \ No newline at end of file diff --git a/labellab-client/src/index.js b/labellab-client/src/index.js index 0e602142a..d01f306b6 100644 --- a/labellab-client/src/index.js +++ b/labellab-client/src/index.js @@ -4,6 +4,7 @@ import { Provider } from 'react-redux' import { store } from './utils/store' import './index.css' import 'semantic-ui-css/semantic.min.css' +import './i18n' import App from './App' import * as serviceWorker from './serviceWorker' ReactDOM.render(