diff --git a/src/components/Elements/MainModal/backend/Tab.jsx b/src/components/Elements/MainModal/backend/Tab.jsx index 756f7c74..3373f097 100644 --- a/src/components/Elements/MainModal/backend/Tab.jsx +++ b/src/components/Elements/MainModal/backend/Tab.jsx @@ -1,21 +1,20 @@ -import variables from 'config/variables'; import { memo, useState, useEffect } from 'react'; -import { useTranslation } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; import { getIconComponent, DIVIDER_LABELS } from '../constants/tabConfig'; function Tab({ label, currentTab, onClick, navbarTab }) { - const { languagecode } = useTranslation(); + const t = useT(); const [isExperimental, setIsExperimental] = useState(true); useEffect(() => { setIsExperimental(localStorage.getItem('experimental') !== 'false'); }, []); - // Get the icon component for this label - const IconComponent = getIconComponent(label, variables); + // Get the icon component for this label (label is already translated) + const IconComponent = getIconComponent(label, { getMessage: t }); // Determine if this label should have a divider after it - const hasDivider = DIVIDER_LABELS.some((key) => variables.getMessage(key) === label); + const hasDivider = DIVIDER_LABELS.some((key) => t(key) === label); // Build className const baseClass = navbarTab ? 'navbar-item' : 'tab-list-item'; @@ -23,8 +22,7 @@ function Tab({ label, currentTab, onClick, navbarTab }) { const className = `${baseClass}${currentTab === label ? ` ${activeClass}` : ''}`; // Hide experimental tab if experimental mode is disabled - const isExperimentalTab = - label === variables.getMessage('modals.main.settings.sections.experimental.title'); + const isExperimentalTab = label === t('modals.main.settings.sections.experimental.title'); if (isExperimentalTab && !isExperimental) { return
; } diff --git a/src/components/Elements/MainModal/backend/Tabs.jsx b/src/components/Elements/MainModal/backend/Tabs.jsx index c543515c..fbbc1776 100644 --- a/src/components/Elements/MainModal/backend/Tabs.jsx +++ b/src/components/Elements/MainModal/backend/Tabs.jsx @@ -1,6 +1,6 @@ -import variables from 'config/variables'; import { useState, useEffect } from 'react'; -import { useTranslation } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; +import variables from 'config/variables'; import Tab from './Tab'; import ReminderInfo from '../components/ReminderInfo'; import ErrorBoundary from '../../../../features/misc/modals/ErrorBoundary'; @@ -16,7 +16,7 @@ const Tabs = ({ navigationTrigger, sections, }) => { - const { languagecode } = useTranslation(); + const t = useT(); // Find initial section from deep link if available const getInitialSection = () => { @@ -24,7 +24,7 @@ const Tabs = ({ const section = sections.find((s) => s.name === deepLinkData.section); if (section) { return { - label: variables.getMessage(section.label), + label: t(section.label), name: section.name, }; } @@ -66,23 +66,23 @@ const Tabs = ({ if (sections && currentName) { const section = sections.find((s) => s.name === currentName); if (section) { - const newLabel = variables.getMessage(section.label); + const newLabel = t(section.label); setCurrentTab(newLabel); } } - }, [languagecode]); + }, [t, sections, currentName]); // Handle navigation trigger for settings sections (popstate) useEffect(() => { if (navigationTrigger?.type === 'settings-section' && sections) { const section = sections.find((s) => s.name === navigationTrigger.data); if (section) { - const label = variables.getMessage(section.label); + const label = t(section.label); setCurrentTab(label); setCurrentName(section.name); } } - }, [navigationTrigger, sections]); + }, [navigationTrigger, sections, t]); // Reset to first tab when requested useEffect(() => { diff --git a/src/components/Elements/MainModal/components/ModalTopBar.jsx b/src/components/Elements/MainModal/components/ModalTopBar.jsx index 6eeef96d..f98ef699 100644 --- a/src/components/Elements/MainModal/components/ModalTopBar.jsx +++ b/src/components/Elements/MainModal/components/ModalTopBar.jsx @@ -1,5 +1,4 @@ -import variables from 'config/variables'; -import { useTranslation } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; import { MdClose, MdChevronRight, MdArrowBack, MdArrowForward } from 'react-icons/md'; import { Tooltip, Button } from 'components/Elements'; import { NAVBAR_BUTTONS } from '../constants/tabConfig'; @@ -32,13 +31,11 @@ function ModalTopBar({ canGoBack, canGoForward, }) { - const { languagecode } = useTranslation(); + const t = useT(); - // Get the current tab label (uses languagecode to re-evaluate on language change) + // Get the current tab label const currentTabButton = NAVBAR_BUTTONS.find(({ tab }) => tab === currentTab); - const currentTabLabel = languagecode && currentTabButton - ? variables.getMessage(currentTabButton.messageKey) - : ''; + const currentTabLabel = currentTabButton ? t(currentTabButton.messageKey) : ''; // Utility function to get translated sub-section label const getSubSectionLabel = (subSection, sectionName) => { @@ -46,7 +43,7 @@ function ModalTopBar({ // Use the same translation pattern as the section components const translationKey = `modals.main.settings.sections.${sectionName}.${subSection}.title`; - const translated = variables.getMessage(translationKey); + const translated = t(translationKey); // If translation key is returned as-is or empty, it means translation doesn't exist // Fall back to capitalized sub-section name @@ -107,7 +104,7 @@ function ModalTopBar({ const categoryKey = MARKETPLACE_TYPE_TO_KEY[productView.type]; if (categoryKey) { breadcrumbPath.push({ - label: variables.getMessage(categoryKey), + label: t(categoryKey), onClick: productView.onBack || null, }); } @@ -214,11 +211,11 @@ function ModalTopBar({ onClick={() => onTabChange(tab)} active={currentTab === tab} icon={} - label={variables.getMessage(messageKey)} + label={t(messageKey)} /> ))} - + diff --git a/src/components/Form/Settings/Radio/Radio.jsx b/src/components/Form/Settings/Radio/Radio.jsx index 595371bc..d0a503b9 100644 --- a/src/components/Form/Settings/Radio/Radio.jsx +++ b/src/components/Form/Settings/Radio/Radio.jsx @@ -1,5 +1,6 @@ import variables from 'config/variables'; import { memo, useState, useCallback } from 'react'; +import { useTranslation } from 'contexts/TranslationContext'; import { Radio as RadioUI, RadioGroup, @@ -9,9 +10,9 @@ import { } from '@mui/material'; import EventBus from 'utils/eventbus'; -import { translations } from 'lib/translations'; const Radio = memo((props) => { + const { changeLanguage } = useTranslation(); const [value, setValue] = useState(localStorage.getItem(props.name)); const handleChange = useCallback(async (e) => { @@ -22,14 +23,8 @@ const Radio = memo((props) => { } if (props.name === 'language') { - // old tab name - if (localStorage.getItem('tabName') === variables.getMessage('tabname')) { - localStorage.setItem('tabName', translations[newValue.replace('-', '_')].tabname); - } - - // Emit language change event for instant update - localStorage.setItem(props.name, newValue); - EventBus.emit('languageChange', { language: newValue }); + // Use context to change language directly - no EventBus needed + changeLanguage(newValue); setValue(newValue); variables.stats.postEvent('setting', `${props.name} from ${value} to ${newValue}`); @@ -59,7 +54,7 @@ const Radio = memo((props) => { } EventBus.emit('refresh', props.category); - }, [value, props]); + }, [value, props, changeLanguage]); return ( diff --git a/src/contexts/README.md b/src/contexts/README.md index 4b224966..a7587ab7 100644 --- a/src/contexts/README.md +++ b/src/contexts/README.md @@ -1,43 +1,96 @@ # Translation System -## Using Reactive Translations +## Refactored Translation System ✨ -The app now supports instant language switching without page refresh! +The app now has a robust, centralized translation system that updates instantly without page refresh! -### For new components +### Using Translations (New API) -Use the `useMessage` hook for reactive translations: +**Recommended approach** - Use the `useT()` hook: ```jsx -import { useMessage } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; function MyComponent() { - const title = useMessage('modals.main.title'); - const description = useMessage('modals.main.description'); + const t = useT(); return (
-

{title}

-

{description}

+

{t('modals.main.title')}

+

{t('modals.main.description')}

); } ``` -### For existing components +**Alternative** - Use the full hook: -The old `variables.getMessage()` method still works but won't automatically update when language changes. Gradually migrate to `useMessage` for components that display translations prominently. +```jsx +import { useTranslation } from 'contexts/TranslationContext'; -### How it works +function MyComponent() { + const { t, language, changeLanguage } = useTranslation(); -1. `TranslationProvider` wraps the app and listens for language change events -2. When language is changed via the Radio component, it emits a `languageChange` event -3. The provider updates the i18n instance and triggers re-renders -4. Components using `useMessage` automatically update to show new translations + return ( +
+

