feat(ui-toolkit): use i18next (#5289)

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
Bartłomiej Głownia 2023-11-20 00:27:52 +01:00 committed by GitHub
parent bb47747501
commit 61a9eb2f5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 194 additions and 75 deletions

View File

@ -1,3 +1,4 @@
import '../i18n';
import { import {
NetworkLoader, NetworkLoader,
NodeFailure, NodeFailure,
@ -28,20 +29,24 @@ function App() {
); );
return ( return (
<TendermintWebsocketProvider> <TendermintWebsocketProvider>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}> <Suspense fallback={splashLoading}>
<NodeGuard <NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
skeleton={<div>{t('Loading')}</div>} <NodeGuard
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />} skeleton={<div>{t('Loading')}</div>}
> failure={
<Suspense fallback={splashLoading}> <NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />
<RouterProvider router={router} fallbackElement={splashLoading} /> }
</Suspense> >
</NodeGuard> <Suspense fallback={splashLoading}>
<NodeSwitcherDialog <RouterProvider router={router} fallbackElement={splashLoading} />
open={nodeSwitcherOpen} </Suspense>
setOpen={setNodeSwitcherOpen} </NodeGuard>
/> <NodeSwitcherDialog
</NetworkLoader> open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader>
</Suspense>
</TendermintWebsocketProvider> </TendermintWebsocketProvider>
); );
} }

View File

@ -3,6 +3,9 @@
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { locales } from '@vegaprotocol/i18n';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
Object.defineProperty(window, 'ResizeObserver', { Object.defineProperty(window, 'ResizeObserver', {
writable: false, writable: false,
@ -13,3 +16,14 @@ Object.defineProperty(window, 'ResizeObserver', {
disconnect: jest.fn(), disconnect: jest.fn(),
})), })),
}); });
// Set up i18n instance so that components have the correct default
// en translations
i18n.use(initReactI18next).init({
// we init with resources
resources: locales,
fallbackLng: 'en',
nsSeparator: false,
ns: ['explorer'],
defaultNS: 'explorer',
});

View File

@ -0,0 +1 @@
../../../../libs/i18n/src/locales

View File

@ -0,0 +1,45 @@
import type { Module } from 'i18next';
import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import LocizeBackend from 'i18next-locize-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
const isInDev = process.env.NODE_ENV === 'development';
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
const backend = useLocize
? {
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
apiKey: process.env.NX_LOCIZE_API_KEY,
referenceLng: 'en',
}
: {
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
};
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
lng: 'en',
fallbackLng: 'en',
supportedLngs: ['en'],
load: 'languageOnly',
debug: isInDev,
// have a common namespace used around the full app
ns: ['explorer'],
defaultNS: 'explorer',
keySeparator: false, // we use content as keys
nsSeparator: false,
backend,
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
interpolation: {
escapeValue: false,
},
});
export default i18n;

View File

