From 79a9d46a16a3d9c6787d135ea8468b77e0bed2cd Mon Sep 17 00:00:00 2001 From: GonzaGomez Date: Thu, 13 Aug 2026 20:28:30 -0300 Subject: [PATCH 1/2] Implement a new component for a better UX of Tags --- .../static/frontend/src/components/Base.tsx | 19 ++++++++--- .../src/components/LanguageSelector.tsx | 34 +++++++++++++++++++ .../frontend/src/components/MainNavbar.tsx | 2 ++ .../static/frontend/src/locales/en.ts | 1 + .../static/frontend/src/locales/es.ts | 1 + 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/frontend/static/frontend/src/components/LanguageSelector.tsx diff --git a/src/frontend/static/frontend/src/components/Base.tsx b/src/frontend/static/frontend/src/components/Base.tsx index 37c5ce36..d79a2938 100644 --- a/src/frontend/static/frontend/src/components/Base.tsx +++ b/src/frontend/static/frontend/src/components/Base.tsx @@ -16,10 +16,13 @@ import 'fomantic-ui-css/semantic.css' import '../css/base.css' const messages = { en, es } +const LOCALE_STORAGE_KEY = 'multiomix_locale' + +type Locale = 'en' | 'es' interface LocaleContextType { - locale: 'en' | 'es', - setLocale: React.Dispatch> + locale: Locale, + setLocale: (locale: Locale) => void } const LocaleContext = React.createContext({ locale: 'es', @@ -51,7 +54,15 @@ const Base = (props: BaseProps) => { const [currentUser, setUser] = useState>(null) const [isLoadingCurrentUser, setIsLoadingCurrentUser] = useState(true) // State that defines the current language ('es' or 'en') for , used to display the interface in the selected locale - const [locale, setLocale] = useState<'en' | 'es'>('es') + const [locale, setLocaleState] = useState(() => { + const storedLocale = localStorage.getItem(LOCALE_STORAGE_KEY) + return storedLocale === 'en' || storedLocale === 'es' ? storedLocale : 'es' + }) + + const setLocale = (newLocale: Locale) => { + localStorage.setItem(LOCALE_STORAGE_KEY, newLocale) + setLocaleState(newLocale) + } /** * Method which is executed when the component has mounted @@ -120,4 +131,4 @@ const Base = (props: BaseProps) => { ) } -export { Base, CurrentUserContext } +export { Base, CurrentUserContext, LocaleContext } diff --git a/src/frontend/static/frontend/src/components/LanguageSelector.tsx b/src/frontend/static/frontend/src/components/LanguageSelector.tsx new file mode 100644 index 00000000..773b3d8b --- /dev/null +++ b/src/frontend/static/frontend/src/components/LanguageSelector.tsx @@ -0,0 +1,34 @@ +import React, { useContext } from 'react' +import { Dropdown, Flag } from 'semantic-ui-react' +import { useIntl } from 'react-intl' +import { LocaleContext } from './Base' + +/** + * Allows the user to change the interface language without reloading the page. + * @returns Component + */ +const LanguageSelector = () => { + const intl = useIntl() + const { locale, setLocale } = useContext(LocaleContext) + + return ( + + + setLocale('es')}> + + Spanish (Español) + + setLocale('en')}> + + English + + + + ) +} + +export { LanguageSelector } diff --git a/src/frontend/static/frontend/src/components/MainNavbar.tsx b/src/frontend/static/frontend/src/components/MainNavbar.tsx index aa48b047..4bd1baeb 100644 --- a/src/frontend/static/frontend/src/components/MainNavbar.tsx +++ b/src/frontend/static/frontend/src/components/MainNavbar.tsx @@ -6,6 +6,7 @@ import { CurrentUserContext } from './Base' import { UpdateUserModal } from './UpdateUserModal' import { Alert } from './common/Alert' import { useIntl } from 'react-intl' +import { LanguageSelector } from './LanguageSelector' // Constants declared in base.html declare const urlIndex: string @@ -320,6 +321,7 @@ const MainNavbar = (props: MainNavbarProps) => { {/* LogIn/LogOut panel */} + diff --git a/src/frontend/static/frontend/src/locales/en.ts b/src/frontend/static/frontend/src/locales/en.ts index 7db45262..bc8b7a87 100644 --- a/src/frontend/static/frontend/src/locales/en.ts +++ b/src/frontend/static/frontend/src/locales/en.ts @@ -1359,6 +1359,7 @@ export default { 'mainNavbar.aboutUs': 'About us', 'mainNavbar.faq': 'FAQ', 'mainNavbar.openSource': 'Open source', + 'mainNavbar.language': 'Language', 'mainNavbar.homepage': 'Multiomix homepage', // 148.UpdateUserModal diff --git a/src/frontend/static/frontend/src/locales/es.ts b/src/frontend/static/frontend/src/locales/es.ts index d8833407..bbed34b4 100644 --- a/src/frontend/static/frontend/src/locales/es.ts +++ b/src/frontend/static/frontend/src/locales/es.ts @@ -1413,6 +1413,7 @@ export default { 'mainNavbar.aboutUs': 'Sobre nosotros', 'mainNavbar.faq': 'Preguntas frecuentes', 'mainNavbar.openSource': 'Código abierto', + 'mainNavbar.language': 'Idioma', 'mainNavbar.homepage': 'Página principal de Multiomix', // 148.UpdateUserModal From 27a1f33a9b7732e2db7b2fe42ff201c804b0ab2f Mon Sep 17 00:00:00 2001 From: GonzaGomez Date: Wed, 26 Aug 2026 21:38:52 -0300 Subject: [PATCH 2/2] fix locale validation and Open Source translations --- .../static/frontend/src/components/Base.tsx | 8 +++-- .../src/components/open-source/OpenSource.tsx | 31 +++++++++++-------- .../static/frontend/src/locales/en.ts | 4 +-- .../static/frontend/src/locales/es.ts | 4 +-- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/frontend/static/frontend/src/components/Base.tsx b/src/frontend/static/frontend/src/components/Base.tsx index d79a2938..a20d8bcf 100644 --- a/src/frontend/static/frontend/src/components/Base.tsx +++ b/src/frontend/static/frontend/src/components/Base.tsx @@ -18,7 +18,9 @@ import '../css/base.css' const messages = { en, es } const LOCALE_STORAGE_KEY = 'multiomix_locale' -type Locale = 'en' | 'es' +const LOCALES = ['en', 'es'] as const +type Locale = typeof LOCALES[number] +const LANG_SET: Set = new Set(LOCALES) interface LocaleContextType { locale: Locale, @@ -55,8 +57,8 @@ const Base = (props: BaseProps) => { const [isLoadingCurrentUser, setIsLoadingCurrentUser] = useState(true) // State that defines the current language ('es' or 'en') for , used to display the interface in the selected locale const [locale, setLocaleState] = useState(() => { - const storedLocale = localStorage.getItem(LOCALE_STORAGE_KEY) - return storedLocale === 'en' || storedLocale === 'es' ? storedLocale : 'es' + const storedLocale = localStorage.getItem(LOCALE_STORAGE_KEY) as Locale + return LANG_SET.has(storedLocale) ? storedLocale : 'es' }) const setLocale = (newLocale: Locale) => { diff --git a/src/frontend/static/frontend/src/components/open-source/OpenSource.tsx b/src/frontend/static/frontend/src/components/open-source/OpenSource.tsx index e2b634df..5f56dfb7 100644 --- a/src/frontend/static/frontend/src/components/open-source/OpenSource.tsx +++ b/src/frontend/static/frontend/src/components/open-source/OpenSource.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Base } from '../Base' import { Container, Divider, Grid, Header, HeaderSubheader, List, ListContent, ListDescription, ListHeader, ListIcon, ListItem } from 'semantic-ui-react' -import { useIntl } from 'react-intl' +import { FormattedMessage, useIntl } from 'react-intl' /** * Wrapper to make Context.Provider work. @@ -17,16 +17,16 @@ const OpenSourceWrapper = () => {
- {intl.formatMessage({ id: 'openSource.header.main' })}{' '} - - Omicsdatascience - {' '} - - GitHub - . + {chunks}, + githubLink: chunks => {chunks} + }} + />
- {intl.formatMessage({ id: 'openSource.header.sub' })} + {intl.formatMessage({ id: 'openSource.intro.description' })} @@ -56,10 +56,15 @@ const OpenSourceWrapper = () => {
- {intl.formatMessage({ id: 'openSource.publications.title' })}{' '} contributions + {chunks} + }} + />
- {intl.formatMessage({ id: 'openSource.publications.sub' })} + {intl.formatMessage({ id: 'openSource.publications.description' })} @@ -69,7 +74,7 @@ const OpenSourceWrapper = () => { Multiomix - {intl.formatMessage({ id: 'openSource.publications.multiomix.description' })} + {intl.formatMessage({ id: 'openSource.publication.multiomix.description' })} @@ -80,7 +85,7 @@ const OpenSourceWrapper = () => { Modulector - {intl.formatMessage({ id: 'openSource.publications.modulector.description' })} + {intl.formatMessage({ id: 'openSource.publication.modulector.description' })}
diff --git a/src/frontend/static/frontend/src/locales/en.ts b/src/frontend/static/frontend/src/locales/en.ts index bc8b7a87..c96b6e98 100644 --- a/src/frontend/static/frontend/src/locales/en.ts +++ b/src/frontend/static/frontend/src/locales/en.ts @@ -813,14 +813,14 @@ export default { 'loginSignUpForm.validEmailMessage': 'Please provide a valid email address', 'loginSignUpForm.passwordMismatchMessage': 'This value is different to Password field', // 91.OpenSource - 'openSource.intro.title': 'At Omicsdatascience, we believe in open science. Explore our open-source projects, freely available to the community to foster collaboration, transparency, and innovation in biomedical research. Available in our GitHub organization.', + 'openSource.intro.title': 'At Omicsdatascience, we believe in open science. Explore our open-source projects, freely available to the community to foster collaboration, transparency, and innovation in biomedical research. Available in our GitHub organization.', 'openSource.intro.description': 'These repositories represent some of our most prominent open-source initiatives, developed to empower the research community with accessible and cutting-edge bioinformatics tools.', 'openSource.multiomix.description': 'Cloud-based platform to infer cancer genomic and epigenomic events associated with gene expression modulation.', 'openSource.modulector.description': 'Modulector is a performing open platform that provides information about miRNAs, genes and methylation sites based on a compilation of information from different resources.', 'openSource.bioapi.description': 'A powerful abstraction of genomics databases. Bioapi is a REST API that provides data related to gene nomenclature, gene expression, and metabolic pathways.', - 'openSource.publications.title': 'Discover our scientific contributions', + 'openSource.publications.title': 'Discover our scientific contributions', 'openSource.publications.description': 'In addition to our open-source software, we regularly publish peer-reviewed scientific articles that document the methodologies, innovations, and findings behind our tools. We invite you to explore our publications to better understand the research impact of our work.', 'openSource.publication.multiomix.description': 'A cloud-based platform to infer cancer genomic and epigenomic events associated with gene expression modulation', diff --git a/src/frontend/static/frontend/src/locales/es.ts b/src/frontend/static/frontend/src/locales/es.ts index bbed34b4..156195dc 100644 --- a/src/frontend/static/frontend/src/locales/es.ts +++ b/src/frontend/static/frontend/src/locales/es.ts @@ -862,14 +862,14 @@ export default { 'loginSignUpForm.validEmailMessage': 'Por favor ingrese una dirección de correo válida', 'loginSignUpForm.passwordMismatchMessage': 'Este valor es diferente al campo Contraseña', // 91.OpenSource - 'openSource.intro.title': 'En Omicsdatascience creemos en la ciencia abierta. Explora nuestros proyectos de código abierto, disponibles libremente para la comunidad con el fin de fomentar la colaboración, la transparencia y la innovación en la investigación biomédica. Disponibles en nuestra organización de GitHub.', + 'openSource.intro.title': 'En Omicsdatascience creemos en la ciencia abierta. Explora nuestros proyectos de código abierto, disponibles libremente para la comunidad con el fin de fomentar la colaboración, la transparencia y la innovación en la investigación biomédica. Disponibles en nuestra organización de GitHub.', 'openSource.intro.description': 'Estos repositorios representan algunas de nuestras iniciativas de código abierto más destacadas, desarrolladas para brindar a la comunidad científica herramientas bioinformáticas accesibles y de vanguardia.', 'openSource.multiomix.description': 'Plataforma basada en la nube para inferir eventos genómicos y epigenómicos del cáncer asociados con la modulación de la expresión génica.', 'openSource.modulector.description': 'Modulector es una plataforma abierta que proporciona información sobre miRNAs, genes y sitios de metilación basada en la recopilación de información de diferentes recursos.', 'openSource.bioapi.description': 'Una potente abstracción de bases de datos genómicas. BioApi es una API REST que proporciona datos relacionados con nomenclatura génica, expresión génica y rutas metabólicas.', - 'openSource.publications.title': 'Descubre nuestras contribuciones científicas', + 'openSource.publications.title': 'Descubre nuestras contribuciones científicas', 'openSource.publications.description': 'Además de nuestro software de código abierto, publicamos regularmente artículos científicos revisados por pares que documentan las metodologías, innovaciones y hallazgos detrás de nuestras herramientas. Te invitamos a explorar nuestras publicaciones para comprender mejor el impacto científico de nuestro trabajo.', 'openSource.publication.multiomix.description': 'Una plataforma basada en la nube para inferir eventos genómicos y epigenómicos del cáncer asociados con la modulación de la expresión génica',