{t('modals.main.title')}

+ +
+ ); +} +``` + +### Backward Compatibility + +The old `variables.getMessage()` method still works and is automatically reactive: + +```jsx +// This still works and updates on language change +const title = variables.getMessage('modals.main.title'); +``` + +This allows gradual migration of components. + +### How It Works + +1. **TranslationProvider** wraps the app and manages the i18n instance +2. **Single source of truth** - All translation logic is centralized in the context +3. **Automatic reactivity** - Components using `t()` automatically re-render on language change +4. **Backward compatible** - `variables.getMessage()` is updated to use the context internally +5. **No EventBus needed** - Direct context updates for language changes ### Benefits -- No page refresh needed -- Minimal performance impact (translations already loaded) -- Backward compatible with existing code -- Smooth user experience +✅ **Instant updates** - No page refresh needed +✅ **Single API** - One consistent way to translate +✅ **Automatic re-renders** - React handles updates efficiently +✅ **Minimal performance impact** - Translations pre-loaded, only switching active language +✅ **Type-safe ready** - Easy to add TypeScript support later +✅ **Clean architecture** - Centralized translation logic + +### Migration Guide + +**Before:** +```jsx +const title = variables.getMessage('modals.main.title'); +``` + +**After:** +```jsx +const t = useT(); +const title = t('modals.main.title'); +``` + +### Components Updated + +Core navigation and UI: +- ✅ TranslationContext (centralized API) +- ✅ Radio component (language selector) +- ✅ Tab, Tabs (sidebar navigation) +- ✅ ModalTopBar (breadcrumbs) +- ✅ Settings view +- ✅ Language section +- ✅ Refresh button +- ✅ Welcome modal + +All other components continue to work via backward compatibility. diff --git a/src/contexts/TranslationContext.jsx b/src/contexts/TranslationContext.jsx index 3ef09a26..39d2442a 100644 --- a/src/contexts/TranslationContext.jsx +++ b/src/contexts/TranslationContext.jsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useState, useEffect, useCallback } from 'react'; +import { createContext, useContext, useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { initTranslations, translations } from 'lib/translations'; import variables from 'config/variables'; import EventBus from 'utils/eventbus'; @@ -6,25 +6,51 @@ import EventBus from 'utils/eventbus'; const TranslationContext = createContext(); export function TranslationProvider({ children, initialLanguage }) { - const [languagecode, setLanguagecode] = useState(initialLanguage); + const [currentLanguage, setCurrentLanguage] = useState(initialLanguage); + const i18nInstance = useRef(null); + // Initialize i18n instance once + useEffect(() => { + i18nInstance.current = initTranslations(currentLanguage); + variables.language = i18nInstance.current; + variables.languagecode = currentLanguage; + document.documentElement.lang = currentLanguage.replace('_', '-'); + }, [currentLanguage]); + + // Change language function const changeLanguage = useCallback((newLanguage) => { // Update the i18n instance - variables.language = initTranslations(newLanguage); + i18nInstance.current = initTranslations(newLanguage); + variables.language = i18nInstance.current; variables.languagecode = newLanguage; document.documentElement.lang = newLanguage.replace('_', '-'); // Update tab name if it's still the default - if (localStorage.getItem('tabName') === variables.getMessage('tabname')) { - const newTabName = translations[newLanguage.replace('-', '_')]?.tabname || variables.getMessage('tabname'); + const currentTabName = localStorage.getItem('tabName'); + const oldDefaultTabName = i18nInstance.current?.getMessage(currentLanguage, 'tabname'); + + if (currentTabName === oldDefaultTabName || !currentTabName) { + const newTabName = translations[newLanguage.replace('-', '_')]?.tabname || + i18nInstance.current?.getMessage(newLanguage, 'tabname') || + 'Mue'; localStorage.setItem('tabName', newTabName); document.title = newTabName; } - // Update state to trigger re-render - setLanguagecode(newLanguage); - }, []); + // Update language in localStorage + localStorage.setItem('language', newLanguage); + // Update state to trigger re-render + setCurrentLanguage(newLanguage); + }, [currentLanguage]); + + // Single translation function - the main API + const t = useCallback((key, optional = {}) => { + if (!i18nInstance.current) return key; + return i18nInstance.current.getMessage(currentLanguage, key, optional); + }, [currentLanguage]); + + // Listen for EventBus language change events (for backward compatibility) useEffect(() => { const handleLanguageChange = (data) => { if (data?.language) { @@ -39,8 +65,20 @@ export function TranslationProvider({ children, initialLanguage }) { }; }, [changeLanguage]); + // Update variables.getMessage for backward compatibility + useEffect(() => { + variables.getMessage = (key, optional = {}) => t(key, optional); + }, [t]); + + const value = useMemo(() => ({ + language: currentLanguage, + languagecode: currentLanguage, // Alias for backward compatibility + changeLanguage, + t, + }), [currentLanguage, changeLanguage, t]); + return ( - + {children} ); @@ -54,10 +92,8 @@ export function useTranslation() { return context; } -// Hook for reactive translations - triggers re-render when language changes -export function useMessage(key, optional = {}) { - const { languagecode } = useTranslation(); - // The languagecode dependency ensures this hook re-evaluates when language changes - // This is intentional - we use it to trigger re-renders even though it's not directly used - return languagecode ? variables.getMessage(key, optional) : ''; +// Convenience hook - just returns the t function +export function useT() { + const { t } = useTranslation(); + return t; } diff --git a/src/contexts/index.js b/src/contexts/index.js index 92d557f6..5e613ec0 100644 --- a/src/contexts/index.js +++ b/src/contexts/index.js @@ -1 +1 @@ -export { TranslationProvider, useTranslation, useMessage } from './TranslationContext'; +export { TranslationProvider, useTranslation, useT } from './TranslationContext'; diff --git a/src/features/background/components/PhotoInformation.jsx b/src/features/background/components/PhotoInformation.jsx index c417153e..17e8ba4e 100644 --- a/src/features/background/components/PhotoInformation.jsx +++ b/src/features/background/components/PhotoInformation.jsx @@ -1,5 +1,6 @@ import variables from 'config/variables'; import { useState, memo } from 'react'; +import { useT } from 'contexts'; import Favourite from './Favourite'; import { MdInfo, @@ -54,6 +55,7 @@ const downloadImage = async (info) => { }; function PhotoInformation({ info, url, api }) { + const t = useT(); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); const [usePhotoMap, setPhotoMap] = useState(false); @@ -64,7 +66,7 @@ function PhotoInformation({ info, url, api }) { const [shareModal, openShareModal] = useState(false); const [excludeModal, openExcludeModal] = useState(false); const [favouriteTooltipText, setFavouriteTooltipText] = useState( - variables.getMessage('widgets.quote.favourite'), + t('widgets.quote.favourite'), ); if (info.hidden === true || !info.credit) { @@ -72,7 +74,7 @@ function PhotoInformation({ info, url, api }) { } let credit = info.credit; - let photo = variables.getMessage('widgets.background.credit'); + let photo = t('widgets.background.credit'); // unsplash credit if (info.photographerURL && info.photographerURL !== '' && !info.offline && api) { @@ -142,31 +144,31 @@ function PhotoInformation({ info, url, api }) { return (
{info.location && info.location !== 'N/A' ? ( -
+
{info.location}
) : null} {info.camera && info.camera !== 'N/A' ? ( -
+
{info.camera}
) : null} -
+
{width}x{height}
{info.category && ( -
+
{info.category[0].toUpperCase() + info.category.slice(1)}
)} {api && ( -
+
{info.photoURL ? ( @@ -189,7 +191,7 @@ function PhotoInformation({ info, url, api }) { return (
{!info.offline && ( - + openShareModal(true)} /> )} @@ -204,7 +206,7 @@ function PhotoInformation({ info, url, api }) { {!info.offline && ( @@ -213,7 +215,7 @@ function PhotoInformation({ info, url, api }) { )} {info.pun && info.category && ( @@ -227,16 +229,16 @@ function PhotoInformation({ info, url, api }) { const UnsplashStats = () => { return (
-
+
{info.views.toLocaleString()}
-
+
{info.downloads.toLocaleString()}
{info.likes ? ( -
+
{info.likes.toLocaleString()}
@@ -366,7 +368,7 @@ function PhotoInformation({ info, url, api }) { {(showExtraInfo || other) && excludeModal === false ? ( <> - {variables.getMessage('widgets.background.information')} + {t('widgets.background.information')} diff --git a/src/features/greeting/Greeting.jsx b/src/features/greeting/Greeting.jsx index d60bb64a..ac04b714 100644 --- a/src/features/greeting/Greeting.jsx +++ b/src/features/greeting/Greeting.jsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react'; import variables from 'config/variables'; import { nth, convertTimezone } from 'utils/date'; import EventBus from 'utils/eventbus'; +import { useT } from 'contexts'; import './greeting.scss'; /** @@ -44,6 +45,7 @@ const calculateAge = (date) => { }; const Greeting = () => { + const t = useT(); const [greeting, setGreeting] = useState(''); const [display, setDisplay] = useState('block'); const [fontSize, setFontSize] = useState('1.6em'); @@ -63,13 +65,13 @@ const Greeting = () => { let message; switch (true) { case hour < 12: - message = variables.getMessage('widgets.greeting.morning'); + message = t('widgets.greeting.morning'); break; case hour < 18: - message = variables.getMessage('widgets.greeting.afternoon'); + message = t('widgets.greeting.afternoon'); break; default: - message = variables.getMessage('widgets.greeting.evening'); + message = t('widgets.greeting.evening'); break; } @@ -103,10 +105,10 @@ const Greeting = () => { if (birth.getDate() === now.getDate() && birth.getMonth() === now.getMonth()) { if (localStorage.getItem('birthdayage') === 'true' && calculateAge(birth) !== 0) { - const text = variables.getMessage('widgets.greeting.birthday').split(' '); + const text = t('widgets.greeting.birthday').split(' '); message = `${text[0]} ${nth(calculateAge(birth))} ${text[1]}`; } else { - message = variables.getMessage('widgets.greeting.birthday'); + message = t('widgets.greeting.birthday'); } } } @@ -149,7 +151,7 @@ const Greeting = () => { clearTimeout(timerRef.current); } }; - }, []); + }, [t]); return ( diff --git a/src/features/misc/sections/Language.jsx b/src/features/misc/sections/Language.jsx index 20ecfc46..86cabe97 100644 --- a/src/features/misc/sections/Language.jsx +++ b/src/features/misc/sections/Language.jsx @@ -1,6 +1,6 @@ -import variables from 'config/variables'; import { useState, useEffect, useRef } from 'react'; -import { useMessage } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; +import variables from 'config/variables'; import { MdOutlineOpenInNew } from 'react-icons/md'; @@ -9,10 +9,11 @@ import { Radio } from 'components/Form/Settings'; import languages from '@/i18n/languages.json'; const LanguageOptions = () => { - const loadingText = useMessage('modals.main.loading'); - const offlineText = useMessage('modals.main.marketplace.offline.description'); - const languageTitle = useMessage('modals.main.settings.sections.language.title'); - const quoteTitle = useMessage('modals.main.settings.sections.language.quote'); + const t = useT(); + const loadingText = t('modals.main.loading'); + const offlineText = t('modals.main.marketplace.offline.description'); + const languageTitle = t('modals.main.settings.sections.language.title'); + const quoteTitle = t('modals.main.settings.sections.language.quote'); const [quoteLanguages, setQuoteLanguages] = useState([ { diff --git a/src/features/misc/views/Settings.jsx b/src/features/misc/views/Settings.jsx index b1415d67..210f4e19 100644 --- a/src/features/misc/views/Settings.jsx +++ b/src/features/misc/views/Settings.jsx @@ -1,6 +1,5 @@ -import variables from 'config/variables'; import { memo, useMemo } from 'react'; -import { useTranslation } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; import Tabs from 'components/Elements/MainModal/backend/Tabs'; @@ -90,15 +89,15 @@ const sections = [ ]; function Settings(props) { - const { languagecode } = useTranslation(); + const t = useT(); // Recalculate section labels when language changes const translatedSections = useMemo(() => sections.map(section => ({ ...section, - translatedLabel: variables.getMessage(section.label) + translatedLabel: t(section.label) })), - [languagecode] + [t] ); return ( diff --git a/src/features/navbar/components/Maximise.jsx b/src/features/navbar/components/Maximise.jsx index 8d290623..cbab8e0a 100644 --- a/src/features/navbar/components/Maximise.jsx +++ b/src/features/navbar/components/Maximise.jsx @@ -1,11 +1,13 @@ import variables from 'config/variables'; import { memo, useState, useCallback } from 'react'; +import { useT } from 'contexts'; import { MdCropFree } from 'react-icons/md'; import { Tooltip } from 'components/Elements'; const Maximise = memo(({ fontSize }) => { + const t = useT(); const [hidden, setHidden] = useState(false); const setAttribute = useCallback((blur, brightness, filter) => { @@ -44,7 +46,7 @@ const Maximise = memo(({ fontSize }) => { const maximise = useCallback(() => { // hide widgets const widgets = document.getElementById('widgets'); - + if (!hidden) { widgets.style.display = 'none'; setHidden(true); @@ -60,13 +62,13 @@ const Maximise = memo(({ fontSize }) => { return ( diff --git a/src/features/navbar/components/Refresh.jsx b/src/features/navbar/components/Refresh.jsx index 1ab7e6ee..ae46e046 100644 --- a/src/features/navbar/components/Refresh.jsx +++ b/src/features/navbar/components/Refresh.jsx @@ -1,34 +1,39 @@ import { useState, useEffect } from 'react'; -import variables from 'config/variables'; +import { useT } from 'contexts/TranslationContext'; import { MdRefresh } from 'react-icons/md'; import { Tooltip } from 'components/Elements'; import EventBus from 'utils/eventbus'; -import { useTranslation } from 'contexts/TranslationContext'; +import variables from 'config/variables'; function Refresh() { - const { languagecode } = useTranslation(); + const t = useT(); const [refreshText, setRefreshText] = useState(''); const [refreshOption, setRefreshOption] = useState(localStorage.getItem('refreshOption') || ''); useEffect(() => { - EventBus.on('refresh', (data) => { + const handleRefresh = (data) => { if (data === 'navbar' || data === 'background' || data === 'language') { setRefreshOption(localStorage.getItem('refreshOption')); updateRefreshText(); } - }); + }; + EventBus.on('refresh', handleRefresh); updateRefreshText(); - }, [languagecode]); + + return () => { + EventBus.off('refresh', handleRefresh); + }; + }, [t]); function updateRefreshText() { let text; switch (localStorage.getItem('refreshOption')) { case 'background': - text = variables.getMessage('modals.main.settings.sections.background.title'); + text = t('modals.main.settings.sections.background.title'); break; case 'quote': - text = variables.getMessage('modals.main.settings.sections.quote.title'); + text = t('modals.main.settings.sections.quote.title'); break; case 'quotebackground': text = new Intl.ListFormat( @@ -38,14 +43,12 @@ function Refresh() { type: 'conjunction', }, ).format([ - variables.getMessage('modals.main.settings.sections.quote.title'), - variables.getMessage('modals.main.settings.sections.background.title'), + t('modals.main.settings.sections.quote.title'), + t('modals.main.settings.sections.background.title'), ]); break; default: - text = variables.getMessage( - 'modals.main.settings.sections.appearance.navbar.refresh_options.page', - ); + text = t('modals.main.settings.sections.appearance.navbar.refresh_options.page'); break; } diff --git a/src/features/search/Search.jsx b/src/features/search/Search.jsx index 7c92585b..cde4bcce 100644 --- a/src/features/search/Search.jsx +++ b/src/features/search/Search.jsx @@ -3,12 +3,14 @@ import variables from 'config/variables'; import { memo, useRef, useEffect, useState, useCallback } from 'react'; import { MdSearch, MdMic } from 'react-icons/md'; import { Tooltip } from 'components/Elements'; +import { useT } from 'contexts'; import EventBus from 'utils/eventbus'; import './search.scss'; function Search() { + const t = useT(); const [microphone, setMicrophone] = useState(null); const [classList] = useState( localStorage.getItem('widgetStyle') === 'legacy' ? 'searchIcons old' : 'searchIcons', @@ -113,14 +115,14 @@ function Search() {
{microphone}
- + @@ -128,7 +130,7 @@ function Search() {
diff --git a/src/features/welcome/components/Sections/ChooseLanguage.jsx b/src/features/welcome/components/Sections/ChooseLanguage.jsx index 5c77c5fe..a3f3779a 100644 --- a/src/features/welcome/components/Sections/ChooseLanguage.jsx +++ b/src/features/welcome/components/Sections/ChooseLanguage.jsx @@ -1,14 +1,15 @@ -import variables from 'config/variables'; import { MdOutlineOpenInNew } from 'react-icons/md'; import languages from '@/i18n/languages.json'; -import { useMessage } from 'contexts/TranslationContext'; +import { useT } from 'contexts/TranslationContext'; +import variables from 'config/variables'; import { Radio } from 'components/Form/Settings'; import { Header, Content } from '../Layout'; function ChooseLanguage() { - const title = useMessage('modals.welcome.sections.language.title'); - const description = useMessage('modals.welcome.sections.language.description'); + const t = useT(); + const title = t('modals.welcome.sections.language.title'); + const description = t('modals.welcome.sections.language.description'); return (