diff --git a/packages/synapse-interface/.eslintrc-i18n.js b/packages/synapse-interface/.eslintrc-i18n.js new file mode 100644 index 0000000000..9f1f881e58 --- /dev/null +++ b/packages/synapse-interface/.eslintrc-i18n.js @@ -0,0 +1,43 @@ +const { ignorePatterns } = require('./.eslintrc') + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + plugins: ['i18next'], + ignorePatterns: [ + 'pages/lifi/index.tsx', + 'components/Maintenance/example/EcotoneForkUpgrade.tsx', + 'pages/_app.tsx', + ], + rules: { + // Enable the i18next rule + 'i18next/no-literal-string': 'error', + + // Disable unrelated rules + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + 'prefer-arrow/prefer-arrow-functions': 'off', + 'import/order': 'off', + 'prefer-const': 'off', + 'jsdoc/newline-after-description': 'off', + 'no-duplicate-imports': 'off', + eqeqeq: 'off', + 'no-return-await': 'off', + 'object-shorthand': 'off', + 'prettier/prettier': 'off', + 'no-redeclare': 'off', + radix: 'off', + 'jsdoc/check-alignment': 'off', + 'no-undef-init': 'off', + }, + // Ignore all other plugins and extends + extends: [], +} diff --git a/packages/synapse-interface/README.md b/packages/synapse-interface/README.md index 52bf427411..4023fc2c54 100644 --- a/packages/synapse-interface/README.md +++ b/packages/synapse-interface/README.md @@ -64,6 +64,7 @@ This guide explains how to use the Maintenance feature to pause a chain or bridg ## How it works There are a few maintenance components implemented around the Synapse Interface Webapp: + 1. Banner - located at the top of the page. 2. Countdown Progress Bar - located at the top of Bridge / Swap cards. 3. Warning Message - located below the input UI in Bridge / Swap cards. @@ -71,16 +72,16 @@ There are a few maintenance components implemented around the Synapse Interface NOTE: Currently, the Synapse Widget implements the Countdown Progress Bar and Warning Message displayed in the Bridge Widget, but not the Banner. These components ingest data fetched from the following JSON files: + - [Pause Chains JSON](https://github.com/synapsecns/sanguine/blob/master/packages/synapse-interface/public/pauses/v1/paused-chains.json) - [Pause Bridge Modules JSON](https://github.com/synapsecns/sanguine/blob/master/packages/synapse-interface/public/pauses/v1/paused-bridge-modules.json) - To control when the Banner, Countdown Progress Bar, and Warning Message components are displayed, update the [Pause Chains JSON](https://github.com/synapsecns/sanguine/blob/master/packages/synapse-interface/public/pauses/v1/paused-chains.json). To specify which bridge modules (SynapseRFQ, SynapseBridge, or SynapseCCTP) are paused, update the [Pause Bridge Modules JSON](https://github.com/synapsecns/sanguine/blob/master/packages/synapse-interface/public/pauses/v1/paused-bridge-modules.json). - After updating the proper JSON files, the following steps must be taken to ensure the production webapp reflects the changes made: + 1. Merge the new branch into `master` 2. Merge `master` branch into `fe-release` branch @@ -88,7 +89,6 @@ After Step 1 is completed, the [Github Pages](https://github.com/synapsecns/sang Although completing Step 1 will already reflect changes in the webapp, Step 2 is required in the slim chance that the github API is down, so that the production webapp can use the local JSON files as a reliable backup data source. - ## Chain Pause You can pause the Bridge and Swap functionalities on specific chains using their chainIds. Pauses can be applied independently to Bridge or Swap functions, or to both simultaneously. @@ -97,7 +97,6 @@ For Bridge functionality, you can specify the origin and destination chainIds to Additionally, you can control which components are displayed during the pause event. - ### Chain Pause Props `id` @@ -145,77 +144,76 @@ Boolean indicating whether to hide Warning Message in Bridge or Swap card. `disableCountdown` Boolean indicating whether to hide Countdown Progress Bar. - ### Example `paused-chains.json` -```tsx - [ - // Bridge Pause - { - "id": "base-chain-pause", - "pausedFromChains": [8453], - "pausedToChains": [8453], - "pauseBridge": true, - "pauseSwap": false, - "startTimePauseChain": "2024-04-12T17:41:00Z", - "endTimePauseChain": null, - "startTimeBanner": "2024-04-12T04:40:00Z", - "endTimeBanner": null, - "inputWarningMessage": "Base bridging is paused until maintenance is complete.", - "bannerMessage": "Base bridging is paused until maintenance is complete.", - "progressBarMessage": "Base maintenance in progress", - "disableBanner": false, - "disableWarning": false, - "disableCountdown": false - }, - { - "id": "ecotone-fork-pause", - "pausedFromChains": [10, 8453], - "pausedToChains": [10, 8453], - "pauseBridge": true, - "pauseSwap": false, - "startTimePauseChain": "2024-03-13T23:35:00Z", - "endTimePauseChain": "2024-03-14T00:25:00Z", - "startTimeBanner": "2024-03-13T23:20:00Z", - "endTimeBanner": "2024-03-14T00:25:00Z", - "inputWarningMessage": "", - "bannerMessage": "Optimism + Base Bridging will be paused 10 minutes ahead of Ecotone (March 14 00:00 UTC, 20:00 EST). Will be back online shortly following the network upgrade.", - "progressBarMessage": "Ecotone Fork maintenance in progress", - "disableBanner": false, - "disableWarning": true, - "disableCountdown": false - }, - // Swap Pause - { - "id": "arbitrum-swap-pause", - "pausedFromChains": [42161], - "pausedToChains": [42161], - "pauseBridge": false, - "pauseSwap": true, - "startTimePauseChain": "2024-03-13T23:35:00Z", - "endTimePauseChain": "2024-03-14T00:25:00Z", - "startTimeBanner": "2024-03-13T23:20:00Z", - "endTimeBanner": "2024-03-14T00:25:00Z", - "inputWarningMessage": "Swapping on Arbitrum is paused until maintenance is complete.", - "bannerMessage": "Swapping on Arbitrum is paused until maintenance is complete.", - "progressBarMessage": "Arbitrum maintenance in progress", - "disableBanner": false, - "disableWarning": false, - "disableCountdown": false - } - ] -``` +```json +[ + // Bridge Pause + { + "id": "base-chain-pause", + "pausedFromChains": [8453], + "pausedToChains": [8453], + "pauseBridge": true, + "pauseSwap": false, + "startTimePauseChain": "2024-04-12T17:41:00Z", + "endTimePauseChain": null, + "startTimeBanner": "2024-04-12T04:40:00Z", + "endTimeBanner": null, + "inputWarningMessage": "Base bridging is paused until maintenance is complete.", + "bannerMessage": "Base bridging is paused until maintenance is complete.", + "progressBarMessage": "Base maintenance in progress", + "disableBanner": false, + "disableWarning": false, + "disableCountdown": false + }, + { + "id": "ecotone-fork-pause", + "pausedFromChains": [10, 8453], + "pausedToChains": [10, 8453], + "pauseBridge": true, + "pauseSwap": false, + "startTimePauseChain": "2024-03-13T23:35:00Z", + "endTimePauseChain": "2024-03-14T00:25:00Z", + "startTimeBanner": "2024-03-13T23:20:00Z", + "endTimeBanner": "2024-03-14T00:25:00Z", + "inputWarningMessage": "", + "bannerMessage": "Optimism + Base Bridging will be paused 10 minutes ahead of Ecotone (March 14 00:00 UTC, 20:00 EST). Will be back online shortly following the network upgrade.", + "progressBarMessage": "Ecotone Fork maintenance in progress", + "disableBanner": false, + "disableWarning": true, + "disableCountdown": false + }, + // Swap Pause + { + "id": "arbitrum-swap-pause", + "pausedFromChains": [42161], + "pausedToChains": [42161], + "pauseBridge": false, + "pauseSwap": true, + "startTimePauseChain": "2024-03-13T23:35:00Z", + "endTimePauseChain": "2024-03-14T00:25:00Z", + "startTimeBanner": "2024-03-13T23:20:00Z", + "endTimeBanner": "2024-03-14T00:25:00Z", + "inputWarningMessage": "Swapping on Arbitrum is paused until maintenance is complete.", + "bannerMessage": "Swapping on Arbitrum is paused until maintenance is complete.", + "progressBarMessage": "Arbitrum maintenance in progress", + "disableBanner": false, + "disableWarning": false, + "disableCountdown": false + } +] +``` ## Bridge Module Pause You can pause a specific bridge module on a given chain. Currently, there are the following bridge modules: + - SynapseRFQ - SynapseCCTP - SynapseBridge - ### Bridge Module Pause Props `chainId` @@ -224,27 +222,33 @@ Chain ID of Chain to pause specific bridge module. `bridgeModuleName` Accepts 'SynapseRFQ', 'SynapseBridge', 'SynapseCCTP', or 'ALL'. If selecting 'ALL', all bridge modules will be paused for respective chainId. - ### Example `paused-bridge-modules.json` -```tsx - [ - { - "chainId": 42161, - "bridgeModuleName": "ALL" - }, - { - "chainId": 10, - "bridgeModuleName": "SynapseRFQ" - }, - { - "chainId": 10, - "bridgeModuleName": "SynapseCCTP" - }, - { - "chainId": 8453, - "bridgeModuleName": "SynapseBridge" - } - ] + +```json +[ + { + "chainId": 42161, + "bridgeModuleName": "ALL" + }, + { + "chainId": 10, + "bridgeModuleName": "SynapseRFQ" + }, + { + "chainId": 10, + "bridgeModuleName": "SynapseCCTP" + }, + { + "chainId": 8453, + "bridgeModuleName": "SynapseBridge" + } +] ``` + +## Adding a New Language + +1. Add the new locale code to `next.config.js`. +2. Include the new locale in the `LanguageSelector`. +3. Populate `/messages/{locale.json}` translations for the new locale. The keys in this should match `en-US.json`. See some of the other language files for reference. diff --git a/packages/synapse-interface/components/Activity/Activity.tsx b/packages/synapse-interface/components/Activity/Activity.tsx index 0c9901c9e4..5fe5012493 100644 --- a/packages/synapse-interface/components/Activity/Activity.tsx +++ b/packages/synapse-interface/components/Activity/Activity.tsx @@ -3,6 +3,8 @@ import _ from 'lodash' import Fuse from 'fuse.js' import { useAccount } from 'wagmi' import { type Address } from 'viem' +import { useTranslations } from 'next-intl' + import { type Chain } from '@/utils/types' import { useTransactionsState } from '@/slices/transactions/hooks' import { usePortfolioState } from '@/slices/portfolio/hooks' @@ -50,6 +52,8 @@ export const Activity = ({ visibility }: { visibility: boolean }) => { } else return null }, [isMasqueradeActive, masqueradeAddress, address]) + const t = useTranslations('Activity') + return (
{ > {!viewingAddress && (
- Your pending and recent transactions will appear here. + {t('Your pending and recent transactions will appear here')}
)} {viewingAddress && isLoading && ( -
Loading activity...
+
{t('Loading activity')}...
)} {viewingAddress && !isLoading && !hasHistoricalTransactions && (
- No transactions in last 30 days. + {t('No transactions in last 30 days.')}
)} {viewingAddress && !isLoading && hasHistoricalTransactions && ( - + {userHistoricalTransactions && filteredHistoricalTransactions .slice(0, isSearchInputActive ? 100 : 6) diff --git a/packages/synapse-interface/components/Activity/Transaction/components/Completed.tsx b/packages/synapse-interface/components/Activity/Transaction/components/Completed.tsx index dcb65c8fc8..f3f8e1c4ea 100644 --- a/packages/synapse-interface/components/Activity/Transaction/components/Completed.tsx +++ b/packages/synapse-interface/components/Activity/Transaction/components/Completed.tsx @@ -1,4 +1,6 @@ import { Address } from 'viem' +import { useTranslations } from 'next-intl' + import { shortenAddress } from '@/utils/shortenAddress' import { convertUnixTimestampToMonthAndDate } from '@/utils/time' import { isTimestampToday } from '@/utils/time' @@ -26,6 +28,8 @@ export const Completed = ({ const isDestinationValid: boolean = isValidAddress(destinationAddress) + const t = useTranslations('Completed') + return (
{isDestinationValid && !isDestinationSender && ( -
to {shortenAddress(destinationAddress)}
+
+ {t('to')} {shortenAddress(destinationAddress)}{' '} +
)} {isToday ? ( -
Today
+
{t('Today')}
) : ( -
{formattedTime ? formattedTime : 'Completed'}
+
{formattedTime ? formattedTime : t('Completed')}
)}
) diff --git a/packages/synapse-interface/components/Activity/Transaction/components/EstimatedDuration.tsx b/packages/synapse-interface/components/Activity/Transaction/components/EstimatedDuration.tsx index d99bbcab4e..ed9b1dcf1b 100644 --- a/packages/synapse-interface/components/Activity/Transaction/components/EstimatedDuration.tsx +++ b/packages/synapse-interface/components/Activity/Transaction/components/EstimatedDuration.tsx @@ -1,4 +1,6 @@ import React from 'react' +import { useTranslations } from 'next-intl' + import { TransactionStatus } from '../Transaction' import ProcessingIcon from '@/components/icons/ProcessingIcon' @@ -9,6 +11,8 @@ export const EstimatedDuration = ({ timeRemaining: number transactionStatus: TransactionStatus }) => { + const t = useTranslations('Time') + return (
= 0 ? (
- {timeRemaining} - {timeRemaining + 1} min + {timeRemaining} - {timeRemaining + 1} {t('min')}
{transactionStatus !== TransactionStatus.PENDING_WALLET_ACTION && ( @@ -25,7 +29,7 @@ export const EstimatedDuration = ({
) : ( -
Waiting...
+
{t('Waiting')}...
)} diff --git a/packages/synapse-interface/components/Activity/Transaction/components/TransactionExplorerLink.tsx b/packages/synapse-interface/components/Activity/Transaction/components/TransactionExplorerLink.tsx index a401d5f57d..53ee35cc01 100644 --- a/packages/synapse-interface/components/Activity/Transaction/components/TransactionExplorerLink.tsx +++ b/packages/synapse-interface/components/Activity/Transaction/components/TransactionExplorerLink.tsx @@ -1,5 +1,7 @@ import Link from 'next/link' import { Address } from 'viem' +import { useTranslations } from 'next-intl' + import { EXPLORER_KAPPA, EXPLORER_PATH } from '@/constants/urls' export const getTransactionExplorerLink = ({ @@ -23,13 +25,15 @@ export const UserExplorerLink = ({ }: { connectedAddress?: Address | string }) => { + const t = useTranslations('Activity') + const explorerLink: string = connectedAddress ? `${EXPLORER_PATH}address/${connectedAddress}` : EXPLORER_PATH return (
- Explorer → + {t('Explorer')}
) diff --git a/packages/synapse-interface/components/ApyTooltip.tsx b/packages/synapse-interface/components/ApyTooltip.tsx index 0284e3be85..48303d0b67 100644 --- a/packages/synapse-interface/components/ApyTooltip.tsx +++ b/packages/synapse-interface/components/ApyTooltip.tsx @@ -1,3 +1,4 @@ +import { useTranslations } from 'next-intl' import { InformationCircleIcon } from '@heroicons/react/outline' import Grid from '@tw/Grid' @@ -35,9 +36,11 @@ export default function ApyTooltip({ const baseDailyApr: number = baseApyData.dailyApr ?? 0 const baseYearlyApr: number = baseApyData.yearlyApr ?? 0 + const t = useTranslations('Pools') + return ( @@ -88,6 +91,8 @@ const PercentageRow = ({ }) => { const totalApr = baseApr + rewardApr + const t = useTranslations('Pools') + return (
@@ -98,7 +103,8 @@ const PercentageRow = ({
{baseApr > 0 && ( - {rewardApr.toFixed(2)} reward + {baseApr.toFixed(2)} base + {rewardApr.toFixed(2)} {t('reward')} + {baseApr.toFixed(2)}{' '} + {t('base')} )}
diff --git a/packages/synapse-interface/components/ConnectionIndicators.tsx b/packages/synapse-interface/components/ConnectionIndicators.tsx index e16397866b..6a2a58a72d 100644 --- a/packages/synapse-interface/components/ConnectionIndicators.tsx +++ b/packages/synapse-interface/components/ConnectionIndicators.tsx @@ -4,6 +4,7 @@ import { useAccount } from 'wagmi' import { switchChain } from '@wagmi/core' import { LoaderIcon } from 'react-hot-toast' import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useTranslations } from 'next-intl' import { CHAINS_BY_ID } from '@/constants/chains' import { setFromChainId } from '@/slices/bridge/reducer' @@ -26,6 +27,9 @@ export const ConnectedIndicator = () => { hover: 'hover:opacity-80', font: 'text-sm', } + + const t = useTranslations('Wallet') + return ( ) } @@ -43,6 +47,8 @@ export const ConnectToNetworkButton = ({ chainId }: { chainId: number }) => { const dispatch = useDispatch() const chain = CHAINS_BY_ID[chainId] + const t = useTranslations('Wallet') + function scrollToTop(): void { window.scrollTo({ top: 0, behavior: 'smooth' }) } @@ -78,13 +84,13 @@ export const ConnectToNetworkButton = ({ chainId }: { chainId: number }) => { {isConnecting ? ( <> - Connecting + {t('Connecting')} ) : ( <> - Switch Network + {t('Switch Network')} )} @@ -95,6 +101,8 @@ export function ConnectWalletButton() { const [clientReady, setClientReady] = useState(false) const { address } = useAccount() + const t = useTranslations('Wallet') + useEffect(() => { setClientReady(true) }, []) @@ -124,7 +132,7 @@ export function ConnectWalletButton() { onClick={openConnectModal} > - Connect Wallet + {t('Connect Wallet')} ) } diff --git a/packages/synapse-interface/components/HarmonyCheck.tsx b/packages/synapse-interface/components/HarmonyCheck.tsx deleted file mode 100644 index aae2d83e77..0000000000 --- a/packages/synapse-interface/components/HarmonyCheck.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as CHAINS from '@/constants/chains/master' - -const HarmonyCheck = ({ - fromChainId, - toChainId, -}: { - fromChainId: number - toChainId: number -}) => { - return ( - <> - {(toChainId === CHAINS.HARMONY.id || - fromChainId === CHAINS.HARMONY.id) && ( -
- The native Harmony bridge has been exploited, which lead to a hard - depeg of the following Harmony-specific tokens: 1DAI, 1USDC, 1USDT, - 1ETH. -
Please see the{' '} - - official Harmony Twitter - {' '} - for status updates and exercise caution when interacting with Harmony. -
- )} - - ) -} -export default HarmonyCheck diff --git a/packages/synapse-interface/components/InteractiveInputRow.tsx b/packages/synapse-interface/components/InteractiveInputRow.tsx index 35f504fd70..4762f7e682 100644 --- a/packages/synapse-interface/components/InteractiveInputRow.tsx +++ b/packages/synapse-interface/components/InteractiveInputRow.tsx @@ -1,5 +1,6 @@ import React from 'react' import Button from '@tw/Button' +import { useTranslations } from 'next-intl' const InteractiveInputRow = ({ title, @@ -22,6 +23,8 @@ const InteractiveInputRow = ({ disabled: boolean icon: string }) => { + const t = useTranslations('Bridge') + return (
@@ -101,7 +104,7 @@ const InteractiveInputRow = ({ `} onClick={disabled ? undefined : onClickBalance} > - Max + {t('Max')}
)} @@ -113,7 +116,10 @@ const InteractiveInputRow = ({ onClick={onClickBalance} > {balanceStr} - available + + {' '} + {t('available')} + )}
diff --git a/packages/synapse-interface/components/LanguageSelector.tsx b/packages/synapse-interface/components/LanguageSelector.tsx new file mode 100644 index 0000000000..2b51d5f920 --- /dev/null +++ b/packages/synapse-interface/components/LanguageSelector.tsx @@ -0,0 +1,89 @@ +import React, { useState, useRef, useEffect } from 'react' +import { useRouter } from 'next/router' +import { useTranslations } from 'next-intl' + +import { GlobeAltIcon } from '@heroicons/react/outline' +import useCloseOnOutsideClick from '@/utils/hooks/useCloseOnOutsideClick' +import { useCloseOnEscape } from '@/utils/hooks/useCloseOnEscape' + +const languages = [ + { code: 'en-US', name: 'English' }, + { code: 'ar', name: 'العربية' }, + { code: 'es', name: 'Español' }, + { code: 'fr', name: 'Français' }, + { code: 'tr', name: 'Türkçe' }, +] + +export const LanguageSelector = () => { + const router = useRouter() + const t = useTranslations('LanguageSelector') + const { pathname, asPath, query } = router + const [isOpen, setIsOpen] = useState(false) + const [currentLocale, setCurrentLocale] = useState(router.locale) + const dropdownRef = useRef(null) + + useEffect(() => { + const storedLanguage = localStorage.getItem('selectedLanguage') + if (storedLanguage && storedLanguage !== router.locale) { + router.push({ pathname, query }, asPath, { locale: storedLanguage }) + } + }, []) + + useEffect(() => { + setCurrentLocale(router.locale) + }, [router.locale]) + + const toggleDropdown = () => setIsOpen(!isOpen) + + const handleLanguageChange = (lang) => { + router.push({ pathname, query }, asPath, { locale: lang.code }) + localStorage.setItem('selectedLanguage', lang.code) + } + + const closeDropdown = () => setIsOpen(false) + + useCloseOnOutsideClick(dropdownRef, closeDropdown) + useCloseOnEscape(closeDropdown) + + return ( +
+ + {isOpen && ( +
+
+
{t('Language')}
+ {languages.map((lang) => ( +
handleLanguageChange(lang)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleLanguageChange(lang) + } + }} + tabIndex={0} + > + {lang.name} +
+ ))} +
+
+ )} +
+ ) +} diff --git a/packages/synapse-interface/components/Maintenance/components/EventCountdownProgressBar.tsx b/packages/synapse-interface/components/Maintenance/components/EventCountdownProgressBar.tsx index 42055a8707..7860c3c3f3 100644 --- a/packages/synapse-interface/components/Maintenance/components/EventCountdownProgressBar.tsx +++ b/packages/synapse-interface/components/Maintenance/components/EventCountdownProgressBar.tsx @@ -1,4 +1,6 @@ import { isNull } from 'lodash' +import { useTranslations } from 'next-intl' + import { LinearAnimatedProgressBar } from './LinearAnimatedProgressBar' import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' @@ -76,6 +78,7 @@ export const EventCountdownProgressBar = ({ status: 'idle' | 'pending' | 'complete' }) => { const isIndefinite = isNull(endDate) + const t = useTranslations('Activity') if (status === 'pending') { return ( @@ -88,7 +91,11 @@ export const EventCountdownProgressBar = ({ >
{eventLabel}
- {isIndefinite ? null :
{timeRemaining} remaining
} + {isIndefinite ? null : ( +
+ {timeRemaining} {t('remaining')} +
+ )}
{ const router = useRouter() + const t = useTranslations('Pools') + const handleWithdrawClick = () => { router.push(`/pool/${pool.routerIndex}`) } @@ -60,43 +64,43 @@ export const PoolActionOptions = ({ ${open ? 'bg-[#101018]' : ''} `} > -
Actions
+
{t('Actions')}
- {options.includes('Deposit') && ( + {options.includes(t('Deposit')) && ( } - text={'Deposit'} + text={t('Deposit')} onClick={handleDepositClick} /> )} - {options.includes('Stake') && ( + {options.includes(t('Stake')) && ( } - text={'Stake'} + text={t('Stake')} onClick={handleStakeClick} /> )} - {options.includes('Unstake') && ( + {options.includes(t('Unstake')) && ( } - text={'Unstake'} + text={t('Unstake')} onClick={handleUnstakeClick} /> )} - {options.includes('Withdraw') && ( + {options.includes(t('Withdraw')) && ( } - text={'Withdraw'} + text={t('Withdraw')} onClick={handleWithdrawClick} /> )} - {options.includes('Claim') && ( + {options.includes(t('Claim')) && ( } - text={'Claim'} + text={t('Claim')} onClick={handleClaimClick} /> )} diff --git a/packages/synapse-interface/components/Pools/PoolHeader.tsx b/packages/synapse-interface/components/Pools/PoolHeader.tsx index 55f1ce6336..f260cb2bc8 100644 --- a/packages/synapse-interface/components/Pools/PoolHeader.tsx +++ b/packages/synapse-interface/components/Pools/PoolHeader.tsx @@ -3,6 +3,7 @@ import { memo, useEffect, useMemo, useState } from 'react' import Link from 'next/link' import { useAccount } from 'wagmi' import { Address } from 'viem' +import { useTranslations } from 'next-intl' import { getPoolUrl } from '@urls' import { CHAINS_BY_ID } from '@/constants/chains' @@ -17,6 +18,8 @@ export const PoolHeader = memo( const chain = CHAINS_BY_ID[pool.chainId] const { balances } = usePortfolioState() + const t = useTranslations('Pools') + useEffect(() => { setMounted(true) }, []) @@ -57,7 +60,7 @@ export const PoolHeader = memo( {canDeposit && pool.incentivized ? (
-
Deposit
+
{t('Deposit')}
@@ -72,10 +75,11 @@ export const PoolHeader = memo( ) const ConnectedIndicator = () => { + const t = useTranslations('Wallet') return (
-
Connected
+
{t('Connected')}
) } diff --git a/packages/synapse-interface/components/Portfolio/components/ConnectWalletButton.tsx b/packages/synapse-interface/components/Portfolio/components/ConnectWalletButton.tsx index 427848a047..349dd4f32f 100644 --- a/packages/synapse-interface/components/Portfolio/components/ConnectWalletButton.tsx +++ b/packages/synapse-interface/components/Portfolio/components/ConnectWalletButton.tsx @@ -1,11 +1,14 @@ import { useState, useEffect } from 'react' import { useAccount } from 'wagmi' import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useTranslations } from 'next-intl' export function ConnectWalletButton() { const [clientReady, setClientReady] = useState(false) const { address } = useAccount() + const t = useTranslations('Wallet') + useEffect(() => { setClientReady(true) }, []) @@ -35,7 +38,7 @@ export function ConnectWalletButton() { style={buttonStyle} onClick={openConnectModal} > - Connect Wallet + {t('Connect Wallet')} ) } diff --git a/packages/synapse-interface/components/Portfolio/components/EmptyPortfolioContent.tsx b/packages/synapse-interface/components/Portfolio/components/EmptyPortfolioContent.tsx index 4d3df1600d..f46f280548 100644 --- a/packages/synapse-interface/components/Portfolio/components/EmptyPortfolioContent.tsx +++ b/packages/synapse-interface/components/Portfolio/components/EmptyPortfolioContent.tsx @@ -1,5 +1,7 @@ import { Address } from 'viem' import Link from 'next/link' +import { useTranslations } from 'next-intl' + import { Chain } from '@/utils/types' import { shortenAddress } from '@/utils/shortenAddress' import { DISCORD_URL, TWITTER_URL } from '@/constants/urls' @@ -12,31 +14,35 @@ export const EmptyPortfolioContent = ({ connectedChain: Chain }) => { const shortened: string = shortenAddress(connectedAddress) + const t = useTranslations('Wallet') + return (

- No bridgeable assets found {connectedAddress && `for ${shortened}`} on{' '} - {connectedChain?.name}. + {t('No bridgeable assets found for {address} on {chainName}.', { + address: connectedAddress && shortened, + chainName: connectedChain?.name, + })}

- Don't see a chain or token you want to bridge? + {t("Don't see a chain or token you want to bridge?")}

- Let us know on + {t('Let us know on')} - Twitter + {t('Twitter')} - or + {t('or')} - Discord + {t('Discord')} . diff --git a/packages/synapse-interface/components/Portfolio/components/GasTokenAsset.tsx b/packages/synapse-interface/components/Portfolio/components/GasTokenAsset.tsx index 43c327f3fe..1949b01a8d 100644 --- a/packages/synapse-interface/components/Portfolio/components/GasTokenAsset.tsx +++ b/packages/synapse-interface/components/Portfolio/components/GasTokenAsset.tsx @@ -1,5 +1,7 @@ import React from 'react' import Image from 'next/image' +import { useTranslations } from 'next-intl' + import { Token } from '@/utils/types' import { getParsedBalance } from '@/utils/getParsedBalance' import { HoverTooltip } from '@/components/HoverTooltip' @@ -16,6 +18,8 @@ export const GasTokenAsset = ({ const { icon, symbol, decimals } = token const parsedBalance = getParsedBalance(balance, decimals as number) + const t = useTranslations('Bridge') + return (
Gas token
} + hoverContent={ +
{t('Gas token')}
+ } >
-
Not bridgeable
+
{t('Not bridgeable')}
) } diff --git a/packages/synapse-interface/components/Portfolio/components/NoSearchResultContent.tsx b/packages/synapse-interface/components/Portfolio/components/NoSearchResultContent.tsx index 447cddf7c8..389c8c5893 100644 --- a/packages/synapse-interface/components/Portfolio/components/NoSearchResultContent.tsx +++ b/packages/synapse-interface/components/Portfolio/components/NoSearchResultContent.tsx @@ -1,11 +1,16 @@ +import { useTranslations } from 'next-intl' + export const NoSearchResultsContent = ({ searchStr, }: { searchStr: string }) => { + const t = useTranslations('Search') return (
-

No results found for '{searchStr}'.

+

+ {t('No results found for')} '{searchStr}'. +

) } diff --git a/packages/synapse-interface/components/Portfolio/components/PortfolioAssetActionButton.tsx b/packages/synapse-interface/components/Portfolio/components/PortfolioAssetActionButton.tsx index 601f589554..d6c6676b0e 100644 --- a/packages/synapse-interface/components/Portfolio/components/PortfolioAssetActionButton.tsx +++ b/packages/synapse-interface/components/Portfolio/components/PortfolioAssetActionButton.tsx @@ -1,3 +1,5 @@ +import { useTranslations } from 'next-intl' + type PortfolioAssetActionButtonProps = { selectCallback: () => void isDisabled: boolean @@ -9,6 +11,8 @@ export const PortfolioAssetActionButton = ({ isDisabled, isSelected, }: PortfolioAssetActionButtonProps) => { + const t = useTranslations('Activity') + return ( <> ) diff --git a/packages/synapse-interface/components/Portfolio/components/PortfolioConnectButton.tsx b/packages/synapse-interface/components/Portfolio/components/PortfolioConnectButton.tsx index f051f41bd4..e79637cd22 100644 --- a/packages/synapse-interface/components/Portfolio/components/PortfolioConnectButton.tsx +++ b/packages/synapse-interface/components/Portfolio/components/PortfolioConnectButton.tsx @@ -1,6 +1,8 @@ import { useMemo, useState } from 'react' import { useDispatch } from 'react-redux' import { switchChain } from '@wagmi/core' +import { useTranslations } from 'next-intl' + import { setFromChainId } from '@/slices/bridge/reducer' import { wagmiConfig } from '@/wagmiConfig' @@ -29,6 +31,8 @@ export const PortfolioConnectButton = ({ } const ConnectedButton = () => { + const t = useTranslations('Wallet') + return (
) @@ -57,6 +61,8 @@ const ConnectButton = ({ chainId }: { chainId: number }) => { const [isConnecting, setIsConnecting] = useState(false) const dispatch = useDispatch() + const t = useTranslations('Wallet') + function scrollToTop(): void { window.scrollTo({ top: 0, behavior: 'smooth' }) } @@ -93,7 +99,7 @@ const ConnectButton = ({ chainId }: { chainId: number }) => { border border-green-400 border-solid rounded-full `} /> - Connecting... + {t('Connecting')}...
) : (
@@ -103,7 +109,7 @@ const ConnectButton = ({ chainId }: { chainId: number }) => { border border-indigo-300 border-solid rounded-full `} /> - Connect + {t('Connect')}
)} diff --git a/packages/synapse-interface/components/Portfolio/components/PortfolioContent.tsx b/packages/synapse-interface/components/Portfolio/components/PortfolioContent.tsx index 7673946f16..f33f8dca10 100644 --- a/packages/synapse-interface/components/Portfolio/components/PortfolioContent.tsx +++ b/packages/synapse-interface/components/Portfolio/components/PortfolioContent.tsx @@ -1,5 +1,7 @@ import React, { useMemo } from 'react' import { Address } from 'viem' +import { useTranslations } from 'next-intl' + import { NetworkTokenBalances, TokenAndBalance, @@ -135,25 +137,29 @@ function getCurrentNetworkPortfolio( } const LoadingPortfolioContent = () => { + const t = useTranslations('Portfolio') return ( <>

- Loading assets... + {t('Loading assets')}...

) } const HomeContent = () => { + const t = useTranslations('Portfolio') return (

- Synapse is the most widely used, extensible, and secure cross-chain - communications network. + {t( + 'Synapse is the most widely used, extensible, and secure cross-chain communications network' + )}

- Get route quotes in the Bridge panel, and connect your wallet when you - are ready to submit a transaction. + {t( + 'Get route quotes in the Bridge panel, and connect your wallet when you are ready to submit a transaction' + )}

diff --git a/packages/synapse-interface/components/Portfolio/components/PortfolioTabManager.tsx b/packages/synapse-interface/components/Portfolio/components/PortfolioTabManager.tsx index 33d448bb99..b469fad237 100644 --- a/packages/synapse-interface/components/Portfolio/components/PortfolioTabManager.tsx +++ b/packages/synapse-interface/components/Portfolio/components/PortfolioTabManager.tsx @@ -1,5 +1,7 @@ -import { useAppDispatch } from '@/store/hooks' import { useAccount } from 'wagmi' +import { useTranslations } from 'next-intl' + +import { useAppDispatch } from '@/store/hooks' import { usePortfolioState } from '@/slices/portfolio/hooks' import { PortfolioTabs, setActiveTab } from '@/slices/portfolio/actions' import { SearchBar } from './SearchBar' @@ -14,17 +16,19 @@ export const PortfolioTabManager = () => { dispatch(setActiveTab(newTab)) } + const t = useTranslations() + return (
{ dispatch(setFromChainId(portfolioChainId)) dispatch(setFromToken(token)) @@ -77,7 +81,7 @@ export const PortfolioTokenAsset = ({ hoverContent={ isPortfolioChainSelected && isGasToken && maxBridgeableGas ? (
- Available:{' '} + {t('Available')}:{' '} {trimTrailingZeroesAfterDecimal(maxBridgeableGas.toFixed(8))}{' '} {symbol}
@@ -95,7 +99,9 @@ export const PortfolioTokenAsset = ({ {isGasToken && ( Gas token
} + hoverContent={ +
{t('Gas token')}
+ } > diff --git a/packages/synapse-interface/components/Portfolio/components/SearchBar.tsx b/packages/synapse-interface/components/Portfolio/components/SearchBar.tsx index b674cbba5e..8434a254b8 100644 --- a/packages/synapse-interface/components/Portfolio/components/SearchBar.tsx +++ b/packages/synapse-interface/components/Portfolio/components/SearchBar.tsx @@ -1,5 +1,7 @@ import React, { useEffect } from 'react' import { Address } from 'viem' +import { useTranslations } from 'next-intl' + import { useAppDispatch } from '@/store/hooks' import { usePortfolioActionHandlers, @@ -117,12 +119,14 @@ const FilterInput = ({ } function getFilterPlaceholder(activeTab: PortfolioTabs | undefined) { + const t = useTranslations('Portfolio') + switch (activeTab) { case PortfolioTabs.PORTFOLIO: - return 'Tokens, chains...' + return `${t('Tokens, chains')}...` case PortfolioTabs.ACTIVITY: - return 'Bridge txs...' + return `${t('Bridge txs')}...` default: - return 'Search...' + return `${t('Search')}...` } } diff --git a/packages/synapse-interface/components/Portfolio/components/SingleNetworkPortfolio.tsx b/packages/synapse-interface/components/Portfolio/components/SingleNetworkPortfolio.tsx index eba363e7d7..7cf79458fc 100644 --- a/packages/synapse-interface/components/Portfolio/components/SingleNetworkPortfolio.tsx +++ b/packages/synapse-interface/components/Portfolio/components/SingleNetworkPortfolio.tsx @@ -2,6 +2,8 @@ import React, { useEffect } from 'react' import { Address } from 'viem' import { useDispatch } from 'react-redux' import _, { isArray } from 'lodash' +import { useTranslations } from 'next-intl' + import { CHAINS_BY_ID } from '@/constants/chains' import { TokenAndBalance, @@ -42,6 +44,8 @@ export const SingleNetworkPortfolio = ({ }: SingleNetworkPortfolioProps) => { const dispatch = useDispatch() + const t = useTranslations('Portfolio') + const isLoading = fetchState === FetchState.LOADING const chain: Chain = CHAINS_BY_ID[portfolioChainId] @@ -98,14 +102,15 @@ export const SingleNetworkPortfolio = ({ twClassName="!p-2 !mt-0" message={

- This chain is not yet supported. New chain or token support can - be discussed on{' '} + {t( + 'This chain is not yet supported; New chain or token support can be discussed on' + )}{' '} - Twitter + {t('Twitter')} {' '} - or{' '} + {t('or')}{' '} - Discord + {t('Discord')} .

diff --git a/packages/synapse-interface/components/Portfolio/components/ViewSearchAddressBanner.tsx b/packages/synapse-interface/components/Portfolio/components/ViewSearchAddressBanner.tsx index 7f548d31d0..cfe49751a1 100644 --- a/packages/synapse-interface/components/Portfolio/components/ViewSearchAddressBanner.tsx +++ b/packages/synapse-interface/components/Portfolio/components/ViewSearchAddressBanner.tsx @@ -1,4 +1,6 @@ import { Address } from 'viem' +import { useTranslations } from 'next-intl' + import { usePortfolioActionHandlers } from '@/slices/portfolio/hooks' import { shortenAddress } from '@/utils/shortenAddress' import { ClearSearchButton } from './ClearSearchButton' @@ -10,6 +12,8 @@ export const ViewSearchAddressBanner = ({ }) => { const { clearSearchResults } = usePortfolioActionHandlers() const shortened: string = shortenAddress(viewingAddress, 4) + const t = useTranslations('Portfolio') + return (
-
Viewing
+
{t('Viewing')}
{shortened}
diff --git a/packages/synapse-interface/components/StateManagedBridge/AvailableBalance.tsx b/packages/synapse-interface/components/StateManagedBridge/AvailableBalance.tsx index a89b8070ab..ef51044d78 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AvailableBalance.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/AvailableBalance.tsx @@ -1,4 +1,6 @@ import React from 'react' +import { useTranslations } from 'next-intl' + import { joinClassNames } from '@/utils/joinClassNames' import { formatAmount } from '@/utils/formatAmount' @@ -22,20 +24,24 @@ export const AvailableBalance = ({ cursor: 'cursor-default', } + const t = useTranslations('Bridge') + if (isDisabled) { return null } else if (isGasToken && isGasEstimateLoading) { return ( ) } else { return ( ) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx index 9524d59225..52c9e9ffcb 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx @@ -1,6 +1,8 @@ import { useMemo } from 'react' import { useBridgeState } from '@/slices/bridge/hooks' import { useAccount } from 'wagmi' +import { useTranslations } from 'next-intl' + import { useCoingeckoPrice } from '@hooks/useCoingeckoPrice' import { formatBigIntToString } from '@/utils/bigint/format' import { formatBigIntToPercentString } from '@/utils/bigint/format' @@ -37,6 +39,8 @@ const DestinationAddress = () => { const { address } = useAccount() const { destinationAddress } = useBridgeState() + const t = useTranslations('Bridge') + const showAddress = destinationAddress && getValidAddress(address) !== getValidAddress(destinationAddress) @@ -48,7 +52,7 @@ const DestinationAddress = () => { if (showAddress && isInputValidAddress) { return (
-
To:
+
{t('To')}:
{destinationAddress}
) @@ -57,6 +61,7 @@ const DestinationAddress = () => { const Slippage = () => { const { debouncedFromValue } = useBridgeState() + const t = useTranslations('Bridge') const { bridgeQuote: { exchangeRate }, @@ -66,7 +71,7 @@ const Slippage = () => { useExchangeRateInfo(debouncedFromValue, exchangeRate) return (
- Slippage + {t('Slippage')} {safeFromAmount !== '0' && !underFee ? ( {formattedPercentSlippage} ) : ( @@ -80,9 +85,10 @@ const Router = () => { const { bridgeQuote: { bridgeModuleName }, } = useBridgeQuoteState() + const t = useTranslations('Bridge') return (
- Router + {t('Router')} {bridgeModuleName}
) @@ -91,6 +97,7 @@ const Router = () => { const TimeEstimate = () => { const { fromToken } = useBridgeState() const { bridgeQuote } = useBridgeQuoteState() + const t = useTranslations() let showText let showTime @@ -98,13 +105,13 @@ const TimeEstimate = () => { if (fromToken && bridgeQuote?.estimatedTime > 60) { showTime = bridgeQuote?.estimatedTime / 60 - timeUnit = 'minutes' + timeUnit = t('Time.minutes') showText = `${showTime} ${timeUnit} via ${bridgeQuote.bridgeModuleName}` } if (fromToken && bridgeQuote.estimatedTime <= 60) { showTime = bridgeQuote?.estimatedTime - timeUnit = 'seconds' + timeUnit = t('Time.seconds') showText = `${showTime} ${timeUnit} via ${bridgeQuote.bridgeModuleName}` } @@ -114,13 +121,13 @@ const TimeEstimate = () => { ) { showText = ( - Powered by Synapse + {t('Bridge.Powered by Synapse')} ) } if (!fromToken) { - showText = `Select origin token` + showText = t('Bridge.Select origin token') } return showText @@ -132,6 +139,8 @@ const GasDropLabel = () => { const { bridgeQuote: { gasDropAmount }, } = useBridgeQuoteState() + + const t = useTranslations('Bridge') const symbol = CHAINS_BY_ID[toChainId]?.nativeCurrency.symbol if ([CHAINS.FANTOM.id].includes(toChainId)) { @@ -159,7 +168,7 @@ const GasDropLabel = () => { return ( <> - Will also receive {formattedGasDropAmount} + {t('Will also receive')} {formattedGasDropAmount} {' '} diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 0bde7cc3ae..44aa9a3fb5 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -2,6 +2,7 @@ import { isAddress } from 'viem' import { useEffect, useState } from 'react' import { useAccount, useAccountEffect, useSwitchChain } from 'wagmi' import { useConnectModal } from '@rainbow-me/rainbowkit' +import { useTranslations } from 'next-intl' import { useAppDispatch } from '@/store/hooks' import { useWalletState } from '@/slices/wallet/hooks' @@ -26,6 +27,8 @@ export const BridgeTransactionButton = ({ const { isConnected: isConnectedInit } = useAccount() const { chains, switchChain } = useSwitchChain() + const t = useTranslations('Bridge') + useAccountEffect({ onDisconnect() { setIsConnected(false) @@ -76,38 +79,38 @@ export const BridgeTransactionButton = ({ if (isBridgePaused) { buttonProperties = { - label: 'Bridge paused', + label: t('Bridge paused'), onClick: null, } } else if (!fromChainId) { buttonProperties = { - label: 'Please select Origin Network', + label: t('Please select Origin Network'), onClick: null, } } else if (!toChainId) { buttonProperties = { - label: 'Please select Destination network', + label: t('Please select Destination network'), onClick: null, } } else if (!fromToken) { buttonProperties = { - label: `Please select an Origin token`, + label: t('Please select an Origin token'), onClick: null, } } else if (isLoading) { buttonProperties = { - label: `Bridge ${fromToken?.symbol}`, - pendingLabel: `Bridge ${fromToken?.symbol}`, + label: t('Bridge {symbol}', { symbol: fromToken?.symbol }), + pendingLabel: t('Bridge {symbol}', { symbol: fromToken?.symbol }), onClick: null, } } else if (!isConnected && hasValidInput) { buttonProperties = { - label: `Connect Wallet to Bridge`, + label: t('Connect Wallet to Bridge'), onClick: openConnectModal, } } else if (!isLoading && isBridgeFeeGreaterThanInput && hasValidInput) { buttonProperties = { - label: `Amount must be greater than fee`, + label: t('Amount must be greater than fee'), onClick: null, } } else if ( @@ -118,7 +121,7 @@ export const BridgeTransactionButton = ({ hasValidInput ) { buttonProperties = { - label: 'Error in bridge quote', + label: t('Error in bridge quote'), onClick: null, } @@ -138,41 +141,43 @@ export const BridgeTransactionButton = ({ hasValidInput ) { buttonProperties = { - label: 'Invalid bridge quote', + label: t('Invalid bridge quote'), onClick: null, } } else if (!isLoading && isConnected && !hasSufficientBalance) { buttonProperties = { - label: 'Insufficient balance', + label: t('Insufficient balance'), onClick: null, } } else if (destinationAddress && !isAddress(destinationAddress)) { buttonProperties = { - label: 'Invalid Destination address', + label: t('Invalid Destination address'), } } else if (showDestinationWarning && !isDestinationWarningAccepted) { buttonProperties = { - label: 'Confirm destination address', + label: t('Confirm destination address'), onClick: () => dispatch(setIsDestinationWarningAccepted(true)), className: '!from-bgLight !to-bgLight', } } else if (!onSelectedChain && hasValidInput) { buttonProperties = { - label: `Switch to ${chains.find((c) => c.id === fromChainId)?.name}`, + label: t('Switch to {chainName}', { + chainName: chains.find((c) => c.id === fromChainId)?.name, + }), onClick: () => switchChain({ chainId: fromChainId }), - pendingLabel: 'Switching chains', + pendingLabel: t('Switching chains'), } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { onClick: approveTxn, - label: `Approve ${fromToken?.symbol}`, - pendingLabel: 'Approving', + label: t('Approve {symbol}', { symbol: fromToken?.symbol }), + pendingLabel: t('Approving'), } } else { buttonProperties = { onClick: executeBridge, - label: `Bridge ${fromToken?.symbol}`, - pendingLabel: 'Bridging', + label: t('Bridge {symbol}', { symbol: fromToken?.symbol }), + pendingLabel: t('Bridging'), } } diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx index 429507eb1e..9484d61882 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx @@ -1,9 +1,13 @@ +import { useTranslations } from 'next-intl' + import { useAppDispatch } from '@/store/hooks' import { useBridgeDisplayState } from '@/slices/bridge/hooks' import { setIsDestinationWarningAccepted } from '@/slices/bridgeDisplaySlice' export const ConfirmDestinationAddressWarning = () => { const dispatch = useAppDispatch() + const t = useTranslations('Destination') + const { showDestinationWarning, isDestinationWarningAccepted, @@ -36,11 +40,8 @@ export const ConfirmDestinationAddressWarning = () => { />

- Required: Verify your destination address to - continue. -
- Do not send assets to a custodial or exchange - address. It may be impossible to recover your funds. + {t('WarningMessage1')} {t('WarningMessage2')}
+ {t('WarningMessage3')} {t('WarningMessage4')}

diff --git a/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx b/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx index a88965cf7a..8734a0aa58 100644 --- a/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx @@ -1,5 +1,7 @@ import React, { useState, useRef, useEffect } from 'react' import { isNull, isString } from 'lodash' +import { useTranslations } from 'next-intl' + import { useAppDispatch } from '@/store/hooks' import { isValidAddress } from '@/utils/isValidAddress' import { shortenAddress } from '@/utils/shortenAddress' @@ -28,6 +30,7 @@ export const DestinationAddressInput = ({ connectedAddress: string }) => { const dispatch = useAppDispatch() + const t = useTranslations('Bridge') const { destinationAddress } = useBridgeState() const { showDestinationWarning } = useBridgeDisplayState() const { userHistoricalTransactions }: TransactionsState = @@ -238,7 +241,7 @@ export const DestinationAddressInput = ({ return (
-
To:
+
{t('To')}:
{shortenAddress(address)}
-
{daysAgo}d
+
+ {daysAgo} + {t('d')} +
) } diff --git a/packages/synapse-interface/components/StateManagedBridge/FromChainSelector.tsx b/packages/synapse-interface/components/StateManagedBridge/FromChainSelector.tsx index ecde8d2816..b0d4b8b373 100644 --- a/packages/synapse-interface/components/StateManagedBridge/FromChainSelector.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/FromChainSelector.tsx @@ -1,3 +1,5 @@ +import { useTranslations } from 'next-intl' + import { setFromChainId } from '@/slices/bridge/reducer' import { ChainSelector } from '@/components/ui/ChainSelector' import { CHAINS_BY_ID } from '@/constants/chains' @@ -9,12 +11,14 @@ export const FromChainSelector = () => { const { fromChainId } = useBridgeState() const { isWalletPending } = useWalletState() + const t = useTranslations('Bridge') + return ( { @@ -10,9 +12,11 @@ export const MaxButton = ({ onClick, isHidden }) => { hover: 'hover:opacity-70 cursor-pointer', } + const t = useTranslations('Bridge') + return ( ) } diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index 9f06ed4bd2..c891391fcd 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -15,6 +15,7 @@ import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { useWalletState } from '@/slices/wallet/hooks' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { useBridgeValidations } from './hooks/useBridgeValidations' +import { useTranslations } from 'next-intl' export const OutputContainer = () => { const { address } = useAccount() @@ -57,12 +58,14 @@ const ToChainSelector = () => { const { toChainId } = useBridgeState() const { isWalletPending } = useWalletState() + const t = useTranslations('Bridge') + return ( { const ToTokenSelector = () => { const { toToken } = useBridgeState() const { isWalletPending } = useWalletState() + const t = useTranslations('Bridge') return ( { const dispatch = useAppDispatch() + const t = useTranslations('Settings') + const { showDestinationAddress } = useBridgeDisplayState() const onClose = () => { @@ -42,13 +46,13 @@ const SettingsSlideOver = () => { `} >
-
Options
+
{t('Options')}
{/* @ts-ignore */}
- Show withdrawal address{' '} - + {t('Show withdrawal address')}{' '} + @@ -90,6 +94,8 @@ const SettingsSlideOver = () => { const DeadlineInput = ({ deadlineMinutes }: { deadlineMinutes: number }) => { const dispatch = useAppDispatch() + const t = useTranslations('Time') + return (
{ value={deadlineMinutes} /> - mins + {t('min')}
diff --git a/packages/synapse-interface/components/StateManagedBridge/SettingsToggle.tsx b/packages/synapse-interface/components/StateManagedBridge/SettingsToggle.tsx index fa001ea391..fe8c287a9b 100644 --- a/packages/synapse-interface/components/StateManagedBridge/SettingsToggle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/SettingsToggle.tsx @@ -1,3 +1,5 @@ +import { useTranslations } from 'next-intl' + import { SettingsIcon } from '../icons/SettingsIcon' export const SettingsToggle = ({ @@ -5,15 +7,16 @@ export const SettingsToggle = ({ }: { showSettingsToggle: boolean }) => { + const t = useTranslations('Settings') return ( <> {showSettingsToggle ? ( <> - Settings + {t('Settings')} ) : ( - Close + {t('Close')} )} ) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useFromChainListArray.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useFromChainListArray.ts index 8d9d423306..ca05617f86 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useFromChainListArray.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useFromChainListArray.ts @@ -1,5 +1,6 @@ import _ from 'lodash' import Fuse from 'fuse.js' +import { useTranslations } from 'next-intl' import * as ALL_CHAINS from '@/constants/chains/master' import { CHAINS_BY_ID, sortChains } from '@/constants/chains' @@ -8,6 +9,8 @@ import { useBridgeState } from '@/slices/bridge/hooks' export const useFromChainListArray = (searchStr: string = '') => { const { fromChainIds } = useBridgeState() + const t = useTranslations('Bridge') + let possibleChains = _(ALL_CHAINS) .pickBy((value) => _.includes(fromChainIds, value.id)) .values() @@ -58,5 +61,8 @@ export const useFromChainListArray = (searchStr: string = '') => { ) } - return { 'From…': possibleChains, 'All chains': remainingChains } + return { + [t('FromWithEllipsis')]: possibleChains, + [t('All chains')]: remainingChains, + } } diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useFromTokenListArray.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useFromTokenListArray.ts index 917e96523e..8719062869 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useFromTokenListArray.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useFromTokenListArray.ts @@ -1,5 +1,6 @@ import _ from 'lodash' import Fuse from 'fuse.js' +import { useTranslations } from 'next-intl' import { usePortfolioBalances } from '@/slices/portfolio/hooks' import { useBridgeState } from '@/slices/bridge/hooks' @@ -11,6 +12,8 @@ export const useFromTokenListArray = (searchStr: string = '') => { const { fromTokens, fromChainId } = useBridgeState() const portfolioBalances = usePortfolioBalances() + const t = useTranslations('Bridge') + let possibleTokens = sortByPriorityRank(fromTokens) possibleTokens = [ @@ -101,8 +104,8 @@ export const useFromTokenListArray = (searchStr: string = '') => { } return { - 'Send…': possibleTokens, - 'All sendable tokens': remainingTokens, - 'All other tokens': allOtherFromTokens, + [t('SendWithEllipsis')]: possibleTokens, + [t('All sendable tokens')]: remainingTokens, + [t('All other tokens')]: allOtherFromTokens, } } diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useToChainListArray.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useToChainListArray.ts index faaa0a69cb..80b5192abb 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useToChainListArray.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useToChainListArray.ts @@ -1,5 +1,6 @@ import _ from 'lodash' import Fuse from 'fuse.js' +import { useTranslations } from 'next-intl' import * as ALL_CHAINS from '@/constants/chains/master' import { CHAINS_BY_ID, sortChains } from '@/constants/chains' @@ -7,6 +8,7 @@ import { useBridgeState } from '@/slices/bridge/hooks' export const useToChainListArray = (searchStr: string = '') => { const { toChainIds } = useBridgeState() + const t = useTranslations('Bridge') let possibleChains = _(ALL_CHAINS) .pickBy((value) => _.includes(toChainIds, value.id)) @@ -58,5 +60,10 @@ export const useToChainListArray = (searchStr: string = '') => { ) } + return { + [t('ToWithEllipsis')]: possibleChains, + [t('All chains')]: remainingChains, + } + return { 'To…': possibleChains, 'All chains': remainingChains } } diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useToTokenListArray.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useToTokenListArray.ts index 3f1c50bb03..f997f78238 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useToTokenListArray.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useToTokenListArray.ts @@ -1,5 +1,6 @@ import _ from 'lodash' import Fuse from 'fuse.js' +import { useTranslations } from 'next-intl' import { getRoutePossibilities } from '@/utils/routeMaker/generateRoutePossibilities' import { Token } from '@/utils/types' @@ -10,6 +11,8 @@ import { sortByPriorityRank } from '@/utils/sortByPriorityRank' export const useToTokenListArray = (searchStr: string = '') => { const { fromChainId, toTokens, toChainId }: BridgeState = useBridgeState() + const t = useTranslations('Bridge') + let possibleTokens: Token[] = sortByPriorityRank(toTokens) const { toTokens: allToChainTokens } = getRoutePossibilities({ @@ -84,8 +87,8 @@ export const useToTokenListArray = (searchStr: string = '') => { } return { - 'Receive…': possibleTokens, - 'All receivable tokens': remainingChainTokens, - 'All other tokens': allOtherToTokens, + [t('ReceiveWithEllipsis')]: possibleTokens, + [t('All receivable tokens')]: remainingChainTokens, + [t('All other tokens')]: allOtherToTokens, } } diff --git a/packages/synapse-interface/components/StateManagedSwap/SwapExchangeRateInfo.tsx b/packages/synapse-interface/components/StateManagedSwap/SwapExchangeRateInfo.tsx index a1da5f8744..49d94ce10a 100644 --- a/packages/synapse-interface/components/StateManagedSwap/SwapExchangeRateInfo.tsx +++ b/packages/synapse-interface/components/StateManagedSwap/SwapExchangeRateInfo.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react' import Image from 'next/image' +import { useTranslations } from 'next-intl' import { CHAINS_BY_ID } from '@constants/chains' import { Token } from '@/utils/types' @@ -67,11 +68,12 @@ const ExpectedPrice = ({ formattedExchangeRate, toToken, }) => { + const t = useTranslations('Swap') + return (
-

Expected Price on

- {expectedToChain} +

{t('Expected price on')}

{expectedToChain}
{safeFromAmount != 0n ? ( @@ -93,9 +95,10 @@ const Slippage = ({ textColor, formattedPercentSlippage, }) => { + const t = useTranslations('Swap') return (
-

Slippage

+

{t('Slippage')}

{safeFromAmount != 0n && !underFee ? ( {formattedPercentSlippage} ) : ( diff --git a/packages/synapse-interface/components/StateManagedSwap/SwapInputContainer.tsx b/packages/synapse-interface/components/StateManagedSwap/SwapInputContainer.tsx index 79f2d4b339..f647006074 100644 --- a/packages/synapse-interface/components/StateManagedSwap/SwapInputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedSwap/SwapInputContainer.tsx @@ -1,6 +1,9 @@ import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react' import { useDispatch } from 'react-redux' import { useAccount } from 'wagmi' +import { debounce } from 'lodash' +import { useTranslations } from 'next-intl' + import { TokenSelector } from '@/components/ui/TokenSelector' import { formatBigIntToString, stringToBigInt } from '@/utils/bigint/format' import { cleanNumberInput } from '@/utils/cleanNumberInput' @@ -30,7 +33,6 @@ import { trimTrailingZeroesAfterDecimal } from '@/utils/trimTrailingZeroesAfterD import { formatAmount } from '@/utils/formatAmount' import { getParsedBalance } from '@/utils/getParsedBalance' import { useWalletState } from '@/slices/wallet/hooks' -import { debounce } from 'lodash' interface InputContainerProps { setIsTyping: React.Dispatch> @@ -70,6 +72,8 @@ export const SwapInputContainer: React.FC = ({ const isInputMax = parsedBalance === swapFromValue + const t = useTranslations('Swap') + useEffect(() => { if ( swapFromToken && @@ -166,7 +170,7 @@ export const SwapInputContainer: React.FC = ({ className={joinClassNames(labelClassNames)} > - Available:{' '} + {t('available')}:{' '} {formattedBalance ?? '0.0'} @@ -186,12 +190,14 @@ const SwapChainSelector = () => { const { swapChainId } = useSwapState() const { isWalletPending } = useWalletState() + const t = useTranslations('Bridge') + return ( { const { swapFromToken } = useSwapState() const { isWalletPending } = useWalletState() + const t = useTranslations('Bridge') + return ( 0) { buttonProperties = { - label: `Connect Wallet to Swap`, + label: t('Connect Wallet to Swap'), onClick: openConnectModal, } } else if (isConnected && !sufficientBalance) { buttonProperties = { - label: 'Insufficient balance', + label: t('Insufficient balance'), onClick: null, } } else if (chain?.id != swapChainId && fromValueBigInt > 0) { buttonProperties = { - label: `Switch to ${chains.find((c) => c.id === swapChainId).name}`, + label: `${t('Switch to')} ${ + chains.find((c) => c.id === swapChainId).name + }`, onClick: () => switchChain({ chainId: swapChainId }), - pendingLabel: 'Switching chains', + pendingLabel: t('Switching chains'), } } else if ( !isApproved && @@ -118,14 +123,14 @@ export const SwapTransactionButton = ({ ) { buttonProperties = { onClick: approveTxn, - label: `Approve ${swapFromToken?.symbol}`, - pendingLabel: 'Approving', + label: `${t('Approve')} ${swapFromToken?.symbol}`, + pendingLabel: t('Approving'), } } else { buttonProperties = { onClick: executeSwap, - label: `Swap ${swapFromToken?.symbol} for ${swapToToken?.symbol}`, - pendingLabel: 'Swapping', + label: `${t('Swap')} ${swapFromToken?.symbol} for ${swapToToken?.symbol}`, + pendingLabel: t('Swapping'), } } diff --git a/packages/synapse-interface/components/Wallet.tsx b/packages/synapse-interface/components/Wallet.tsx index bd6426881d..9262f427e7 100644 --- a/packages/synapse-interface/components/Wallet.tsx +++ b/packages/synapse-interface/components/Wallet.tsx @@ -1,6 +1,8 @@ import { useMemo, useState, useEffect } from 'react' import { ConnectButton } from '@rainbow-me/rainbowkit' import { useAccount } from 'wagmi' +import { useTranslations } from 'next-intl' + import { MetamaskIcon } from '@icons/WalletIcons/Metamask' import { CoinbaseWalletIcon } from '@icons/WalletIcons/CoinbaseWalletIcon' import { WalletConnectIcon } from '@icons/WalletIcons/WalletConnectIcon' @@ -40,6 +42,8 @@ export const Wallet = () => { const [mounted, setMounted] = useState(false) + const t = useTranslations('Wallet') + useEffect(() => { setMounted(true) }, []) @@ -80,7 +84,7 @@ export const Wallet = () => { whitespace-nowrap `} > - Connect Wallet + {t('Connect Wallet')} ) } @@ -96,7 +100,7 @@ export const Wallet = () => { whitespace-nowrap `} > - Wrong Network + {t('Wrong Network')} ) } @@ -160,12 +164,7 @@ export const Wallet = () => { }} ) - }, [connectedAddress, currentChain, walletId]) + }, [connectedAddress, currentChain, walletId, t]) return mounted && render } - -function FormattedDisplayName(displayName: string) { - const [, hex] = displayName.split('0x') - return '0x' + hex -} diff --git a/packages/synapse-interface/components/Warning.tsx b/packages/synapse-interface/components/Warning.tsx index 1feaa40ca4..ab6e511b6b 100644 --- a/packages/synapse-interface/components/Warning.tsx +++ b/packages/synapse-interface/components/Warning.tsx @@ -1,4 +1,6 @@ -import { ARBITRUM, DOGE, FANTOM, HARMONY } from '@/constants/chains/master' +import { useTranslations } from 'next-intl' + +import { DOGE, FANTOM, HARMONY } from '@/constants/chains/master' import { useBridgeState } from '@/slices/bridge/hooks' export const Warning = () => { @@ -8,15 +10,18 @@ export const Warning = () => { const isChainFantom = [fromChainId, toChainId].includes(FANTOM.id) const isChainDoge = [fromChainId, toChainId].includes(DOGE.id) + const t = useTranslations('Warning') + if (isChainHarmony) { return (

- Do not bridge via Harmony unless you understand the risks - involved. + {t( + 'Do not bridge via Harmony unless you understand the risks involved' + )}

} @@ -25,11 +30,13 @@ export const Warning = () => { } else if (isChainFantom) { return (

- Do not bridge via Fantom unless you understand the risks involved. + {t( + 'Do not bridge via Fantom unless you understand the risks involved' + )}

} @@ -38,12 +45,13 @@ export const Warning = () => { } else if (isChainDoge) { return (

- You may still bridge funds from Dogechain to any supported - destination chain. + {t( + 'You may still bridge funds from Dogechain to any supported destination chain' + )}

} diff --git a/packages/synapse-interface/components/WipWrapperComponents/Footer.tsx b/packages/synapse-interface/components/WipWrapperComponents/Footer.tsx deleted file mode 100644 index 8d25ea84c2..0000000000 --- a/packages/synapse-interface/components/WipWrapperComponents/Footer.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { SynapseAnchor } from './SynapseLogo' - -const sections = [ - { - header: 'Functions', - links: [ - { label: 'Swap', url: '#' }, - { label: 'Bridge', url: '#' }, - { label: 'Pools', url: '#' }, - { label: 'Stake', url: '#' }, - ], - }, - { - header: 'Developers', - links: [ - { label: 'Build on Synapse', url: '#' }, - { label: 'Documentation', url: '#' }, - { label: 'GitHub', url: '#' }, - { label: 'Blog', url: '#' }, - ], - }, - { - header: 'Support', - links: [ - { label: 'Discord', url: '#' }, - { label: 'Twitter', url: '#' }, - { label: 'Forum', url: '#' }, - { label: 'Telegram', url: '#' }, - ], - }, -] - -export default function Footer() { - return ( -
- -
- {sections.map((section) => ( -
-
{section.header}
- -
- ))} -
-
- ) -} diff --git a/packages/synapse-interface/components/WipWrapperComponents/NavMenu.tsx b/packages/synapse-interface/components/WipWrapperComponents/NavMenu.tsx deleted file mode 100644 index 61f1f3f9b3..0000000000 --- a/packages/synapse-interface/components/WipWrapperComponents/NavMenu.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { Fragment } from 'react' - -const sections = [ - { - label: 'About', - url: '#', - links: [ - { - label: 'Vision', - url: '#', - description: - 'Vision lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Philosophy', - url: '#', - description: - 'Philosophy lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Roadmap', - url: '#', - description: - 'Roadmap lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - ], - }, - { - label: 'Bridge', - url: '#', - links: [ - { - label: 'Synapse Bridge', - url: '#', - description: - 'Smart routes & real-time competitive quotes on 20 supported chains.', - }, - { - label: 'On-chain swap', - url: '#', - description: - 'Swap lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Solana bridge', - url: '#', - description: - 'Solana lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - ], - }, - { - label: 'Community', - url: '#', - links: [ - { - label: 'Discord', - url: '#', - description: - 'Discord lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Telegram', - url: '#', - description: - 'Telegram lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Twitter', - url: '#', - description: - 'Twitter lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Blog', - url: '#', - description: - 'Blog lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Forum', - url: '#', - description: - 'Forum lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - ], - }, - { - label: 'Developers', - url: '#', - links: [ - { - label: 'Docs', - url: '#', - description: - 'Docs lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'GitHub', - url: '#', - description: - 'GitHub lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Synapse CNS', - url: '#', - description: - 'Synapse CNS lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - { - label: 'Interchain Network', - url: '#', - description: - 'Interchain Network lorem ipsum dolor sit amet consecteteur adipisicing elit.', - }, - ], - }, - { - label: 'Explorer', - url: '#', - }, -] - -export default function Header() { - return ( -
    - {sections.map((section) => ( -
  • - - {section.label} - - {section.links && ( -
    -
    - {section.links.map((link) => { - return ( - -
    - - {link.label} - -
    -
    -
    {link.label}
    -

    - {link.description} -

    -
    -
    - ) - })} -
    -
    - )} -
  • - ))} -
- ) -} diff --git a/packages/synapse-interface/components/WipWrapperComponents/SynapseLogo.tsx b/packages/synapse-interface/components/WipWrapperComponents/SynapseLogo.tsx deleted file mode 100644 index 1678c31ba8..0000000000 --- a/packages/synapse-interface/components/WipWrapperComponents/SynapseLogo.tsx +++ /dev/null @@ -1,44 +0,0 @@ -export default function SynapseIcon({ width, height }) { - return ( - - - - - - - - - - - - - ) -} - -export function SynapseAnchor() { - return ( - - - Synapse - - ) -} diff --git a/packages/synapse-interface/components/WipWrapperComponents/Ticker.tsx b/packages/synapse-interface/components/WipWrapperComponents/Ticker.tsx deleted file mode 100644 index 5c33cdc76c..0000000000 --- a/packages/synapse-interface/components/WipWrapperComponents/Ticker.tsx +++ /dev/null @@ -1,286 +0,0 @@ -/* TODO - * Add play/pause state - * Toggle play/paused from 'Live'