@ -15,6 +15,7 @@ import en_markets from './locales/en/markets.json';
import en_web3 from './locales/en/web3.json'; import en_web3 from './locales/en/web3.json';
import en_positions from './locales/en/positions.json'; import en_positions from './locales/en/positions.json';
import en_trades from './locales/en/trading.json'; import en_trades from './locales/en/trading.json';
import en_ui_toolkit from './locales/en/ui-toolkit.json';
export const locales = { export const locales = {
en: { en: {
@ -33,5 +34,6 @@ export const locales = {
web3: en_web3, web3: en_web3,
positions: en_positions, positions: en_positions,
trades: en_trades, trades: en_trades,
'ui-toolkit': en_ui_toolkit,
}, },
}; };

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,21 @@
{
"{{fee}} Fee": "{{fee}} Fee",
"Auction Trigger stake {{trigger}}": "Auction Trigger stake {{trigger}}",
"Collapse": "Collapse",
"Copied": "Copied",
"Dark mode": "Dark mode",
"Dismiss all toasts": "Dismiss all toasts",
"Dismiss all": "Dismiss all",
"Exit view as": "Exit view as",
"Expand": "Expand",
"Light mode": "Light mode",
"Loading...": "Loading...",
"No data": "No data",
"Providers greater than 2x target stake not shown": "Providers greater than 2x target stake not shown",
"Show more": "Show more",
"Something went wrong: {{errorMessage}}": "Something went wrong: {{errorMessage}}",
"Target stake {{target}}": "Target stake {{target}}",
"This is an example of a toast notification": "This is an example of a toast notification",
"Try again": "Try again",
"Viewing as Vega user: {{pubKey}}": "Viewing as Vega user: {{pubKey}}"
}

View File

@ -0,0 +1,14 @@
export const useTranslation = () => ({
t: (label: string, replacements?: Record<string, string>) => {
let translatedLabel = label;
if (typeof replacements === 'object' && replacements !== null) {
Object.keys(replacements).forEach((key) => {
translatedLabel = translatedLabel.replace(
`{{${key}}}`,
replacements[key]
);
});
}
return translatedLabel;
},
});

View File

@ -1,7 +1,7 @@
import { Splash } from '../splash'; import { Splash } from '../splash';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Button } from '../button'; import { Button } from '../button';
import { useT } from '../../use-t';
interface AsyncRendererProps<T> { interface AsyncRendererProps<T> {
loading: boolean; loading: boolean;
@ -28,15 +28,18 @@ export function AsyncRenderer<T = object>({
render, render,
reload, reload,
}: AsyncRendererProps<T>) { }: AsyncRendererProps<T>) {
const t = useT();
if (error) { if (error) {
if (!data || (Array.isArray(data) && !data.length)) { if (!data || (Array.isArray(data) && !data.length)) {
return ( return (
<div className="h-full flex items-center justify-center"> <div className="flex h-full items-center justify-center">
<div className="h-12 flex flex-col items-center"> <div className="flex h-12 flex-col items-center">
<Splash> <Splash>
{errorMessage {errorMessage
? errorMessage ? errorMessage
: t(`Something went wrong: ${error.message}`)} : t('Something went wrong: {{errorMessage}}', {
errorMessage: error.message,
})}
</Splash> </Splash>
{reload && error.message === 'Timeout exceeded' && ( {reload && error.message === 'Timeout exceeded' && (
<Button <Button
@ -77,6 +80,7 @@ export function AsyncRendererInline<T>({
render, render,
reload, reload,
}: AsyncRendererProps<T>) { }: AsyncRendererProps<T>) {
const t = useT();
const wrapperClasses = 'text-sm'; const wrapperClasses = 'text-sm';
if (error) { if (error) {
if (!data) { if (!data) {
@ -85,7 +89,9 @@ export function AsyncRendererInline<T>({
<p> <p>
{errorMessage {errorMessage
? errorMessage ? errorMessage
: t(`Something went wrong: ${error.message}`)} : t('Something went wrong: {{errorMessage}}', {
errorMessage: error.message,
})}
</p> </p>
{reload && error.message === 'Timeout exceeded' && ( {reload && error.message === 'Timeout exceeded' && (
<Button <Button

View File

@ -5,7 +5,7 @@ import { forwardRef } from 'react';
import { VegaIcon, VegaIconNames } from '../icon'; import { VegaIcon, VegaIconNames } from '../icon';
import { useCopyTimeout } from '@vegaprotocol/react-helpers'; import { useCopyTimeout } from '@vegaprotocol/react-helpers';
import CopyToClipboard from 'react-copy-to-clipboard'; import CopyToClipboard from 'react-copy-to-clipboard';
import { t } from '@vegaprotocol/i18n'; import { useT } from '../../use-t';
const itemClass = classNames( const itemClass = classNames(
'relative flex gap-2 items-center rounded-sm p-2 text-sm', 'relative flex gap-2 items-center rounded-sm p-2 text-sm',
@ -214,6 +214,7 @@ export const DropdownMenuCopyItem = ({
value: string; value: string;
text: string; text: string;
}) => { }) => {
const t = useT();
const [copied, setCopied] = useCopyTimeout(); const [copied, setCopied] = useCopyTimeout();
return ( return (

View File

@ -3,14 +3,14 @@ import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
formatNumberPercentage, formatNumberPercentage,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { BigNumber } from 'bignumber.js'; import { BigNumber } from 'bignumber.js';
import { getIntentBackground, Intent } from '../../utils/intent'; import { getIntentBackground, Intent } from '../../utils/intent';
import { Indicator } from '../indicator'; import { Indicator } from '../indicator';
import { Tooltip } from '../tooltip'; import { Tooltip } from '../tooltip';
import { useT } from '../../use-t';
const Remainder = () => ( const Remainder = () => (
<div className="bg-greys-light-200 h-[inherit] relative flex-1" /> <div className="bg-greys-light-200 relative h-[inherit] flex-1" />
); );
const Target = ({ const Target = ({
@ -22,6 +22,7 @@ const Target = ({
target: string; target: string;
decimals: number; decimals: number;
}) => { }) => {
const t = useT();
return ( return (
<Tooltip <Tooltip
description={ description={
@ -30,20 +31,22 @@ const Target = ({
<Indicator variant={Intent.None} /> <Indicator variant={Intent.None} />
</div> </div>
<span> <span>
{t('Target stake')} {addDecimalsFormatNumber(target, decimals)} {t('Target stake {{target}}', {
target: addDecimalsFormatNumber(target, decimals),
})}{' '}
</span> </span>
</div> </div>
} }
> >
<div <div
className={classNames( className={classNames(
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group' 'group absolute left-1/2 top-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5'
)} )}
style={{ left: '50%' }} style={{ left: '50%' }}
> >
<div <div
className={classNames( className={classNames(
'health-target w-0.5 bg-vega-dark-100 dark:bg-vega-light-100 group-hover:scale-x-150 group-hover:scale-y-108', 'health-target bg-vega-dark-100 dark:bg-vega-light-100 group-hover:scale-y-108 w-0.5 group-hover:scale-x-150',
{ {
'h-6': !isLarge, 'h-6': !isLarge,
'h-12': isLarge, 'h-12': isLarge,
@ -66,6 +69,7 @@ const AuctionTarget = ({
rangeLimit: number; rangeLimit: number;
decimals: number; decimals: number;
}) => { }) => {
const t = useT();
const leftPosition = new BigNumber(trigger).div(rangeLimit).multipliedBy(100); const leftPosition = new BigNumber(trigger).div(rangeLimit).multipliedBy(100);
return ( return (
<Tooltip <Tooltip
@ -75,15 +79,16 @@ const AuctionTarget = ({
<Indicator variant={Intent.None} /> <Indicator variant={Intent.None} />
</div> </div>
<span> <span>
{t('Auction Trigger stake')}{' '} {t('Auction Trigger stake {{trigger}}', {
{addDecimalsFormatNumber(trigger, decimals)} trigger: addDecimalsFormatNumber(trigger, decimals),
})}
</span> </span>
</div> </div>
} }
> >
<div <div
className={classNames( className={classNames(
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group' 'group absolute left-1/2 top-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5'
)} )}
style={{ style={{
left: `${leftPosition}%`, left: `${leftPosition}%`,
@ -91,7 +96,7 @@ const AuctionTarget = ({
> >
<div <div
className={classNames( className={classNames(
'health-target w-0.5 group-hover:scale-x-150 group-hover:scale-y-108 dashed-background', 'health-target group-hover:scale-y-108 dashed-background w-0.5 group-hover:scale-x-150',
{ {
'h-6': !isLarge, 'h-6': !isLarge,
'h-12': isLarge, 'h-12': isLarge,
@ -120,6 +125,7 @@ const Level = ({
decimals: number; decimals: number;
intent: Intent; intent: Intent;
}) => { }) => {
const t = useT();
const width = new BigNumber(commitmentAmount) const width = new BigNumber(commitmentAmount)
.div(rangeLimit) .div(rangeLimit)
.multipliedBy(100) .multipliedBy(100)
@ -134,9 +140,7 @@ const Level = ({
<div className="mt-1.5 inline-flex"> <div className="mt-1.5 inline-flex">
<Indicator variant={intent} /> <Indicator variant={intent} />
</div> </div>
<span> <span>{t('{{fee}} Fee', { fee: formattedFee })}</span>
{formattedFee} {t('Fee')}
</span>
<div className="flex flex-col"> <div className="flex flex-col">
<span> <span>
{prevLevel ? addDecimalsFormatNumber(prevLevel, decimals) : '0'} -{' '} {prevLevel ? addDecimalsFormatNumber(prevLevel, decimals) : '0'} -{' '}
@ -149,14 +153,14 @@ const Level = ({
return ( return (
<Tooltip description={tooltipContent}> <Tooltip description={tooltipContent}>
<div <div
className={classNames(`relative h-[inherit] w-full group min-w-[1px]`)} className="group relative h-[inherit] w-full min-w-[1px]"
style={{ style={{
width: `${width}%`, width: `${width}%`,
}} }}
> >
<div <div
className={classNames( className={classNames(
'relative w-full h-[inherit] group-hover:scale-y-150', 'relative h-[inherit] w-full group-hover:scale-y-150',
getIntentBackground(intent) getIntentBackground(intent)
)} )}
style={{ opacity }} style={{ opacity }}
@ -167,7 +171,7 @@ const Level = ({
}; };
const Full = () => ( const Full = () => (
<div className="bg-transparent w-full h-[inherit] absolute bottom-0 left-0" /> <div className="absolute bottom-0 left-0 h-[inherit] w-full bg-transparent" />
); );
interface Levels { interface Levels {
@ -190,6 +194,7 @@ export const HealthBar = ({
intent: Intent; intent: Intent;
triggerRatio?: string; triggerRatio?: string;
}) => { }) => {
const t = useT();
const targetNumber = parseInt(target, 10); const targetNumber = parseInt(target, 10);
const rangeLimit = targetNumber * 2; const rangeLimit = targetNumber * 2;
@ -220,7 +225,7 @@ export const HealthBar = ({
})} })}
> >
<div <div
className={classNames('health-inner relative w-full flex', { className={classNames('health-inner relative flex w-full', {
'h-4': !isLarge, 'h-4': !isLarge,
'h-8': isLarge, 'h-8': isLarge,
})} })}
@ -228,8 +233,8 @@ export const HealthBar = ({
<Full /> <Full />
<div <div
className="health-bars h-[inherit] flex w-full className="health-bars outline-vega-light-200 dark:outline-vega-dark-200 flex
gap-0.5 outline outline-vega-light-200 dark:outline-vega-dark-200" h-[inherit] w-full gap-0.5 outline"
> >
{levels.map((p, index) => { {levels.map((p, index) => {
const { commitmentAmount, fee } = p; const { commitmentAmount, fee } = p;
@ -253,11 +258,11 @@ export const HealthBar = ({
<Tooltip <Tooltip
description={ description={
<div className="text-vega-dark-100 dark:text-vega-light-200"> <div className="text-vega-dark-100 dark:text-vega-light-200">
t( 'Providers greater than 2x target stake not shown' ) {t('Providers greater than 2x target stake not shown')}
</div> </div>
} }
> >
<div className="h-[inherit] relative flex-1 leading-4">...</div> <div className="relative h-[inherit] flex-1 leading-4">...</div>
</Tooltip> </Tooltip>
)} )}
</div> </div>

View File

@ -1,8 +1,8 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { useRef, useState, useEffect } from 'react'; import { useRef, useState, useEffect } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Button } from '../button'; import { Button } from '../button';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useT } from '../../use-t';
type ShowMoreProps = { type ShowMoreProps = {
children: ReactNode; children: ReactNode;
@ -15,6 +15,7 @@ export const ShowMore = ({
closedMaxHeightPx = 125, closedMaxHeightPx = 125,
overlayColourOverrides, overlayColourOverrides,
}: ShowMoreProps) => { }: ShowMoreProps) => {
const t = useT();
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);

View File

@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers'; import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { SunIcon, MoonIcon } from './icons'; import { SunIcon, MoonIcon } from './icons';
import { Toggle } from '../toggle'; import { Toggle } from '../toggle';
import { useT } from '../../use-t';
export const ThemeSwitcher = ({ export const ThemeSwitcher = ({
className, className,
@ -10,6 +10,7 @@ export const ThemeSwitcher = ({
className?: string; className?: string;
withMobile?: boolean; withMobile?: boolean;
}) => { }) => {
const t = useT();
const { theme, setTheme } = useThemeSwitcher(); const { theme, setTheme } = useThemeSwitcher();
const button = ( const button = (
<button <button
@ -35,7 +36,7 @@ export const ThemeSwitcher = ({
]; ];
return withMobile ? ( return withMobile ? (
<> <>
<div className="flex grow gap-6 md:hidden whitespace-nowrap justify-between"> <div className="flex grow justify-between gap-6 whitespace-nowrap md:hidden">
{button}{' '} {button}{' '}
<Toggle <Toggle
name="theme-switch" name="theme-switch"

View File

@ -1,28 +1,27 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { t } from '@vegaprotocol/i18n';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import { Icon } from '../icon'; import { Icon } from '../icon';
import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts'; import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { Intent } from '../../utils/intent'; import { Intent } from '../../utils/intent';
import { useT } from '../../use-t';
const TEST_TOAST = {
id: 'test-toast',
intent: Intent.Primary,
content: <>{t('This is an example of a toast notification')}</>,
onClose: () => useToasts.getState().remove('test-toast'),
};
export const ToastPositionSetter = () => { export const ToastPositionSetter = () => {
const t = useT();
const setPostion = useToastsConfiguration((store) => store.setPosition); const setPostion = useToastsConfiguration((store) => store.setPosition);
const position = useToastsConfiguration((store) => store.position); const position = useToastsConfiguration((store) => store.position);
const setToast = useToasts((store) => store.setToast); const setToast = useToasts((store) => store.setToast);
const handleChange = useCallback( const handleChange = useCallback(
(position: ToastPosition) => { (position: ToastPosition) => {
setPostion(position); setPostion(position);
setToast(TEST_TOAST); setToast({
id: 'test-toast',
intent: Intent.Primary,
content: <>{t('This is an example of a toast notification')}</>,
onClose: () => useToasts.getState().remove('test-toast'),
});
}, },
[setToast, setPostion] [setToast, setPostion, t]
); );
const buttonCssClasses = const buttonCssClasses =
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400'; 'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';

View File

@ -17,7 +17,7 @@ import {
import { Intent } from '../../utils/intent'; import { Intent } from '../../utils/intent';
import { Icon, VegaIcon, VegaIconNames } from '../icon'; import { Icon, VegaIcon, VegaIconNames } from '../icon';
import { Loader } from '../loader'; import { Loader } from '../loader';
import { t } from '@vegaprotocol/i18n'; import { useT } from '../../use-t';
export type ToastContent = JSX.Element | undefined; export type ToastContent = JSX.Element | undefined;
@ -83,6 +83,7 @@ export const CollapsiblePanel = forwardRef<
HTMLDivElement, HTMLDivElement,
CollapsiblePanelProps & HTMLAttributes<HTMLDivElement> CollapsiblePanelProps & HTMLAttributes<HTMLDivElement>
>(({ children, className, actions, ...props }, ref) => { >(({ children, className, actions, ...props }, ref) => {
const t = useT();
const [collapsed, setCollapsed] = useState(true); const [collapsed, setCollapsed] = useState(true);
return ( return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions // eslint-disable-next-line jsx-a11y/no-static-element-interactions

View File

@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import { usePrevious } from '@vegaprotocol/react-helpers'; import { usePrevious } from '@vegaprotocol/react-helpers';
import classNames from 'classnames'; import classNames from 'classnames';
import type { Ref } from 'react'; import type { Ref } from 'react';
@ -9,6 +8,7 @@ import type { Toasts } from './use-toasts';
import { ToastPosition, useToasts, useToastsConfiguration } from './use-toasts'; import { ToastPosition, useToasts, useToastsConfiguration } from './use-toasts';
import { Portal } from '@radix-ui/react-portal'; import { Portal } from '@radix-ui/react-portal';
import { useT } from '../../use-t';
type ToastsContainerProps = { type ToastsContainerProps = {
toasts: Toasts; toasts: Toasts;
@ -21,6 +21,7 @@ export const ToastsContainer = ({
order = 'asc', order = 'asc',
showHidden = false, showHidden = false,
}: ToastsContainerProps) => { }: ToastsContainerProps) => {
const t = useT();
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>();
const closeAll = useToasts((store) => store.closeAll); const closeAll = useToasts((store) => store.closeAll);
const position = useToastsConfiguration((store) => store.position); const position = useToastsConfiguration((store) => store.position);
@ -55,17 +56,17 @@ export const ToastsContainer = ({
'absolute z-20', 'absolute z-20',
{ 'bottom-0 right-0': position === ToastPosition.BottomRight }, { 'bottom-0 right-0': position === ToastPosition.BottomRight },
{ 'bottom-0 left-0': position === ToastPosition.BottomLeft }, { 'bottom-0 left-0': position === ToastPosition.BottomLeft },
{ 'top-0 left-0': position === ToastPosition.TopLeft }, { 'left-0 top-0': position === ToastPosition.TopLeft },
{ 'top-0 right-0': position === ToastPosition.TopRight }, { 'right-0 top-0': position === ToastPosition.TopRight },
{ {
'top-0 left-[50%] translate-x-[-50%]': 'left-[50%] top-0 translate-x-[-50%]':
position === ToastPosition.TopCenter, position === ToastPosition.TopCenter,
}, },
{ {
'bottom-0 left-[50%] translate-x-[-50%]': 'bottom-0 left-[50%] translate-x-[-50%]':
position === ToastPosition.BottomCenter, position === ToastPosition.BottomCenter,
}, },
'max-w-full max-h-full overflow-x-hidden overflow-y-auto', 'max-h-full max-w-full overflow-y-auto overflow-x-hidden',
{ {
'p-4': validToasts.length > 0, // only apply padding when toasts showing, otherwise a small section of the screen is covered 'p-4': validToasts.length > 0, // only apply padding when toasts showing, otherwise a small section of the screen is covered
hidden: validToasts.length === 0, hidden: validToasts.length === 0,
@ -89,9 +90,9 @@ export const ToastsContainer = ({
})} })}
<div <div
className={classNames( className={classNames(
'absolute w-full top-[-38px] right-0 z-20', 'absolute right-0 top-[-38px] z-20 w-full',
'transition-opacity', 'transition-opacity',
'opacity-0 group-hover:opacity-50 hover:!opacity-100', 'opacity-0 hover:!opacity-100 group-hover:opacity-50',
{ {
hidden: validToasts.length === 0, hidden: validToasts.length === 0,
} }

View File

@ -4,7 +4,7 @@ import { forwardRef, type ComponentProps, type ReactNode } from 'react';
import { VegaIcon, VegaIconNames } from '../icon'; import { VegaIcon, VegaIconNames } from '../icon';
import { useCopyTimeout } from '@vegaprotocol/react-helpers'; import { useCopyTimeout } from '@vegaprotocol/react-helpers';
import CopyToClipboard from 'react-copy-to-clipboard'; import CopyToClipboard from 'react-copy-to-clipboard';
import { t } from '@vegaprotocol/i18n'; import { useT } from '../../use-t';
const itemClass = classNames( const itemClass = classNames(
'relative flex gap-2 items-center rounded-sm p-2 text-sm', 'relative flex gap-2 items-center rounded-sm p-2 text-sm',
@ -188,6 +188,7 @@ export const TradingDropdownCopyItem = ({
value: string; value: string;
text: string; text: string;
}) => { }) => {
const t = useT();
const [copied, setCopied] = useCopyTimeout(); const [copied, setCopied] = useCopyTimeout();
return ( return (

View File

@ -1,12 +1,10 @@
import { t } from '@vegaprotocol/i18n';
type VegaLogoProps = { type VegaLogoProps = {
className?: string; className?: string;
}; };
export const VegaLogo = ({ className }: VegaLogoProps) => { export const VegaLogo = ({ className }: VegaLogoProps) => {
return ( return (
<svg <svg
aria-label={t('Vega logo')} aria-label="Vega"
className={className || 'h-6'} className={className || 'h-6'}
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -23,7 +21,7 @@ export const VegaLogo = ({ className }: VegaLogoProps) => {
export const VLogo = ({ className }: { className?: string }) => { export const VLogo = ({ className }: { className?: string }) => {
return ( return (
<svg <svg
aria-label={t('Vega logo')} aria-label="Vega"
width="29" width="29"
height="34" height="34"
fill="currentColor" fill="currentColor"

View File

@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { NotificationBanner, SHORT } from '../notification-banner'; import { NotificationBanner, SHORT } from '../notification-banner';
import { Intent } from '../../utils/intent'; import { Intent } from '../../utils/intent';
import { TradingButton } from '../trading-button'; import { TradingButton } from '../trading-button';
import { useT } from '../../use-t';
export function truncateMiddle(address: string, start = 6, end = 4) { export function truncateMiddle(address: string, start = 6, end = 4) {
if (address.length < 11) return address; if (address.length < 11) return address;
@ -21,15 +21,14 @@ export const ViewingAsBanner = ({
pubKey, pubKey,
disconnect, disconnect,
}: ViewingAsBannerProps) => { }: ViewingAsBannerProps) => {
const t = useT();
return ( return (
<NotificationBanner <NotificationBanner intent={Intent.None} className={SHORT}>
data-testid="view-banner" <div className="flex items-baseline justify-between">
intent={Intent.None} <span data-testid="view-banner">
className={SHORT} {t('Viewing as Vega user: {{pubKey}}', {
> pubKey: pubKey && truncateMiddle(pubKey),
<div className="flex justify-between items-baseline"> })}
<span>
{t('Viewing as Vega user:')} {pubKey && truncateMiddle(pubKey)}{' '}
</span> </span>
<TradingButton <TradingButton
intent={Intent.None} intent={Intent.None}

View File

@ -0,0 +1,3 @@
import { useTranslation } from 'react-i18next';
export const ns = 'ui-toolkit';
export const useT = () => useTranslation(ns).t;