feat(translation): integrate useT hook for improved translation handling across components

This commit is contained in:
alexsparkes
2026-01-24 18:07:25 +00:00
parent 30aa53fdd7
commit 6ca19fc48d
10 changed files with 268 additions and 279 deletions

View File

@@ -7,15 +7,17 @@ const TranslationContext = createContext();
export function TranslationProvider({ children, initialLanguage }) {
const [currentLanguage, setCurrentLanguage] = useState(initialLanguage);
const i18nInstance = useRef(null);
const i18nInstance = useRef(initTranslations(initialLanguage));
// Initialize i18n instance once
// Update i18n instance when language changes
useEffect(() => {
i18nInstance.current = initTranslations(currentLanguage);
if (currentLanguage !== initialLanguage) {
i18nInstance.current = initTranslations(currentLanguage);
}
variables.language = i18nInstance.current;
variables.languagecode = currentLanguage;
document.documentElement.lang = currentLanguage.replace('_', '-');
}, [currentLanguage]);
}, [currentLanguage, initialLanguage]);
// Change language function
const changeLanguage = useCallback((newLanguage) => {

View File

@@ -1,5 +1,6 @@
import variables from 'config/variables';
import { useState, useEffect, useRef } from 'react';
import { useT } from 'contexts';
import { MdSettings } from 'react-icons/md';
@@ -10,34 +11,36 @@ import EventBus from 'utils/eventbus';
import './scss/index.scss';
const getRefreshText = () => {
switch (localStorage.getItem('refreshOption')) {
case 'background':
return variables.getMessage('modals.main.settings.sections.background.title');
case 'quote':
return variables.getMessage('modals.main.settings.sections.quote.title');
case 'quotebackground':
return (
variables.getMessage('modals.main.settings.sections.quote.title') +
' ' +
variables.getMessage('modals.main.settings.sections.background.title')
);
default:
return variables.getMessage(
'modals.main.settings.sections.appearance.navbar.refresh_options.page',
);
}
};
const getZoomFontSize = () => {
return Number(((localStorage.getItem('zoomNavbar') || 100) / 100) * 1.2) + 'rem';
};
const Navbar = ({ openModal }) => {
const t = useT();
const navbarContainer = useRef();
const [classList] = useState(
localStorage.getItem('widgetStyle') === 'legacy' ? 'navbar old' : 'navbar new',
);
const getRefreshText = () => {
switch (localStorage.getItem('refreshOption')) {
case 'background':
return t('modals.main.settings.sections.background.title');
case 'quote':
return t('modals.main.settings.sections.quote.title');
case 'quotebackground':
return (
t('modals.main.settings.sections.quote.title') +
' ' +
t('modals.main.settings.sections.background.title')
);
default:
return t(
'modals.main.settings.sections.appearance.navbar.refresh_options.page',
);
}
};
const getZoomFontSize = () => {
return Number(((localStorage.getItem('zoomNavbar') || 100) / 100) * 1.2) + 'rem';
};
const [refreshText, setRefreshText] = useState(getRefreshText());
const [refreshEnabled, setRefreshEnabled] = useState(localStorage.getItem('refresh'));
const [refreshOption, setRefreshOption] = useState(localStorage.getItem('refreshOption') || '');
@@ -99,16 +102,16 @@ const Navbar = ({ openModal }) => {
{refreshEnabled !== 'false' && <Refresh fontSize={zoomFontSize} />}
<Tooltip
title={variables.getMessage('modals.main.navbar.settings', {
type: variables.getMessage('modals.main.navbar.tooltips.refresh_' + refreshOption),
title={t('modals.main.navbar.settings', {
type: t('modals.main.navbar.tooltips.refresh_' + refreshOption),
})}
>
<button
className="navbarButton"
onClick={() => openModal('mainModal')}
style={{ fontSize: zoomFontSize }}
aria-label={variables.getMessage('modals.main.navbar.settings', {
type: variables.getMessage('modals.main.navbar.tooltips.refresh_' + refreshOption),
aria-label={t('modals.main.navbar.settings', {
type: t('modals.main.navbar.tooltips.refresh_' + refreshOption),
})}
>
<MdSettings className="settings-icon topicons" />

View File

@@ -1,5 +1,6 @@
import variables from 'config/variables';
import { memo, useState, useEffect } from 'react';
import { useT } from 'contexts';
import { MdContentCopy, MdAssignment, MdPushPin, MdDownload } from 'react-icons/md';
import { useFloating, shift } from '@floating-ui/react-dom';
@@ -11,6 +12,7 @@ import { saveFile } from 'utils/saveFile';
import EventBus from 'utils/eventbus';
const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => {
const t = useT();
const [notes, setNotes] = useState(localStorage.getItem('notes') || '');
const [showNotes, setShowNotes] = useState(localStorage.getItem('notesPinned') === 'true');
const [zoomFontSize, setZoomFontSize] = useState('1.2rem');
@@ -53,7 +55,7 @@ const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => {
const handleCopy = () => {
variables.stats.postEvent('feature', 'Notes copied');
navigator.clipboard.writeText(notes);
toast(variables.getMessage('toasts.notes'));
toast(t('toasts.notes'));
};
const handleDownload = () => {
@@ -74,7 +76,7 @@ const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => {
onBlur={handleHideNotes}
ref={notesRef}
style={{ fontSize: zoomFontSize }}
aria-label={variables.getMessage('widgets.navbar.notes.title')}
aria-label={t('widgets.navbar.notes.title')}
>
<MdAssignment className="topicons" />
</button>
@@ -91,27 +93,27 @@ const Notes = ({ notesRef, floatRef, position, xPosition, yPosition }) => {
<div className="flexNotes">
<div className="topBarNotes" style={{ display: 'flex' }}>
<MdAssignment />
<span>{variables.getMessage('widgets.navbar.notes.title')}</span>
<span>{t('widgets.navbar.notes.title')}</span>
</div>
<div className="notes-buttons">
<Tooltip title={variables.getMessage('widgets.navbar.todo.pin')}>
<Tooltip title={t('widgets.navbar.todo.pin')}>
<button onClick={handlePin}>
<MdPushPin />
</button>
</Tooltip>
<Tooltip title={variables.getMessage('widgets.quote.copy')}>
<Tooltip title={t('widgets.quote.copy')}>
<button onClick={handleCopy} disabled={notes === ''}>
<MdContentCopy />
</button>
</Tooltip>
<Tooltip title={variables.getMessage('widgets.background.download')}>
<Tooltip title={t('widgets.background.download')}>
<button onClick={handleDownload} disabled={notes === ''}>
<MdDownload />
</button>
</Tooltip>
</div>
<TextareaAutosize
placeholder={variables.getMessage('widgets.navbar.notes.placeholder')}
placeholder={t('widgets.navbar.notes.placeholder')}
value={notes}
onChange={handleSetNotes}
minRows={5}

View File

@@ -70,11 +70,11 @@ function Refresh() {
}
return (
<Tooltip title={variables.getMessage('widgets.navbar.tooltips.refresh')} subtitle={refreshText}>
<Tooltip title={t('widgets.navbar.tooltips.refresh')} subtitle={refreshText}>
<button
className="navbarButton"
onClick={refresh}
aria-label={variables.getMessage('widgets.navbar.tooltips.refresh')}
aria-label={t('widgets.navbar.tooltips.refresh')}
>
<MdRefresh className="refreshicon topicons" />
</button>

View File

@@ -1,5 +1,6 @@
import variables from 'config/variables';
import { PureComponent, memo, useState } from 'react';
import { memo, useState, useEffect } from 'react';
import { useT } from 'contexts';
import {
MdChecklist,
@@ -65,206 +66,180 @@ const SortableList = ({ items, onDragEnd, children }) => {
);
};
class Todo extends PureComponent {
constructor() {
super();
this.state = {
todo: JSON.parse(localStorage.getItem('todo')) || [],
visibility: localStorage.getItem('todoPinned') === 'true' ? 'visible' : 'hidden',
marginLeft: localStorage.getItem('refresh') === 'false' ? '-200px' : '-130px',
showTodo: localStorage.getItem('todoPinned') === 'true',
};
}
function Todo({ todoRef, floatRef, position, xPosition, yPosition }) {
const t = useT();
const [todo, setTodo] = useState(JSON.parse(localStorage.getItem('todo')) || []);
const [showTodo, setShowTodo] = useState(localStorage.getItem('todoPinned') === 'true');
const [zoomFontSize, setZoomFontSize] = useState('1.2rem');
setZoom() {
this.setState({
zoomFontSize: Number(((localStorage.getItem('zoomNavbar') || 100) / 100) * 1.2) + 'rem',
});
}
componentDidMount() {
EventBus.on('refresh', (data) => {
useEffect(() => {
const handleRefresh = (data) => {
if (data === 'navbar') {
this.forceUpdate();
try {
this.setZoom();
} catch {
// Ignore errors
}
setZoomFontSize(Number(((localStorage.getItem('zoomNavbar') || 100) / 100) * 1.2) + 'rem');
}
});
};
this.setZoom();
}
setZoomFontSize(Number(((localStorage.getItem('zoomNavbar') || 100) / 100) * 1.2) + 'rem');
componentWillUnmount() {
EventBus.off('refresh');
}
EventBus.on('refresh', handleRefresh);
return () => {
EventBus.off('refresh', handleRefresh);
};
}, []);
/**
* It takes an array, removes an item from it, and then inserts it at a new index.
* @param {Array} array The array to move the item in.
* @param {Number} oldIndex The index of the item to move.
* @param {Number} newIndex The index to move the item to.
* @returns The result of the splice method.
*/
arrayMove(array, oldIndex, newIndex) {
const result = Array.from(array);
const [removed] = result.splice(oldIndex, 1);
result.splice(newIndex, 0, removed);
const handleShowTodo = () => {
setShowTodo(true);
};
return result;
}
const handleHideTodo = () => {
setShowTodo(localStorage.getItem('todoPinned') === 'true');
};
handleDragEnd = (event) => {
const handleDragEnd = (event) => {
const { active, over } = event;
if (over && active.id !== over.id) {
const oldIndex = Number(active.id);
const newIndex = Number(over.id);
this.setState({ todo: arrayMove(this.state.todo, oldIndex, newIndex) });
setTodo((currentTodo) => {
const newTodo = arrayMove(currentTodo, oldIndex, newIndex);
localStorage.setItem('todo', JSON.stringify(newTodo));
return newTodo;
});
}
};
showTodo() {
this.setState({ showTodo: true });
}
hideTodo() {
this.setState({ showTodo: localStorage.getItem('todoPinned') === 'true' });
}
/**
* This function takes in an action, an index, and data, and then updates the todo list accordingly.
* @param {String} action The action to perform. Can be 'add', 'remove', 'set', or 'done'.
* @param {Number} index The index of the item to perform the action on.
* @param {Object} data The data to use for the action.
*/
updateTodo(action, index, data) {
const todo = this.state.todo;
switch (action) {
case 'add':
todo.push({ value: '', done: false });
break;
case 'remove':
todo.splice(index, 1);
break;
case 'set':
todo[index] = { value: data.target.value, done: todo[index].done };
break;
case 'done':
todo[index].done = !todo[index].done;
break;
default:
break;
}
const updateTodo = (action, index, data) => {
setTodo((currentTodo) => {
const newTodo = [...currentTodo];
localStorage.setItem('todo', JSON.stringify(todo));
this.setState({ todo });
this.forceUpdate();
}
switch (action) {
case 'add':
newTodo.push({ value: '', done: false });
break;
case 'remove':
newTodo.splice(index, 1);
break;
case 'set':
newTodo[index] = { value: data.target.value, done: newTodo[index].done };
break;
case 'done':
newTodo[index].done = !newTodo[index].done;
break;
default:
break;
}
pin() {
localStorage.setItem('todo', JSON.stringify(newTodo));
return newTodo;
});
};
const handlePin = () => {
variables.stats.postEvent('feature', 'Todo pin');
const todoPinned = localStorage.getItem('todoPinned') === 'true';
localStorage.setItem('todoPinned', !todoPinned);
this.setState({ showTodo: !todoPinned });
}
setShowTodo(!todoPinned);
};
render() {
return (
<div className="notes" onMouseLeave={() => this.hideTodo()} onFocus={() => this.showTodo()}>
<button
className="navbarButton"
onMouseEnter={() => this.showTodo()}
onFocus={() => this.hideTodo()}
onBlur={() => this.showTodo()}
ref={this.props.todoRef}
style={{ fontSize: this.state.zoomFontSize }}
return (
<div className="notes" onMouseLeave={handleHideTodo} onFocus={handleShowTodo}>
<button
className="navbarButton"
onMouseEnter={handleShowTodo}
onFocus={handleHideTodo}
onBlur={handleShowTodo}
ref={todoRef}
style={{ fontSize: zoomFontSize }}
>
<MdChecklist className="topicons" />
</button>
{showTodo && (
<span
className="notesContainer"
ref={floatRef}
style={{
position: position,
top: yPosition ?? '44px',
left: xPosition ?? '',
}}
>
<MdChecklist className="topicons" />
</button>
{this.state.showTodo && (
<span
className="notesContainer"
ref={this.props.floatRef}
style={{
position: this.props.position,
top: this.props.yPosition ?? '44px',
left: this.props.xPosition ?? '',
}}
>
<div className="flexTodo">
<div className="topBarNotes" style={{ display: 'flex' }}>
<MdChecklist />
<span>{variables.getMessage('widgets.navbar.todo.title')}</span>
</div>
<div className="notes-buttons">
<Tooltip title={variables.getMessage('widgets.navbar.todo.pin')}>
<button onClick={() => this.pin()}>
<MdPushPin />
</button>
</Tooltip>
<Tooltip title={variables.getMessage('widgets.navbar.todo.add')}>
<button onClick={() => this.updateTodo('add')}>
<MdPlaylistAdd />
</button>
</Tooltip>
</div>
<div className={'todoRows'}>
{this.state.todo.length === 0 ? (
<div className="todosEmpty">
<div className="emptyNewMessage">
<MdPlaylistRemove />
<span className="title">
{variables.getMessage('widgets.navbar.todo.no_todos')}
</span>
<span className="subtitle">
{variables.getMessage('modals.main.settings.sections.message.add_some')}
</span>
</div>
</div>
) : (
<SortableList
items={this.state.todo.map((_, index) => index)}
onDragEnd={this.handleDragEnd}
>
{this.state.todo.map((todo, index) => (
<SortableItem key={index} id={index}>
{({ attributes, listeners }) => (
<div className={'todoRow' + (todo.done ? ' done' : '')}>
<Checkbox
checked={todo.done}
onClick={() => this.updateTodo('done', index)}
/>
<TextareaAutosize
placeholder={variables.getMessage('widgets.navbar.notes.placeholder')}
value={todo.value}
onChange={(data) => this.updateTodo('set', index, data)}
readOnly={todo.done}
/>
<Tooltip
title={variables.getMessage(
'modals.main.marketplace.product.buttons.remove',
)}
>
<MdDelete onClick={() => this.updateTodo('remove', index)} />
</Tooltip>
<DragHandle {...attributes} {...listeners} />
</div>
)}
</SortableItem>
))}
</SortableList>
)}
</div>
<div className="flexTodo">
<div className="topBarNotes" style={{ display: 'flex' }}>
<MdChecklist />
<span>{t('widgets.navbar.todo.title')}</span>
</div>
</span>
)}
</div>
);
}
<div className="notes-buttons">
<Tooltip title={t('widgets.navbar.todo.pin')}>
<button onClick={handlePin}>
<MdPushPin />
</button>
</Tooltip>
<Tooltip title={t('widgets.navbar.todo.add')}>
<button onClick={() => updateTodo('add')}>
<MdPlaylistAdd />
</button>
</Tooltip>
</div>
<div className={'todoRows'}>
{todo.length === 0 ? (
<div className="todosEmpty">
<div className="emptyNewMessage">
<MdPlaylistRemove />
<span className="title">
{t('widgets.navbar.todo.no_todos')}
</span>
<span className="subtitle">
{t('modals.main.settings.sections.message.add_some')}
</span>
</div>
</div>
) : (
<SortableList
items={todo.map((_, index) => index)}
onDragEnd={handleDragEnd}
>
{todo.map((todoItem, index) => (
<SortableItem key={index} id={index}>
{({ attributes, listeners }) => (
<div className={'todoRow' + (todoItem.done ? ' done' : '')}>
<Checkbox
checked={todoItem.done}
onClick={() => updateTodo('done', index)}
/>
<TextareaAutosize
placeholder={t('widgets.navbar.notes.placeholder')}
value={todoItem.value}
onChange={(data) => updateTodo('set', index, data)}
readOnly={todoItem.done}
/>
<Tooltip
title={t(
'modals.main.marketplace.product.buttons.remove',
)}
>
<MdDelete onClick={() => updateTodo('remove', index)} />
</Tooltip>
<DragHandle {...attributes} {...listeners} />
</div>
)}
</SortableItem>
))}
</SortableList>
)}
</div>
</div>
</span>
)}
</div>
);
}
function TodoWrapper() {

View File

@@ -1,6 +1,7 @@
import variables from 'config/variables';
import { useState } from 'react';
import { useT } from 'contexts';
import Modal from 'react-modal';
import EventBus from 'utils/eventbus';
@@ -15,6 +16,7 @@ import { getTitleFromUrl, isValidUrl } from 'utils/links';
import { QuickLink } from 'features/quicklinks/options/QuickLink';
function AppsOptions({ appsEnabled }) {
const t = useT();
const [appsModalInfo, setAppsModalInfo] = useState({
newLink: false,
edit: false,
@@ -34,14 +36,14 @@ function AppsOptions({ appsEnabled }) {
if (url.length <= 0 || isValidUrl(url) === false) {
return setAppsModalInfo((oldState) => ({
...oldState,
urlError: variables.getMessage('widgets.quicklinks.url_error'),
urlError: t('widgets.quicklinks.url_error'),
}));
}
if (icon.length > 0 && isValidUrl(icon) === false) {
return setAppsModalInfo((oldState) => ({
...oldState,
iconError: variables.getMessage('widgets.quicklinks.icon_error'),
iconError: t('widgets.quicklinks.icon_error'),
}));
}
@@ -114,8 +116,8 @@ function AppsOptions({ appsEnabled }) {
<>
<Row final={true} inactive={!appsEnabled}>
<Content
title={variables.getMessage('widgets.navbar.apps.title')}
subtitle={variables.getMessage(
title={t('widgets.navbar.apps.title')}
subtitle={t(
'modals.main.settings.sections.appearance.navbar.apps_subtitle',
)}
/>
@@ -124,7 +126,7 @@ function AppsOptions({ appsEnabled }) {
type="settings"
onClick={() => setAppsModalInfo((oldState) => ({ ...oldState, newLink: true }))}
icon={<MdAddLink />}
label={variables.getMessage('modals.main.settings.sections.quicklinks.add_link')}
label={t('modals.main.settings.sections.quicklinks.add_link')}
/>
</Action>
</Row>

View File

@@ -1,6 +1,7 @@
import variables from 'config/variables';
import { useState, memo } from 'react';
import { useT } from 'contexts';
import { MdAssignment, MdCropFree, MdRefresh, MdChecklist, MdOutlineApps } from 'react-icons/md';
@@ -13,6 +14,7 @@ import { Header } from 'components/Layout/Settings';
import AppsOptions from './AppsOptions';
function NavbarOptions() {
const t = useT();
const [showRefreshOptions, setShowRefreshOptions] = useState(
localStorage.getItem('refresh') === 'true',
);
@@ -26,15 +28,15 @@ function NavbarOptions() {
return (
<Row final={false}>
<Content
title={variables.getMessage('modals.main.settings.additional_settings')}
subtitle={variables.getMessage(
title={t('modals.main.settings.additional_settings')}
subtitle={t(
'modals.main.settings.sections.appearance.navbar.additional',
)}
/>
<Action>
<Checkbox
name="navbarHover"
text={variables.getMessage(`${NAVBAR_SECTION}.hover`)}
text={t(`${NAVBAR_SECTION}.hover`)}
category="navbar"
/>
</Action>
@@ -70,7 +72,7 @@ function NavbarOptions() {
className={`navbarButtonOption ${isDisabled === true ? 'disabled' : ''}`}
>
{icon}
<span className="subtitle">{variables.getMessage(messageKey)}</span>
<span className="subtitle">{t(messageKey)}</span>
</button>
);
};
@@ -106,7 +108,7 @@ function NavbarOptions() {
return (
<Row>
<Content
title={variables.getMessage('modals.main.settings.sections.appearance.navbar.widgets')}
title={t('modals.main.settings.sections.appearance.navbar.widgets')}
/>
<Action>
<div className="navbarButtonOptions">
@@ -123,8 +125,8 @@ function NavbarOptions() {
return (
<Row final={false} inactive={!showRefreshOptions}>
<Content
title={variables.getMessage(`${NAVBAR_SECTION}.refresh`)}
subtitle={variables.getMessage(
title={t(`${NAVBAR_SECTION}.refresh`)}
subtitle={t(
'modals.main.settings.sections.appearance.navbar.refresh_subtitle',
)}
/>
@@ -135,24 +137,24 @@ function NavbarOptions() {
items={[
{
value: 'page',
text: variables.getMessage(
text: t(
'modals.main.settings.sections.appearance.navbar.refresh_options.page',
),
},
{
value: 'background',
text: variables.getMessage('modals.main.settings.sections.background.title'),
text: t('modals.main.settings.sections.background.title'),
},
{
value: 'quote',
text: variables.getMessage('modals.main.settings.sections.quote.title'),
text: t('modals.main.settings.sections.quote.title'),
},
{
value: 'quotebackground',
text:
variables.getMessage('modals.main.settings.sections.quote.title') +
t('modals.main.settings.sections.quote.title') +
' + ' +
variables.getMessage('modals.main.settings.sections.background.title'),
t('modals.main.settings.sections.background.title'),
},
]}
/>
@@ -164,7 +166,7 @@ function NavbarOptions() {
return (
<>
<Header
title={variables.getMessage(`${NAVBAR_SECTION}.title`)}
title={t(`${NAVBAR_SECTION}.title`)}
setting="navbar"
category="widgets"
zoomSetting="zoomNavbar"

View File

@@ -1,6 +1,6 @@
import { MdPerson, MdOpenInNew } from 'react-icons/md';
import { Tooltip } from 'components/Elements';
import variables from 'config/variables';
import { useT } from 'contexts';
import QuoteButtons from './QuoteButtons';
/**
@@ -17,9 +17,10 @@ export default function AuthorInfo({
onShare,
isFavourited,
}) {
const t = useT();
const showAuthorImg = localStorage.getItem('authorImg') !== 'false';
const hasLink = authorOccupation !== 'Unknown' && authorlink !== null;
const trimmedLicense = authorimglicense?.substring(0, 40) +
const trimmedLicense = authorimglicense?.substring(0, 40) +
(authorimglicense?.length > 40 ? '…' : '');
return (
@@ -30,7 +31,7 @@ export default function AuthorInfo({
{!authorimg && authorimg !== undefined && <MdPerson />}
</div>
)}
{author ? (
<div className="author-content">
<span className="title">{author}</span>
@@ -49,10 +50,10 @@ export default function AuthorInfo({
<span className="subtitle pulse">loading</span>
</div>
)}
<div className="quote-buttons">
{hasLink && (
<Tooltip title={variables.getMessage('widgets.quote.link_tooltip')}>
<Tooltip title={t('widgets.quote.link_tooltip')}>
<a
href={authorlink}
className="quoteAuthorLink"

View File

@@ -1,16 +1,18 @@
import { MdContentCopy, MdStarBorder, MdStar, MdIosShare } from 'react-icons/md';
import { Tooltip } from 'components/Elements';
import { useT } from 'contexts';
import variables from 'config/variables';
/**
* Quote action buttons component
*/
export default function QuoteButtons({
onCopy,
onFavourite,
onShare,
export default function QuoteButtons({
onCopy,
onFavourite,
onShare,
isFavourited,
}) {
const t = useT();
const showCopy = localStorage.getItem('copyButton') !== 'false';
const showShare = localStorage.getItem('quoteShareButton') !== 'false';
const showFavourite = localStorage.getItem('favouriteQuoteEnabled') === 'true';
@@ -18,39 +20,39 @@ export default function QuoteButtons({
return (
<>
{showCopy && (
<Tooltip title={variables.getMessage('widgets.quote.copy')}>
<Tooltip title={t('widgets.quote.copy')}>
<button
onClick={onCopy}
aria-label={variables.getMessage('widgets.quote.copy')}
aria-label={t('widgets.quote.copy')}
>
<MdContentCopy className="copyButton" />
</button>
</Tooltip>
)}
{showShare && (
<Tooltip title={variables.getMessage('widgets.quote.share')}>
<Tooltip title={t('widgets.quote.share')}>
<button
onClick={onShare}
aria-label={variables.getMessage('widgets.quote.share')}
aria-label={t('widgets.quote.share')}
>
<MdIosShare className="copyButton" />
</button>
</Tooltip>
)}
{showFavourite && (
<Tooltip
<Tooltip
title={
isFavourited
? variables.getMessage('widgets.quote.unfavourite')
: variables.getMessage('widgets.quote.favourite')
? t('widgets.quote.unfavourite')
: t('widgets.quote.favourite')
}
>
<button
onClick={onFavourite}
aria-label={
isFavourited
? variables.getMessage('widgets.quote.unfavourite')
: variables.getMessage('widgets.quote.favourite')
? t('widgets.quote.unfavourite')
: t('widgets.quote.favourite')
}
>
{isFavourited ? (

View File

@@ -1,38 +1,38 @@
import I18n from '@eartharoid/i18n';
import * as ar from 'translations/ar.json';
import * as arz from 'translations/arz.json';
import * as az from 'translations/az.json';
import * as azb from 'translations/azb.json';
import * as bn from 'translations/bn.json';
import * as de_DE from 'translations/de_DE.json';
import * as el from 'translations/el.json';
import * as en_GB from 'translations/en_GB.json';
import * as en_US from 'translations/en_US.json';
import * as es from 'translations/es.json';
import * as es_419 from 'translations/es_419.json';
import * as et from 'translations/et.json';
import * as fa from 'translations/fa.json';
import * as fr from 'translations/fr.json';
import * as hu from 'translations/hu.json';
import * as id_ID from 'translations/id_ID.json';
import * as ja from 'translations/ja.json';
import * as lt from 'translations/lt.json';
import * as lv from 'translations/lv.json';
import * as nl from 'translations/nl.json';
import * as no from 'translations/no.json';
import * as peo from 'translations/peo.json';
import * as pt from 'translations/pt.json';
import * as pt_BR from 'translations/pt_BR.json';
import * as ru from 'translations/ru.json';
import * as sl from 'translations/sl.json';
import * as sv from 'translations/sv.json';
import * as ta from 'translations/ta.json';
import * as tr_TR from 'translations/tr_TR.json';
import * as uk from 'translations/uk.json';
import * as vi from 'translations/vi.json';
import * as zh_CN from 'translations/zh_CN.json';
import * as zh_Hant from 'translations/zh_Hant.json';
import ar from 'translations/ar.json';
import arz from 'translations/arz.json';
import az from 'translations/az.json';
import azb from 'translations/azb.json';
import bn from 'translations/bn.json';
import de_DE from 'translations/de_DE.json';
import el from 'translations/el.json';
import en_GB from 'translations/en_GB.json';
import en_US from 'translations/en_US.json';
import es from 'translations/es.json';
import es_419 from 'translations/es_419.json';
import et from 'translations/et.json';
import fa from 'translations/fa.json';
import fr from 'translations/fr.json';
import hu from 'translations/hu.json';
import id_ID from 'translations/id_ID.json';
import ja from 'translations/ja.json';
import lt from 'translations/lt.json';
import lv from 'translations/lv.json';
import nl from 'translations/nl.json';
import no from 'translations/no.json';
import peo from 'translations/peo.json';
import pt from 'translations/pt.json';
import pt_BR from 'translations/pt_BR.json';
import ru from 'translations/ru.json';
import sl from 'translations/sl.json';
import sv from 'translations/sv.json';
import ta from 'translations/ta.json';
import tr_TR from 'translations/tr_TR.json';
import uk from 'translations/uk.json';
import vi from 'translations/vi.json';
import zh_CN from 'translations/zh_CN.json';
import zh_Hant from 'translations/zh_Hant.json';
/**
* Initialise the i18n object.