Fix localisation (#3123)

* fix localised errors

* fix locales
This commit is contained in:
Andrey Babushkin 2025-03-10 15:51:21 +01:00 committed by GitHub
parent c6cbc4eba8
commit eab2d3a2cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 344 additions and 93 deletions

View file

@ -228,7 +228,10 @@ function AlertForm(props) {
<div className="w-4/6 flex items-center">
<Select
placeholder={t('Select Condition')}
options={conditions}
options={conditions.map((c) => ({
...c,
label: t(c.label),
}))}
name="operator"
defaultValue={instance.query.operator}
// onChange={ writeQueryOption }

View file

@ -6,10 +6,12 @@ import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import { Badge, Button, Tooltip } from 'antd';
import { BellOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
const AUTOREFRESH_INTERVAL = 5 * 60 * 1000;
function Notifications() {
const { t } = useTranslation();
const { showModal } = useModal();
const { notificationStore } = useStore();
const count = notificationStore.notificationsCount;
@ -28,7 +30,7 @@ function Notifications() {
return (
<Badge dot={count > 0} size="small">
<Tooltip title="Alerts">
<Tooltip title={t('Alerts')}>
<Button
icon={<BellOutlined />}
onClick={() => showModal(<AlertTriggersModal />, { right: true })}

View file

@ -258,7 +258,7 @@ function AssistActions({ userId, isCallActive, agentIds }: Props) {
<ScreenRecorder />
{/* @ts-ignore */}
<Tooltip title="Call user to initiate remote control" disabled={livePlay}>
<Tooltip title={t('Call user to initiate remote control')} disabled={livePlay}>
<div
className={cn('cursor-pointer p-2 flex items-center', {
[stl.disabled]:

View file

@ -1,10 +1,10 @@
import React, { useEffect } from "react";
import { Pagination, NoContent, Icon } from "UI";
import ErrorListItem from "App/components/Dashboard/components/Errors/ErrorListItem";
import { withRouter, RouteComponentProps } from "react-router-dom";
import { useModal } from "App/components/Modal";
import ErrorDetailsModal from "App/components/Dashboard/components/Errors/ErrorDetailsModal";
import { useTranslation } from "react-i18next";
import React, { useEffect } from 'react';
import { Pagination, NoContent, Icon } from 'UI';
import ErrorListItem from 'App/components/Dashboard/components/Errors/ErrorListItem';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useModal } from 'App/components/Modal';
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
import { useTranslation } from 'react-i18next';
interface Props {
metric: any;
@ -14,9 +14,10 @@ interface Props {
location: any;
}
function CustomMetricTableErrors(props: RouteComponentProps & Props) {
const { metric, data } = props;
const errorId = new URLSearchParams(props.location.search).get("errorId");
const { showModal, hideModal } = useModal();
const { t } = useTranslation();
const { metric, data } = props;
const errorId = new URLSearchParams(props.location.search).get('errorId');
const { showModal, hideModal } = useModal();
const onErrorClick = (e: any, error: any) => {
e.stopPropagation();

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { Input } from 'UI';
import Select from 'Shared/Select';
import { alertConditions as conditions } from 'App/constants';
@ -41,7 +41,13 @@ function Condition({
unit,
changeUnit,
}: ICondition) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const localizedConditions = useMemo(() => conditions.map((c) => ({
...c,
label: t(c.label),
})), [i18n.language]);
return (
<div>
{!isThreshold && (
@ -85,10 +91,10 @@ function Condition({
<div className="w-2/6 flex items-center">
<Select
placeholder={t('Select Condition')}
options={conditions}
options={localizedConditions}
name="operator"
value={
conditions.find((c) => c.value === instance.query.operator) || ''
localizedConditions.find((c) => c.value === instance.query.operator) || ''
}
onChange={({ value }) =>
writeQueryOption(null, { name: 'operator', value: value.value })

View file

@ -35,7 +35,7 @@ function DashboardOptions(props: Props) {
{
icon: <Icon name="trash" />,
key: 'delete',
label: 'Delete',
label: t('Delete'),
onClick: deleteHandler,
},
{

View file

@ -7,6 +7,7 @@ import { Button, Popover, Tooltip } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { Loader } from 'UI';
import AddCardSection from '../AddCardSection/AddCardSection';
import { useTranslation } from 'react-i18next';
interface Props {
siteId: string;
@ -53,6 +54,7 @@ function DashboardWidgetGrid(props: Props) {
}
function GridItem({ item, index, dashboard, dashboardId, siteId }: any) {
const { t } = useTranslation();
const [popoverOpen, setPopoverOpen] = React.useState(false);
const handleOpenChange = (open: boolean) => {
setPopoverOpen(open);
@ -92,7 +94,7 @@ function GridItem({ item, index, dashboard, dashboardId, siteId }: any) {
content={<AddCardSection handleOpenChange={handleOpenChange} />}
trigger="click"
>
<Tooltip title="Add Card">
<Tooltip title={t('Add Card')}>
<Button
icon={<PlusOutlined size={14} />}
shape="circle"

View file

@ -1,5 +1,6 @@
import React, { useState, useRef, useEffect } from 'react';
import { Input, Tooltip } from 'antd';
import { useTranslation } from 'react-i18next';
interface Props {
name: string;
@ -9,6 +10,7 @@ interface Props {
}
function SeriesName(props: Props) {
const { t } = useTranslation();
const { seriesIndex = 1 } = props;
const [editing, setEditing] = useState(false);
const [name, setName] = useState(props.name);
@ -56,7 +58,7 @@ function SeriesName(props: Props) {
size="small"
/>
) : (
<Tooltip title="Click to rename">
<Tooltip title={t('Click to rename')}>
<div
className="text-lg font-medium h-8 flex items-center border-transparent p-2 hover:bg-teal/10 cursor-pointer rounded-lg btn-input-rename-series"
onClick={() => setEditing(true)}

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Tooltip } from 'UI';
const MIN_WIDTH = '20px';
@ -6,6 +7,7 @@ interface Props {
issue: any;
}
function FunnelIssueGraph(props: Props) {
const { t } = useTranslation();
const { issue } = props;
return (
@ -17,7 +19,7 @@ function FunnelIssueGraph(props: Props) {
}}
className="relative"
>
<Tooltip title="Unaffected sessions" placement="top">
<Tooltip title={t('Unaffected sessions')} placement="top">
<div
className="w-full relative rounded-tl-sm rounded-bl-sm"
style={{
@ -34,7 +36,7 @@ function FunnelIssueGraph(props: Props) {
style={{ width: `${issue.affectedSessionsPer}%`, minWidth: MIN_WIDTH }}
className="border-l relative"
>
<Tooltip title="Affected sessions" placement="top">
<Tooltip title={t('Affected sessions')} placement="top">
<div
className="w-full relative"
style={{
@ -51,7 +53,7 @@ function FunnelIssueGraph(props: Props) {
style={{ width: `${issue.lostConversionsPer}%`, minWidth: MIN_WIDTH }}
className="border-l relative"
>
<Tooltip title="Conversion lost" placement="top">
<Tooltip title={t('Conversion lost')} placement="top">
<div
className="w-full relative rounded-tr-sm rounded-br-sm"
style={{ height: '18px', backgroundColor: 'rgba(204, 0, 0, 0.26)' }}

View file

@ -62,7 +62,7 @@ function WidgetName(props: Props) {
/>
) : (
// @ts-ignore
<Tooltip mouseEnterDelay={1} title="Click to edit" disabled={!canEdit}>
<Tooltip mouseEnterDelay={1} title={t('Click to edit')} disabled={!canEdit}>
<div
onClick={() => setEditing(true)}
className={cn(

View file

@ -13,8 +13,10 @@ import React from 'react';
import { useModal } from 'Components/ModalContext';
import AlertFormModal from 'Components/Alerts/AlertFormModal/AlertFormModal';
import { showAddToDashboardModal } from 'Components/Dashboard/components/AddToDashboardButton';
import { useTranslation } from 'react-i18next';
function CardViewMenu() {
const { t } = useTranslation();
const history = useHistory();
const { alertsStore, metricStore, dashboardStore } = useStore();
const widget = metricStore.instance;
@ -32,29 +34,29 @@ function CardViewMenu() {
const items: MenuProps['items'] = [
{
key: 'add-to-dashboard',
label: 'Add to Dashboard',
label: t('Add to Dashboard'),
icon: <Grid2x2Plus size={16} />,
disabled: !widget.exists(),
onClick: () => showAddToDashboardModal(widget.metricId, dashboardStore),
},
{
key: 'alert',
label: 'Set Alerts',
label: t('Set Alerts'),
icon: <BellIcon size={16} />,
disabled: !widget.exists() || widget.metricType !== 'timeseries',
onClick: showAlertModal,
},
{
key: 'remove',
label: 'Delete',
label: t('Delete'),
icon: <TrashIcon size={15} />,
disabled: !widget.exists(),
onClick: () => {
Modal.confirm({
title: 'Confirm Card Deletion',
title: t('Confirm Card Deletion'),
icon: null,
content:
'Are you sure you want to remove this card? This action is permanent and cannot be undone.',
t('Are you sure you want to remove this card? This action is permanent and cannot be undone.'),
footer: (_, { OkBtn, CancelBtn }) => (
<>
<CancelBtn />
@ -68,7 +70,7 @@ function CardViewMenu() {
history.goBack();
})
.catch(() => {
toast.error('Failed to remove card');
toast.error(t('Failed to remove card'));
});
},
});

View file

@ -205,7 +205,7 @@ function WidgetView({
{
value: 'flex-row',
icon: (
<Tooltip title="Filters on Left">
<Tooltip title={t('Filters on Left')}>
<LayoutPanelLeft size={16} />
</Tooltip>
),
@ -213,7 +213,7 @@ function WidgetView({
{
value: 'flex-col',
icon: (
<Tooltip title="Filters on Top">
<Tooltip title={t('Filters on Top')}>
<LayoutPanelTop size={16} />
</Tooltip>
),
@ -221,7 +221,7 @@ function WidgetView({
{
value: 'flex-row-reverse',
icon: (
<Tooltip title="Filters on Right">
<Tooltip title={t('Filters on Right')}>
<div className="rotate-180">
<LayoutPanelLeft size={16} />
</div>

View file

@ -8,6 +8,7 @@ import { Button, Space, Tooltip } from 'antd';
import CardViewMenu from 'Components/Dashboard/components/WidgetView/CardViewMenu';
import { Link2 } from 'lucide-react';
import copy from 'copy-to-clipboard';
import { useTranslation } from 'react-i18next';
interface Props {
onClick?: () => void;
@ -17,15 +18,16 @@ interface Props {
isPreview?: boolean;
}
const defaultText = 'Copy link to clipboard';
function WidgetViewHeader({
onClick,
onSave,
layoutControl,
isPreview,
}: Props) {
const { t } = useTranslation();
const defaultText = t('Copy link to clipboard');
const [tooltipText, setTooltipText] = React.useState(defaultText);
const { metricStore } = useStore();
const widget = metricStore.instance;
@ -39,7 +41,7 @@ function WidgetViewHeader({
const copyUrl = () => {
const url = window.location.href;
copy(url);
setTooltipText('Link copied to clipboard!');
setTooltipText(t('Link copied to clipboard!'));
setTimeout(() => setTooltipText(defaultText), 2000);
};
return (
@ -73,7 +75,7 @@ function WidgetViewHeader({
className="font-medium btn-update-card"
size="small"
>
{widget.exists() ? 'Update' : 'Create'}
{widget.exists() ? t('Update') : t('Create')}
</Button>
{/* <MetricTypeSelector /> */}

View file

@ -2,6 +2,7 @@ import React from 'react';
import cn from 'classnames';
import { Icon, Tooltip } from 'UI';
import stl from './funnelMenuItem.module.css';
import { useTranslation } from 'react-i18next';
function FunnelMenuItem({
iconName = 'info',
@ -12,6 +13,7 @@ function FunnelMenuItem({
isPublic = false,
onClick,
}) {
const { t } = useTranslation();
return (
<div
className={cn(
@ -41,7 +43,7 @@ function FunnelMenuItem({
</div>
<div className="flex items-center">
<div className={cn('mx-2', { invisible: !isPublic })}>
<Tooltip title="Shared with team">
<Tooltip title={t('Shared with team')}>
<div
className={cn(
'bg-gray-light h-8 w-8 rounded-full flex items-center justify-center',

View file

@ -4,7 +4,7 @@ import { Tooltip } from 'UI';
function IssueGraph({ issue }) {
return (
<div className="flex rounded-sm" style={{ width: '600px' }}>
<Tooltip title="Unaffected sessions">
<Tooltip title={t('Unaffected sessions')}>
<div
style={{ width: `${issue.unaffectedSessionsPer}%` }}
className="relative"
@ -21,7 +21,7 @@ function IssueGraph({ issue }) {
</div>
</div>
</Tooltip>
<Tooltip title="Affected sessions">
<Tooltip title={t('Affected sessions')}>
<div
style={{ width: `${issue.affectedSessionsPer}%` }}
className="border-l relative"
@ -39,7 +39,7 @@ function IssueGraph({ issue }) {
{/* <div className="absolute left-0 ml-1 text-xs">{issue.affectedSessionsPer}</div> */}
</div>
</Tooltip>
<Tooltip title="Conversion lost">
<Tooltip title={t('Conversion lost')}>
<div
style={{ width: `${issue.lostConversionsPer}%` }}
className="border-l relative"

View file

@ -52,7 +52,7 @@ function HighlightsListHeader({
: 'capitalize'
}
>
{tag.toLowerCase()}
{(t(tag.toLowerCase()))}
</div>
),
})),

View file

@ -10,8 +10,10 @@ import {
CaretRightOutlined,
PauseOutlined,
} from './.store/@ant-design-icons-virtual-de151eefe5/package';
import { useTranslation } from 'react-i18next';
function AutoplayToggle() {
const { t } = useTranslation();
const { clipStore } = useStore();
const playerContext = React.useContext<IPlayerContext>(PlayerContext);
// const { player, store } = playerContext;
@ -22,7 +24,7 @@ function AutoplayToggle() {
};
return (
<Tooltip title="Toggle Autoplay" placement="bottom">
<Tooltip title={t('Toggle Autoplay')} placement="bottom">
<Switch
className="custom-switch"
onChange={handleToggle}

View file

@ -9,6 +9,7 @@ import * as routes from '@/routes';
import { useStore } from '@/mstore';
import { LinkIcon, X } from 'lucide-react';
import { PartialSessionBadge } from 'Components/Session_/WarnBadge';
import { useTranslation } from 'react-i18next';
interface Props {
session: Session;
@ -18,6 +19,7 @@ interface Props {
}
function ClipPlayerHeader(props: Props) {
const { t } = useTranslation();
const { projectsStore } = useStore();
const { session, range, onClose, isHighlight } = props;
const { siteId } = projectsStore;
@ -35,7 +37,7 @@ function ClipPlayerHeader(props: Props) {
<UserCard session={props.session} />
<Space>
<Tooltip title="Copy link to clipboard" placement="bottom">
<Tooltip title={t('Copy link to clipboard')} placement="bottom">
<Button
onClick={copyHandler}
size="small"

View file

@ -4,8 +4,10 @@ import { Link2 } from 'lucide-react';
import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite';
import SessionTabs from 'Components/Session/Player/SharedComponents/SessionTabs';
import { useTranslation } from 'react-i18next';
function SubHeader() {
const { t } = useTranslation();
const { store } = React.useContext(PlayerContext);
const { location: currentLocation = 'loading...' } = store.get();
@ -23,7 +25,7 @@ function SubHeader() {
<div className="w-full bg-white border-b border-gray-lighter">
<div className="flex w-fit items-center cursor-pointer color-gray-medium text-sm p-1">
<Link2 className="mx-2" size={16} />
<Tooltip title="Open in new tab" delay={0} placement="bottom">
<Tooltip title={t('Open in new tab')} delay={0} placement="bottom">
<a href={location} target="_blank" rel="noreferrer">
{location}
</a>

View file

@ -11,6 +11,7 @@ import { useModal } from 'Components/ModalContext';
import { PlayerContext } from 'Components/Session/playerContext';
import HighlightButton from 'Components/Session_/Highlight/HighlightButton';
import IssueForm from 'Components/Session_/Issues/IssueForm';
import { useTranslation } from 'react-i18next';
interface Props {
setActiveTab: (tab: string) => void;
@ -18,6 +19,7 @@ interface Props {
}
function SubHeader(props: Props) {
const { t } = useTranslation();
const { sessionStore, integrationsStore, issueReportingStore } = useStore();
const integrations = integrationsStore.issues.list;
const isIOS = sessionStore.current.platform === 'ios';
@ -66,7 +68,7 @@ function SubHeader(props: Props) {
<HighlightButton onClick={() => props.setActiveTab('HIGHLIGHT')} />
{enabledIntegration && <Issues sessionId={props.sessionId} />}
<Bookmark sessionId={props.sessionId} />
<Tooltip title="Share Session" placement="bottom">
<Tooltip title={t('Share Session')} placement="bottom">
<AntButton
size="small"
className="flex items-center justify-center"
@ -77,7 +79,7 @@ function SubHeader(props: Props) {
hideModal={closeModal}
time={store?.get().time}
/>,
{ title: 'Share Session' },
{ title: t('Share Session') },
)
}
>

View file

@ -35,7 +35,7 @@ const CopyableTextArea: React.FC<CopyableTextAreaProps> = ({
style={{ paddingRight: '40px' }}
placeholder={t('Enter selector to tag elements. E.g. .btn-primary')}
/>
<Tooltip title="Copy">
<Tooltip title={t('Copy')}>
<Button
type="text"
icon={<CopyOutlined />}

View file

@ -28,6 +28,8 @@ const MODES = {
}
function EventsBlock(props: IProps) {
const defaultFramework = getDefaultFramework();
const [mode, setMode] = React.useState(MODES.SELECT);
const { t } = useTranslation();
const { notesStore, uxtestingStore, uiPlayerStore, sessionStore } =
useStore();

View file

@ -2,13 +2,15 @@ import React from 'react';
import { Button, Tooltip } from 'antd';
import { MessageSquareQuote } from 'lucide-react';
import { Icon } from 'UI';
import { useTranslation } from 'react-i18next';
function HighlightButton({ onClick }: { onClick: () => void }) {
const { t } = useTranslation();
const openPanel = () => {
onClick();
};
return (
<Tooltip title="Highlight a moment" placement="bottom">
<Tooltip title={t('Highlight a moment')} placement="bottom">
<Button onClick={openPanel} size="small">
<Icon name="chat-square-quote" color="inherit" size={15} />
</Button>

View file

@ -37,7 +37,7 @@ function Issues({ sessionId }) {
)}
>
<div>
<Tooltip title="Create Issue" placement="bottom">
<Tooltip title={t('Create Issue')} placement="bottom">
<Button size="small" className="flex items-center justify-center">
<Icon name={`integrations/${provider ?? 'github'}`} />
</Button>

View file

@ -277,20 +277,21 @@ function Storage() {
style={{ paddingRight: 30, marginLeft: 'auto' }}
className="font-semibold"
>
<Tooltip title="Time to execute">{t('TTE')}</Tooltip>
<Tooltip title={t('Time to execute')}>{t('TTE')}</Tooltip>
</h3>
<Segmented options={[{ label: 'Current Tab', value: 'all' }]} />
</div>
</BottomBlock.Header>
<BottomBlock.Content className="flex">
<NoContent
title="Nothing to display yet"
title={t('Nothing to display yet')}
subtext={
!hintIsHidden ? (
<>
{
'Inspect your application state while youre replaying your users sessions. OpenReplay supports '
}
{t(
'Inspect your application state while youre replaying your users sessions. OpenReplay supports',
)}
&nbsp;
<a
className="underline color-teal"
href="https://docs.openreplay.com/plugins/redux"

View file

@ -238,7 +238,7 @@ function SubHeader(props) {
<div className="w-full bg-white border-b border-gray-lighter">
<div className="flex w-fit items-center cursor-pointer color-gray-medium text-sm p-1">
<Link2 className="mx-2" size={16} />
<Tooltip title="Open in new tab" delay={0} placement="bottom">
<Tooltip title={t('Open in new tab')} delay={0} placement="bottom">
<a
href={currentLocation}
target="_blank"

View file

@ -4,8 +4,10 @@ import { Tooltip } from 'antd';
import { Icon } from 'UI';
import { Link2 } from 'lucide-react';
import spotPlayerStore from '../spotPlayerStore';
import { useTranslation } from 'react-i18next';
function SpotLocation() {
const { t } = useTranslation();
const currUrl = spotPlayerStore.getClosestLocation(
spotPlayerStore.time,
)?.location;
@ -15,7 +17,7 @@ function SpotLocation() {
<div className="w-full bg-white border-b border-gray-lighter">
<div className="flex w-fit items-center cursor-pointer color-gray-medium text-sm p-1">
<Link2 className="mx-2" size={16} />
<Tooltip title="Open in new tab" placement="bottom">
<Tooltip title={t('Open in new tab')} placement="bottom">
<a
href={currUrl}
target="_blank"

View file

@ -22,6 +22,7 @@ import { spot as spotUrl, withSiteId } from 'App/routes';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import EditItemModal from './EditItemModal';
import { useTranslation } from 'react-i18next';
const backgroundUrl = '/assets/img/spotThumbBg.svg';
@ -42,9 +43,10 @@ function SpotListItem({
onSelect,
isSelected,
}: ISpotListItem) {
const { t } = useTranslation();
const [isEdit, setIsEdit] = useState(false);
const [loading, setLoading] = useState(true);
const [tooltipText, setTooltipText] = useState('Copy link to clipboard');
const [tooltipText, setTooltipText] = useState(t('Copy link to clipboard'));
const history = useHistory();
const { siteId } = useParams<{ siteId: string }>();
@ -52,17 +54,17 @@ function SpotListItem({
{
key: 'rename',
icon: <EditOutlined />,
label: 'Rename',
label: t('Rename'),
},
{
key: 'download',
label: 'Download Video',
label: t('Download Video'),
icon: <DownloadOutlined />,
},
{
key: 'delete',
icon: <DeleteOutlined />,
label: 'Delete',
label: t('Delete'),
},
];
@ -70,7 +72,7 @@ function SpotListItem({
menuItems.splice(1, 0, {
key: 'slack',
icon: <SlackOutlined />,
label: 'Share via Slack',
label: t('Share via Slack'),
});
}, []);
const onMenuClick = async ({ key }: any) => {
@ -88,7 +90,7 @@ function SpotListItem({
siteId,
)}`,
);
return toast.success('Spot URL copied to clipboard');
return toast.success(t('Spot URL copied to clipboard'));
case 'delete':
return onDelete();
case 'slack':
@ -114,12 +116,12 @@ function SpotListItem({
navigator.clipboard
.writeText(fullLink)
.then(() => {
setTooltipText('Link copied to clipboard!');
setTimeout(() => setTooltipText('Copy link to clipboard'), 2000); // Reset tooltip text after 2 seconds
setTooltipText(t('Link copied to clipboard!'));
setTimeout(() => setTooltipText(t('Copy link to clipboard')), 2000); // Reset tooltip text after 2 seconds
})
.catch(() => {
setTooltipText('Failed to copy URL');
setTimeout(() => setTooltipText('Copy link to clipboard'), 2000); // Reset tooltip text after 2 seconds
setTooltipText(t('Failed to copy URL'));
setTimeout(() => setTooltipText(t('Copy link to clipboard')), 2000); // Reset tooltip text after 2 seconds
});
};

View file

@ -1,9 +1,11 @@
import React from 'react';
import copy from 'copy-to-clipboard';
import { Tooltip } from 'UI';
import { useTranslation } from 'react-i18next';
const withCopy = (WrappedComponent: React.ComponentType) => {
function ComponentWithCopy(props: any) {
const { t } = useTranslation();
const [copied, setCopied] = React.useState(false);
const { value, tooltip } = props;
const copyToClipboard = (text: string) => {
@ -18,7 +20,7 @@ const withCopy = (WrappedComponent: React.ComponentType) => {
onClick={() => copyToClipboard(value)}
className="w-fit cursor-pointer"
>
<Tooltip title={copied ? tooltip : 'Click to copy'} delay={0}>
<Tooltip title={copied ? tooltip : t('Click to copy')} delay={0}>
<WrappedComponent {...props} copyToClipboard={copyToClipboard} />
</Tooltip>
</div>

View file

@ -4,22 +4,24 @@ import { observer } from 'mobx-react-lite';
import { Switch, Tooltip, message } from 'antd';
import { CaretRightOutlined, PauseOutlined } from '@ant-design/icons';
import './AutoplayToggle.css';
import { useTranslation } from 'react-i18next';
const AutoplayToggle: React.FC = () => {
const { t } = useTranslation();
const { player, store } = useContext(PlayerContext);
const { autoplay } = store.get();
const handleToggle = () => {
player.toggleAutoplay();
if (!autoplay) {
message.success('Autoplay is ON');
message.success(t('Autoplay is ON'));
} else {
message.info('Autoplay is OFF');
message.info(t('Autoplay is OFF'));
}
};
return (
<Tooltip title="Toggle Autoplay" placement="bottom">
<Tooltip title={t('Toggle Autoplay')} placement="bottom">
<Switch
className="custom-switch"
onChange={handleToggle}

View file

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Tooltip } from 'antd';
import copy from 'copy-to-clipboard';
import { useTranslation } from 'react-i18next';
interface Props {
label?: string;
@ -9,10 +10,11 @@ interface Props {
content: string;
}
function CopyText(props: Props) {
const { t } = useTranslation();
const {
children,
label = 'Click to copy',
afterLabel = 'Copied',
label = t('Click to copy'),
afterLabel = t('Copied'),
content = '',
} = props;
const [isCopied, setIsCopied] = useState(false);

View file

@ -216,7 +216,7 @@ const EventsPanel = observer(
{ label: 'All Tabs', value: 'all' },
{
label: (
<Tooltip title="Stack Events overview is available only for all tabs combined.">
<Tooltip title={t('Stack Events overview is available only for all tabs combined.')}>
<span>{t('Current Tab')}</span>
</Tooltip>
),

View file

@ -4,6 +4,7 @@ import { JSONTree, Tabs, NoContent } from 'UI';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import Headers from '../Headers';
import { useTranslation } from 'react-i18next';
import { TFunction } from 'i18next';
const HEADERS = 'HEADERS';
const REQUEST = 'REQUEST';
@ -23,8 +24,8 @@ function parseRequestResponse(
setHeaders: (hs: Record<string, string> | null) => void,
setJSONBody: (body: Record<string, unknown> | null) => void,
setStringBody: (body: string) => void,
t: TFunction,
): void {
const { t } = useTranslation();
try {
if (!r) {
setHeaders(null);
@ -100,12 +101,14 @@ function FetchTabs({ resource, isSpot }: Props) {
setRequestHeaders,
setJsonRequest,
setStringRequest,
t,
);
parseRequestResponse(
response,
setResponseHeaders,
setJsonResponse,
setStringResponse,
t,
);
}, [resource]);

View file

@ -205,7 +205,7 @@ export function AutocompleteModal({
</Button>
</div>
<Tooltip title="Clear all selection">
<Tooltip title={t('Clear all selection')}>
<Button
onClick={clearSelection}
type="text"

View file

@ -36,7 +36,7 @@ const EventsOrder = observer(
return (
<div className="flex items-center gap-2">
<Tooltip
title="Select the operator to be applied between events."
title={t('Select the operator to be applied between events.')}
placement="bottom"
>
<div className="text-neutral-500/90 text-sm font-normal cursor-default">

View file

@ -22,7 +22,7 @@ function LiveSearchBar(props: Props) {
<LiveSessionSearchField />
</div>
<div className="flex items-center" style={{ width: '40%' }}>
<Tooltip title="Clear Steps">
<Tooltip title={t('Clear Steps')}>
<Button
type="text"
disabled={!hasFilters}

View file

@ -47,14 +47,18 @@ function SelectDateRange(props: Props) {
const dateRangeOptions = props.comparison
? DATE_RANGE_COMPARISON_OPTIONS
: DATE_RANGE_OPTIONS;
const dateRangeOptionsLocalized = dateRangeOptions.map((obj: any) => ({
...obj,
label: t(obj.label),
}));
const selectedValue = usedPeriod?.rangeName
? dateRangeOptions.find(
? dateRangeOptionsLocalized.find(
(obj: any) => obj.value === usedPeriod?.rangeName,
)
: null;
const options = dateRangeOptions.filter((obj: any) =>
const options = dateRangeOptionsLocalized.filter((obj: any) =>
disableCustom ? obj.value !== CUSTOM_RANGE : true,
);
).map((obj) => ({ ...obj, label: t(obj.label) }));
const onChange = (value: any) => {
if (value === CUSTOM_RANGE) {
@ -215,6 +219,7 @@ function AndDateRange({
onApplyDateRange,
isTileDisabled,
}: Props) {
const { t } = useTranslation();
const menuProps = {
items: options.map((opt: any) => ({
label: opt.label,
@ -245,7 +250,7 @@ function AndDateRange({
>
<span>{`Compare to ${comparisonValue || ''}`}</span>
{selectedValue && (
<Tooltip title="Reset">
<Tooltip title={t('Reset')}>
<SyncOutlined
className="cursor-pointer p-2 py-1.5 hover:bg-neutral-200/50 text-sm"
onClick={(e) => {

View file

@ -1,6 +1,7 @@
import React from 'react';
import cn from 'classnames';
import stl from './ErrorBars.module.css';
import { useTranslation } from 'react-i18next';
const GOOD = 'Good';
const LESS_CRITICAL = 'Few Issues';
@ -19,6 +20,7 @@ interface Props {
count?: number;
}
export default function ErrorBars(props: Props) {
const { t } = useTranslation();
const { count = 2 } = props;
const state = React.useMemo(() => getErrorState(count), [count]);
const isGood = state === GOOD;
@ -50,7 +52,7 @@ export default function ErrorBars(props: Props) {
{/* <div className={cn("rounded-tr rounded-br", bgColor, stl.bar)}></div> */}
</div>
</div>
<div className="mt-1 color-gray-medium text-sm">{state}</div>
<div className="mt-1 color-gray-medium text-sm">{t(state)}</div>
</div>
);
}

View file

@ -3,8 +3,10 @@ import React from 'react';
import SessionSettings from 'Shared/SessionSettings';
import { Icon, Tooltip } from 'UI';
import { Button } from 'antd';
import { useTranslation } from 'react-i18next';
function SessionSettingButton(props: any) {
const { t } = useTranslation();
const { showModal } = useModal();
const handleClick = () => {
@ -13,7 +15,7 @@ function SessionSettingButton(props: any) {
return (
<div className="cursor-pointer ml-4" onClick={handleClick}>
<Tooltip title="Session Settings">
<Tooltip title={t('Session Settings')}>
<Button
icon={<Icon name="sliders" />}
type="text"

View file

@ -4,6 +4,7 @@ import { Icon, TextEllipsis } from 'UI';
import { Tooltip } from 'antd';
import { countries } from 'App/constants';
import CountryFlagIcon from 'Shared/CountryFlagIcon';
import { useTranslation } from 'react-i18next';
interface CountryFlagProps {
userCity?: string;
@ -26,9 +27,10 @@ const CountryFlag: FC<CountryFlagProps> = ({
height = 15,
showLabel = false,
}) => {
const { t } = useTranslation();
const knownCountry = !!country && country !== 'UN';
const countryFlag = knownCountry ? country.toLowerCase() : '';
const countryName = knownCountry ? countries[country] : 'Unknown Country';
const countryName = knownCountry ? countries[country] : t('Unknown Country');
const displayGeoInfo = userCity || userState || countryName;

View file

@ -1448,5 +1448,43 @@
"Select Series": "Select Series",
"Integrate Github": "Integrate Github",
"Integrate GraphQL": "Integrate GraphQL",
"Installation Docs": "Installation Docs"
"Installation Docs": "Installation Docs",
"Good": "Good",
"Few Issues": "Few Issues",
"Many Issues": "Many Issues",
"Past 24 Hours": "Past 24 Hours",
"Past 30 Days": "Past 30 Days",
"Custom Range": "Custom Range",
"Unknown Country": "Unknown Country",
"design": "Design",
"note": "Note",
"issue": "Issue",
"Set Alerts": "Set Alerts",
"Confirm Card Deletion": "Confirm Card Deletion",
"Are you sure you want to remove this card? This action is permanent and cannot be undone.": "Are you sure you want to remove this card? This action is permanent and cannot be undone.",
"Failed to remove card": "Failed to remove card",
"Filters on Left": "Filters on Left",
"Filters on Top": "Filters on Top",
"Filters on Right": "Filters on Right",
"Call user to initiate remote control": "Call user to initiate remote control",
"Click to rename": "Click to rename",
"Shared with team": "Shared with team",
"Conversion lost": "Conversion lost",
"Toggle Autoplay": "Toggle Autoplay",
"Open in new tab": "Open in new tab",
"Highlight a moment": "Highlight a moment",
"Time to execute": "Time to execute",
"Inspect your application state while youre replaying your users sessions. OpenReplay supports": "Inspect your application state while youre replaying your users' sessions. OpenReplay supports",
"Autoplay is ON": "Autoplay is ON",
"Autoplay is OFF": "Autoplay is OFF",
"Click to copy": "Click to copy",
"Stack Events overview is available only for all tabs combined.": "Stack Events overview is available only for all tabs combined.",
"Clear all selection": "Clear all selection",
"Clear Steps": "Clear Steps",
"Session Settings": "Session Settings",
"Select the operator to be applied between events.": "Select the operator to be applied between events.",
"above": "above",
"above or equal to": "above or equal to",
"below": "below",
"below or equal to": "below or equal to"
}

View file

@ -1448,5 +1448,43 @@
"Select Series": "Seleccionar Serie",
"Integrate Github": "Integrar con Github",
"Integrate GraphQL": "Integrar con GraphQL",
"Installation Docs": "Documentación de Instalación"
"Installation Docs": "Documentación de Instalación",
"Good": "Bueno",
"Few Issues": "Algunos problemas",
"Many Issues": "Muchos problemas",
"Past 24 Hours": "Últimas 24 horas",
"Past 30 Days": "Últimos 30 días",
"Custom Range": "Rango personalizado",
"Unknown Country": "País desconocido",
"design": "Diseño",
"note": "Nota",
"issue": "Problema",
"Set Alerts": "Configurar alertas",
"Confirm Card Deletion": "Confirmar eliminación de tarjeta",
"Are you sure you want to remove this card? This action is permanent and cannot be undone.": "¿Estás seguro de que quieres eliminar esta tarjeta? Esta acción es permanente y no se puede deshacer.",
"Failed to remove card": "No se pudo eliminar la tarjeta",
"Filters on Left": "Filtros a la izquierda",
"Filters on Top": "Filtros en la parte superior",
"Filters on Right": "Filtros a la derecha",
"Call user to initiate remote control": "Llamar al usuario para iniciar el control remoto",
"Click to rename": "Haz clic para renombrar",
"Shared with team": "Compartido con el equipo",
"Conversion lost": "Conversión perdida",
"Toggle Autoplay": "Activar/desactivar reproducción automática",
"Open in new tab": "Abrir en una nueva pestaña",
"Highlight a moment": "Resaltar un momento",
"Time to execute": "Tiempo de ejecución",
"Inspect your application state while youre replaying your users sessions. OpenReplay supports": "Inspecciona el estado de tu aplicación mientras reproduces las sesiones de los usuarios. OpenReplay admite",
"Autoplay is ON": "La reproducción automática está activada",
"Autoplay is OFF": "La reproducción automática está desactivada",
"Click to copy": "Haz clic para copiar",
"Stack Events overview is available only for all tabs combined.": "El resumen de eventos de la pila está disponible solo para todas las pestañas combinadas.",
"Clear all selection": "Borrar todas las selecciones",
"Clear Steps": "Borrar pasos",
"Session Settings": "Configuración de sesión",
"Select the operator to be applied between events.": "Seleccione el operador que se aplicará entre los eventos.",
"above": "por encima de",
"above or equal to": "por encima o igual a",
"below": "por debajo de",
"below or equal to": "por debajo o igual a"
}

View file

@ -1448,5 +1448,43 @@
"Select Series": "Sélectionner la série",
"Integrate Github": "Intégrer avec Github",
"Integrate GraphQL": "Intégrer avec GraphQL",
"Installation Docs": "Documentation d'installation"
"Installation Docs": "Documentation d'installation",
"Good": "Bon",
"Few Issues": "Quelques problèmes",
"Many Issues": "De nombreux problèmes",
"Past 24 Hours": "Dernières 24 heures",
"Past 30 Days": "Derniers 30 jours",
"Custom Range": "Plage personnalisée",
"Unknown Country": "Pays inconnu",
"design": "Design",
"note": "Note",
"issue": "Problème",
"Set Alerts": "Définir des alertes",
"Confirm Card Deletion": "Confirmer la suppression de la carte",
"Are you sure you want to remove this card? This action is permanent and cannot be undone.": "Êtes-vous sûr de vouloir supprimer cette carte ? Cette action est permanente et ne peut pas être annulée.",
"Failed to remove card": "Échec de la suppression de la carte",
"Filters on Left": "Filtres à gauche",
"Filters on Top": "Filtres en haut",
"Filters on Right": "Filtres à droite",
"Call user to initiate remote control": "Appeler l'utilisateur pour initier le contrôle à distance",
"Click to rename": "Cliquer pour renommer",
"Shared with team": "Partagé avec l'équipe",
"Conversion lost": "Conversion perdue",
"Toggle Autoplay": "Activer/désactiver la lecture automatique",
"Open in new tab": "Ouvrir dans un nouvel onglet",
"Highlight a moment": "Mettre en avant un moment",
"Time to execute": "Temps d'exécution",
"Inspect your application state while youre replaying your users sessions. OpenReplay supports": "Inspectez l'état de votre application pendant la lecture des sessions des utilisateurs. OpenReplay prend en charge",
"Autoplay is ON": "La lecture automatique est activée",
"Autoplay is OFF": "La lecture automatique est désactivée",
"Click to copy": "Cliquer pour copier",
"Stack Events overview is available only for all tabs combined.": "L'aperçu des événements de la pile est disponible uniquement pour toutes les onglets combinés.",
"Clear all selection": "Effacer toutes les sélections",
"Clear Steps": "Effacer les étapes",
"Session Settings": "Paramètres de session",
"Select the operator to be applied between events.": "Sélectionnez l'opérateur à appliquer entre les événements.",
"above": "au-dessus de",
"above or equal to": "supérieur ou égal à",
"below": "en dessous de",
"below or equal to": "inférieur ou égal à"
}

View file

@ -579,7 +579,7 @@
"Add Exclusion": "Добавить исключение",
"Significant issues": "Значительные проблемы",
"in this funnel": "в этой воронке",
"Click Rage": "Rage-клик",
"Click Rage": "Click Rage",
"Dead Click": "Мёртвый клик",
"Bad Request": "Некорректный запрос",
"Missing Image": "Отсутствующее изображение",
@ -944,7 +944,7 @@
"s to enter a value in this input field.": "c, чтобы ввести значение в это поле.",
"View": "Просмотр",
"Mouse Thrashing": "Беспорядочные движения мыши",
"Speed Index": "Инденс скорости",
"Speed Index": "Индекс скорости",
"Copy CSS": "Копировать CSS",
"Copy URL": "Копировать URL",
"Referrer:": "Реферер:",
@ -1448,5 +1448,43 @@
"Select Series": "Выбрать серию",
"Integrate Github": "Интеграция с Github",
"Integrate GraphQL": "Интеграция с GraphQL",
"Installation Docs": "Документация по установке"
"Installation Docs": "Документация по установке",
"Good": "Хорошо",
"Few Issues": "Есть проблемы",
"Many Issues": "Много проблем",
"Past 24 Hours": "Последние 24 часа",
"Past 30 Days": "Последние 30 дней",
"Custom Range": "Произвольный диапазон",
"Unknown Country": "Неизвестная страна",
"design": "Дизайн",
"note": "Заметка",
"issue": "Проблема",
"Set Alerts": "Установить уведомления",
"Confirm Card Deletion": "Подтвердить удаление карточки",
"Are you sure you want to remove this card? This action is permanent and cannot be undone.": "Вы уверены, что хотите удалить эту карточку? Это действие необратимо.",
"Failed to remove card": "Не удалось удалить карточку",
"Filters on Left": "Фильтры слева",
"Filters on Top": "Фильтры сверху",
"Filters on Right": "Фильтры справа",
"Call user to initiate remote control": "Позвонить пользователю для начала удаленного управления",
"Click to rename": "Нажмите, чтобы переименовать",
"Shared with team": "Доступно команде",
"Conversion lost": "Потерянные конверсии",
"Toggle Autoplay": "Включить автовоспроизведение",
"Open in new tab": "Открыть в новой вкладке",
"Highlight a moment": "Выделить момент",
"Time to execute": "Время выполнения",
"Inspect your application state while youre replaying your users sessions. OpenReplay supports": "Изучайте состояние вашего приложения во время воспроизведения сессий пользователей. OpenReplay поддерживает",
"Autoplay is ON": "Автовоспроизведение включено",
"Autoplay is OFF": "Автовоспроизведение выключено",
"Click to copy": "Нажмите, чтобы скопировать",
"Stack Events overview is available only for all tabs combined.": "Обзор событий стека доступен только для всех вкладок вместе.",
"Clear all selection": "Очистить все выборы",
"Clear Steps": "Очистить шаги",
"Session Settings": "Настройки сессии",
"Select the operator to be applied between events.": "Выберите оператор, который будет применен между событиями.",
"above": "выше",
"above or equal to": "выше или равно",
"below": "ниже",
"below or equal to": "ниже или равно"
}

View file

@ -1448,5 +1448,43 @@
"Select Series": "选择系列",
"Integrate Github": "集成 Github",
"Integrate GraphQL": "集成 GraphQL",
"Installation Docs": "安装文档"
"Installation Docs": "安装文档",
"Good": "良好",
"Few Issues": "少量问题",
"Many Issues": "大量问题",
"Past 24 Hours": "过去 24 小时",
"Past 30 Days": "过去 30 天",
"Custom Range": "自定义范围",
"Unknown Country": "未知国家",
"design": "设计",
"note": "备注",
"issue": "问题",
"Set Alerts": "设置提醒",
"Confirm Card Deletion": "确认删除卡片",
"Are you sure you want to remove this card? This action is permanent and cannot be undone.": "您确定要删除此卡片吗?此操作是永久性的,无法撤销。",
"Failed to remove card": "删除卡片失败",
"Filters on Left": "左侧筛选器",
"Filters on Top": "顶部筛选器",
"Filters on Right": "右侧筛选器",
"Call user to initiate remote control": "呼叫用户以启动远程控制",
"Click to rename": "点击重命名",
"Shared with team": "已与团队共享",
"Conversion lost": "转化丢失",
"Toggle Autoplay": "切换自动播放",
"Open in new tab": "在新标签页中打开",
"Highlight a moment": "突出显示时刻",
"Time to execute": "执行时间",
"Inspect your application state while youre replaying your users sessions. OpenReplay supports": "在回放用户会话时检查您的应用程序状态。OpenReplay 支持",
"Autoplay is ON": "自动播放已开启",
"Autoplay is OFF": "自动播放已关闭",
"Click to copy": "点击复制",
"Stack Events overview is available only for all tabs combined.": "堆栈事件概览仅适用于所有选项卡的组合视图。",
"Clear all selection": "清除所有选择",
"Clear Steps": "清除步骤",
"Session Settings": "会话设置",
"Select the operator to be applied between events.": "选择要应用于事件之间的运算符。",
"above": "大于",
"above or equal to": "大于或等于",
"below": "小于",
"below or equal to": "小于或等于"
}