diff --git a/rollup.config.dev.js b/rollup.config.dev.js index da55098..45816b0 100644 --- a/rollup.config.dev.js +++ b/rollup.config.dev.js @@ -7,7 +7,7 @@ import resolve from '@rollup/plugin-node-resolve'; import typescript from '@rollup/plugin-typescript'; import peerDepsExternal from 'rollup-plugin-peer-deps-external'; -import pkg from './package.json' assert { type: 'json' }; +import pkg from './package.json' with { type: 'json' }; export default { input: 'src/index.ts', @@ -30,8 +30,8 @@ export default { { find: 'global', replacement: 'globalThis', - } - ] + }, + ], }), json(), peerDepsExternal(), @@ -53,9 +53,7 @@ export default { exclude: ['node_modules', 'motion'], }), ], - external: [ - ...Object.keys(pkg.peerDependencies || {}), - ], + external: [...Object.keys(pkg.peerDependencies || {})], watch: { clearScreen: false, include: 'src/**', diff --git a/rollup.config.prod.js b/rollup.config.prod.js index 034c63c..bc274c9 100644 --- a/rollup.config.prod.js +++ b/rollup.config.prod.js @@ -9,7 +9,7 @@ import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import tailwindcss from '@tailwindcss/postcss'; -import pkg from './package.json' assert { type: 'json' }; +import pkg from './package.json' with { type: 'json' }; export default { input: 'src/index.ts', @@ -33,8 +33,8 @@ export default { { find: 'global', replacement: 'globalThis', - } - ] + }, + ], }), json(), peerDepsExternal(), @@ -54,7 +54,13 @@ export default { commonjs(), postcss({ extract: false, - inject: true, + extensions: ['.css'], + inject: { + insertAt: 'top', + }, + config: { + path: './postcss.config.mjs', + }, minimize: true, plugins: [tailwindcss], }), @@ -63,7 +69,5 @@ export default { exclude: ['node_modules', 'motion'], }), ], - external: [ - ...Object.keys(pkg.peerDependencies || {}), - ], + external: [...Object.keys(pkg.peerDependencies || {})], }; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 7f68da4..697cdca 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -20,7 +20,7 @@ interface ButtonProps { } const buttonBase = - 'bluxcc:flex bluxcc:justify-center bluxcc:items-center bluxcc:px-[10px] bluxcc:transition-all bluxcc:w-full'; + 'bluxcc:flex bluxcc:justify-center bluxcc:items-center bluxcc:px-[10px] bluxcc:transition-all bluxcc:duration-300 bluxcc:w-full'; const sizeClasses: Record = { small: 'bluxcc:h-8 bluxcc:!text-sm bluxcc:gap-1', diff --git a/src/components/CardItem/index.tsx b/src/components/CardItem/index.tsx index 9000cb8..84f2a97 100644 --- a/src/components/CardItem/index.tsx +++ b/src/components/CardItem/index.tsx @@ -2,6 +2,7 @@ import React, { useState, MouseEvent } from 'react'; import { useProvider } from '../../context/provider'; import { ArrowRight } from '../../assets/Icons'; import hexToRgba from '../../utils/hexToRgba'; +import { useLang } from '../../hooks/useLang'; type CardItemProps = { variant?: 'social' | 'default' | 'input'; @@ -30,6 +31,7 @@ const CardItem = ({ }: CardItemProps) => { const context = useProvider(); const { appearance } = context.value.config; + const t = useLang(); const [inputValue, setInputValue] = useState(label || ''); const [isValid, setIsValid] = useState(false); @@ -70,7 +72,7 @@ const CardItem = ({ return (
setIsFocused(true)} @@ -133,7 +135,7 @@ const CardItem = ({ : '1px', }} > - Submit + {t('submit')}
@@ -150,7 +152,7 @@ const CardItem = ({ backgroundColor: `${hexToRgba(appearance.accent, 0.1)}`, }} > - Recent + {t('recent')} )} diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index f15597a..e1e69a4 100644 --- a/src/components/Input/index.tsx +++ b/src/components/Input/index.tsx @@ -99,7 +99,7 @@ const InputField = ({ )}
setIsFocused(true)} onBlur={() => setIsFocused(false)} onMouseEnter={onMouseEnter} diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index a7d35d3..8cfb832 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -87,7 +87,7 @@ const Modal = ({ onClick={(e) => e.target === e.currentTarget && handleClose(onClose)} >
= { +export const getModalContent = ( + lang: LanguageKey, +): Record => ({ [Routes.ONBOARDING]: { - title: 'Log in or Signup', + title: translate('logInOrSignUp', lang), Component: , }, [Routes.PROFILE]: { - title: 'Profile', + title: translate('profile', lang), Component: , }, [Routes.WAITING]: { @@ -35,24 +38,24 @@ export const modalContent: Record = { Component: , }, [Routes.SIGN_TRANSACTION]: { - title: 'Confirmation', + title: translate('confirmation', lang), Component: , }, [Routes.SEND]: { - title: 'Send', + title: translate('send', lang), Component: , }, [Routes.ACTIVITY]: { - title: 'Activity', + title: translate('activity', lang), Component: , }, [Routes.OTP]: { - title: '', + title: translate('enterConfirmationCodeTitle', lang), Component: , }, [Routes.WRONG_NETWORK]: { isSticky: true, - title: 'Wrong Network', + title: translate('wrongNetwork', lang), Component: , }, -}; +}); diff --git a/src/containers/BluxModal/index.tsx b/src/containers/BluxModal/index.tsx index 57d2ef4..e0b0d73 100644 --- a/src/containers/BluxModal/index.tsx +++ b/src/containers/BluxModal/index.tsx @@ -4,7 +4,8 @@ import Modal from '../../components/Modal'; import { useProvider } from '../../context/provider'; import { Routes } from '../../types'; -import { modalContent } from './content'; +import { getModalContent } from './content'; +import { LanguageKey } from '../../constants/locales'; interface BluxModalProps { isOpen: boolean; @@ -59,6 +60,7 @@ export default function BluxModal({ isOpen, closeModal }: BluxModalProps) { } } }; + const modalContent = getModalContent(value.config.lang as LanguageKey); const { title, Component, isSticky } = modalContent[route]; diff --git a/src/containers/Pages/Activity/index.tsx b/src/containers/Pages/Activity/index.tsx index 84f1b87..dd62a03 100644 --- a/src/containers/Pages/Activity/index.tsx +++ b/src/containers/Pages/Activity/index.tsx @@ -2,6 +2,7 @@ import { Horizon } from '@stellar/stellar-sdk'; import React, { useEffect, useState } from 'react'; import Button from '../../../components/Button'; +import { useLang } from '../../../hooks/useLang'; import { useTransactions } from '../../../useStellar'; import { useProvider } from '../../../context/provider'; import toTitleFormat from '../../../utils/toTitleFormat'; @@ -27,7 +28,7 @@ const Activity: React.FC = () => { }); const { value } = useProvider(); - + const t = useLang(); const userAddress = value.user.wallet?.address as string; const explorerUrl = getExplorerUrl( value.activeNetwork, @@ -60,12 +61,12 @@ const Activity: React.FC = () => { }; if (tx.operations.length > 1) { - details.title = 'Multi Operation'; + details.title = t('multiOperation'); } else if (op.type === 'payment') { - let title = 'Send'; + let title = t('send'); if (op.to.toLowerCase() === userAddress.toLowerCase()) { - title = 'Receive'; + title = t('receive'); } details.title = title; @@ -75,8 +76,11 @@ const Activity: React.FC = () => { Horizon.HorizonApi.OperationResponseType.pathPaymentStrictSend || op.type === Horizon.HorizonApi.OperationResponseType.pathPayment ) { - details.title = 'Swap'; - details.description = `Path payment of ${op.amount} ${handleAssetText(op)}`; + details.title = t('swap'); + details.description = t('pathPaymentDescription', { + amount: op.amount, + asset: handleAssetText(op), + }); } result.push(details); @@ -91,11 +95,11 @@ const Activity: React.FC = () => {
{loading ? (
- Loading activity... + {t('loadingActivity')}
) : isEmpty ? (
- No activity found + {t('noActivityFound')}
) : ( transactionsDetails.map((tx, index) => ( @@ -126,7 +130,7 @@ const Activity: React.FC = () => { size="medium" onClick={handleGoToExplorer} > - See all in explorer + {t('seeAllInExplorer')}
)} diff --git a/src/containers/Pages/ConfirmCode/index.tsx b/src/containers/Pages/ConfirmCode/index.tsx index fa2ffc9..25e9307 100644 --- a/src/containers/Pages/ConfirmCode/index.tsx +++ b/src/containers/Pages/ConfirmCode/index.tsx @@ -1,12 +1,14 @@ import React, { useState, useEffect } from 'react'; -import { EmailIcon } from '../../../assets/Icons'; +import { useLang } from '../../../hooks/useLang'; import Button from '../../../components/Button'; +import { EmailIcon } from '../../../assets/Icons'; import { useProvider } from '../../../context/provider'; import OTPInput from '../../../components/Input/OTPInput'; const ConfirmCode: React.FC = () => { const { value } = useProvider(); const appearance = value.config.appearance; + const t = useLang(); const email = value.user.email; const [otp, setOtp] = useState(Array(6).fill('')); @@ -36,7 +38,7 @@ const ConfirmCode: React.FC = () => { }, [otp, email]); return ( -
+
{

- Enter confirmation code + {t('enterConfirmationCodeTitle')}

{error ? (

- Invalid code, please try again. + {t('invalidCodeError')}

) : (

- Please check your email and enter confirmation code below + {t('enterConfirmationCodeHelp')}

)}
@@ -71,7 +73,7 @@ const ConfirmCode: React.FC = () => { {/* divider */}
{ color: appearance.accent, }} > - Resend code + {t('resendCode')}
); diff --git a/src/containers/Pages/OnBoarding/index.tsx b/src/containers/Pages/OnBoarding/index.tsx index 1392c8f..8632908 100644 --- a/src/containers/Pages/OnBoarding/index.tsx +++ b/src/containers/Pages/OnBoarding/index.tsx @@ -2,6 +2,7 @@ import React, { useState, useMemo } from 'react'; import CardItem from '../../../components/CardItem'; import handleLogos from '../../../utils/handleLogos'; +import { useLang } from '../../../hooks/useLang'; import { useProvider } from '../../../context/provider'; import { Routes, WalletInterface } from '../../../types'; import getContrastColor from '../../../utils/getContrastColor'; @@ -12,6 +13,7 @@ import isBackgroundDark from '../../../utils/isBackgroundDark'; const OnBoarding = () => { const { value, setValue, setRoute } = useProvider(); + const t = useLang(); const [inputValue, setInputValue] = useState(''); const wallets = value.availableWallets; @@ -88,7 +90,7 @@ const OnBoarding = () => { color: appearance.borderColor, }} > - or + {t('or')}
); @@ -143,7 +145,7 @@ const OnBoarding = () => { {hiddenWallets.length > 0 && !value.showAllWallets && ( { className="bluxcc:mt-6! bluxcc:flex bluxcc:h-4 bluxcc:cursor-pointer bluxcc:items-center bluxcc:justify-center bluxcc:text-sm bluxcc:leading-[28px] bluxcc:font-medium" style={{ color: appearance.accent }} > - Log in with Passkey + {t('logInWithPasskey')}{' '}
); } @@ -200,17 +202,17 @@ const OnBoarding = () => {
diff --git a/src/containers/Pages/Profile/index.tsx b/src/containers/Pages/Profile/index.tsx index a273621..ed61ebf 100644 --- a/src/containers/Pages/Profile/index.tsx +++ b/src/containers/Pages/Profile/index.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Routes } from '../../../types'; import copyText from '../../../utils/copyText'; +import { useLang } from '../../../hooks/useLang'; import { useBlux } from '../../../hooks/useBlux'; import CardItem from '../../../components/CardItem'; import { useProvider } from '../../../context/provider'; @@ -11,6 +12,7 @@ import { Copy, History, LogOut, Send } from '../../../assets/Icons'; const Profile = () => { const { logout } = useBlux(); + const t = useLang(); const context = useProvider(); const [copied, setCopied] = useState(false); @@ -30,13 +32,13 @@ const Profile = () => { setCopied(false); }, 1000); }) - .catch(() => { }); + .catch(() => {}); }; const balance = context.value.account.account ? context.value.account.account.balances.find( - (b) => b.asset_type === 'native', - )?.balance + (b) => b.asset_type === 'native', + )?.balance : '0'; return ( @@ -51,7 +53,7 @@ const Profile = () => { style={{ color: appearance.textColor }} > {copied ? ( - 'Copied!' + t('copied') ) : ( {address ? shortenAddress(address, 5) : ''} @@ -63,13 +65,13 @@ const Profile = () => { className="bluxcc:text-center bluxcc:text-base" style={{ color: appearance.accent }} > - {balance ? `${humanizeAmount(balance)} XLM` : 'Loading...'} + {balance ? `${humanizeAmount(balance)} XLM` : t('loading')}

} onClick={() => { context.setRoute(Routes.SEND); @@ -78,7 +80,7 @@ const Profile = () => { } onClick={() => { context.setRoute(Routes.ACTIVITY); @@ -104,7 +106,7 @@ const Profile = () => { className="bluxcc:flex bluxcc:h-12 bluxcc:w-full bluxcc:cursor-pointer bluxcc:items-center bluxcc:justify-center bluxcc:gap-2" > - Logout + {t('logout')}
); diff --git a/src/containers/Pages/SelectAsset/index.tsx b/src/containers/Pages/SelectAsset/index.tsx index 2f2de03..1232014 100644 --- a/src/containers/Pages/SelectAsset/index.tsx +++ b/src/containers/Pages/SelectAsset/index.tsx @@ -2,6 +2,7 @@ import React, { useState, MouseEvent, ChangeEvent } from 'react'; import { IAsset } from '../../../types'; import { Search } from '../../../assets/Icons'; +import { useLang } from '../../../hooks/useLang'; import { useProvider } from '../../../context/provider'; import humanizeAmount from '../../../utils/humanizeAmount'; @@ -17,6 +18,7 @@ const SelectAssets = ({ setShowSelectAssetPage, }: SelectAssetsProps) => { const context = useProvider(); + const t = useLang(); const appearance = context.value.config.appearance; const [isFocused, setIsFocused] = useState(false); const [searchQuery, setSearchQuery] = useState(''); @@ -46,7 +48,7 @@ const SelectAssets = ({ }; if (context.value.account.loading) { - return
Loading
; + return
{t('loading')}
; } return ( @@ -76,7 +78,7 @@ const SelectAssets = ({ ) => setSearchQuery(e.target.value) @@ -135,7 +137,7 @@ const SelectAssets = ({ style={{ color: appearance.textColor }} className="bluxcc:mt-2 bluxcc:text-center" > - No assets found + {t('noAssetsFound')}
)}
diff --git a/src/containers/Pages/Send/SendForm.tsx b/src/containers/Pages/Send/SendForm.tsx index ab5198f..b782767 100644 --- a/src/containers/Pages/Send/SendForm.tsx +++ b/src/containers/Pages/Send/SendForm.tsx @@ -11,6 +11,7 @@ import { StellarSmallLogo } from '../../../assets/logos'; import getContrastColor from '../../../utils/getContrastColor'; import paymentTransaction from '../../../utils/stellar/paymentTransaction'; import { StrKey } from '@stellar/stellar-sdk'; +import { useLang } from '../../../hooks/useLang'; type SendFormValues = { memo: string; @@ -19,6 +20,7 @@ type SendFormValues = { }; const SendForm = () => { + const t = useLang(); const { value, setValue } = useProvider(); const { sendTransaction } = useBlux(); const [errors, setErrors] = useState>({}); @@ -85,15 +87,15 @@ const SendForm = () => { const errorMessages: typeof errors = {}; if (!form.amount) { - errorMessages.amount = 'Amount is required'; + errorMessages.amount = t('amountRequired'); } else if (Number(form.amount) > Number(selectedAsset.balance)) { - errorMessages.amount = 'Amount is greater than max balance'; + errorMessages.amount = t('amountExceedsBalance'); } if (!form.address) { - errorMessages.address = 'Address is required'; + errorMessages.address = t('addressRequired'); } else if (!StrKey.isValidEd25519PublicKey(form.address)) { - errorMessages.address = 'Address is invalid'; + errorMessages.address = t('addressInvalid'); } setErrors(errorMessages); @@ -143,7 +145,7 @@ const SendForm = () => { { style={{ color: appearance.accent }} className="bluxcc:mr-2 bluxcc:inline-flex bluxcc:cursor-pointer" > - Max + {t('max')} } onButtonClick={handleOpenAssets} @@ -173,20 +175,20 @@ const SendForm = () => {
@@ -211,7 +213,7 @@ const SendForm = () => { state="enabled" onClick={handleSubmit} > - Send + {t('sendButton')}
diff --git a/src/containers/Pages/Send/index.tsx b/src/containers/Pages/Send/index.tsx index 949c38b..9657f4a 100644 --- a/src/containers/Pages/Send/index.tsx +++ b/src/containers/Pages/Send/index.tsx @@ -1,19 +1,20 @@ import React from 'react'; import { useProvider } from '../../../context/provider'; +import { useLang } from '../../../hooks/useLang'; import SendForm from './SendForm'; const Send = () => { const { value } = useProvider(); - + const t = useLang(); const { account, loading } = value.account; if (loading) { - return

Loading

; + return

{t('loading')}

; } if (!account) { - return

Account is inactive

; + return

{t('inactiveAccount')}

; } return ; diff --git a/src/containers/Pages/SignTransaction/index.tsx b/src/containers/Pages/SignTransaction/index.tsx index 64698c9..8dc56f7 100644 --- a/src/containers/Pages/SignTransaction/index.tsx +++ b/src/containers/Pages/SignTransaction/index.tsx @@ -10,8 +10,10 @@ import shortenAddress from '../../../utils/shortenAddress'; import Summary from '../../../components/Transaction/Summary'; import getActiveNetworkTitle from '../../../utils/network/getNetworkTitle'; import getTransactionDetails from '../../../utils/stellar/getTransactionDetails'; +import { useLang } from '../../../hooks/useLang'; const SignTransaction = () => { + const t = useLang(); const context = useProvider(); const { balance } = useBalance({ asset: 'native' }); @@ -33,7 +35,7 @@ const SignTransaction = () => { if (!txDetails) { return (
-

Invalid XDR

+

{t('invalidXdr')}

); } @@ -47,7 +49,7 @@ const SignTransaction = () => { {context.value.config.appName}{' '} - wants your permission to approve the following transaction. + {t('signTransactionPrompt')}

{ {isLobstr && (

- Ensure that your LOBSTR wallet is set to the {networkTitle} network. - Otherwise, the transaction will definitely fail. + {t('lobstrWarning', { network: networkTitle })}

)} @@ -76,7 +77,7 @@ const SignTransaction = () => { >

- Your wallet + {t('yourWallet')}

{ > {context.value.user.wallet?.address ? shortenAddress(context.value.user.wallet.address as string, 5) - : 'No address found'} + : t('noAddressFound')}

{ variant="fill" onClick={handleSignTx} > - Approve + {t('approve')}
); diff --git a/src/containers/Pages/Successful/index.tsx b/src/containers/Pages/Successful/index.tsx index 24a3fca..07dcf53 100644 --- a/src/containers/Pages/Successful/index.tsx +++ b/src/containers/Pages/Successful/index.tsx @@ -6,9 +6,11 @@ import { useProvider } from '../../../context/provider'; import getExplorerUrl from '../../../utils/stellar/getExplorerUrl'; import capitalizeFirstLetter from '../../../utils/capitalizeFirstLetter'; import { Horizon, rpc } from '@stellar/stellar-sdk'; +import { useLang } from '../../../hooks/useLang'; const Successful = () => { const context = useProvider(); + const t = useLang(); const waitingStatus = context.value.waitingStatus; const appearance = context.value.config.appearance; const { result, options } = context.value.signTransaction; @@ -71,20 +73,23 @@ const Successful = () => { }; return ( -
+

- {waitingStatus === 'connecting' ? 'Connection' : 'Transaction'}{' '} - Successful + {waitingStatus === 'connecting' + ? t('connectionSuccessfulTitle') + : t('transactionSuccessfulTitle')}

{waitingStatus === 'connecting' - ? `Your account has been successfully connected to ${capitalizeFirstLetter(context.value.config.appName)}` - : 'Your transaction was successfully completed'} + ? t('connectionSuccessfulMessage', { + appName: capitalizeFirstLetter(context.value.config.appName), + }) + : t('transactionSuccessfulMessage')}

{waitingStatus === 'signing' && @@ -97,14 +102,14 @@ const Successful = () => { className="mt-4" onClick={handleGoToExplorer} > - See in explorer + {t('seeInExplorer')} )} {/* divider */}
{ variant="outline" className="bluxcc:cursor-default!" > - Logging In + {t('loggingIn')} ) : ( )}
diff --git a/src/containers/Pages/Waiting/index.tsx b/src/containers/Pages/Waiting/index.tsx index 4bb8545..6e65dce 100644 --- a/src/containers/Pages/Waiting/index.tsx +++ b/src/containers/Pages/Waiting/index.tsx @@ -9,9 +9,11 @@ import isBackgroundDark from '../../../utils/isBackgroundDark'; import { Loading, RedExclamation } from '../../../assets/Icons'; import { setRecentConnectionMethod } from '../../../utils/setRecentConnectionMethod'; import handleTransactionSigning from '../../../utils/stellar/handleTransactionSigning'; +import { useLang } from '../../../hooks/useLang'; const Waiting = () => { const context = useProvider(); + const t = useLang(); const hasConnected = useRef(false); const [error, setError] = useState(false); const [matchedWallet, setMatchedWallet] = useState( @@ -107,7 +109,7 @@ const Waiting = () => { }; return ( -
+
{error ? (
{

{error - ? `${waitingStatus === 'connecting' - ? 'Login failed' - : `Signing with ${user?.wallet?.name} failed` - }` - : `${waitingStatus === 'connecting' ? 'Waiting for' : `Signing with` - } ${user?.wallet?.name}`} + ? waitingStatus === 'connecting' + ? t('loginFailed') + : t('signingFailed', { + walletName: user?.wallet?.name ?? 'wallet', + }) + : waitingStatus === 'connecting' + ? t('waitingFor', { walletName: user?.wallet?.name ?? 'wallet' }) + : t('signingWith', { + walletName: user?.wallet?.name ?? 'wallet', + })}

{error - ? `Please try ${waitingStatus === 'connecting' ? 'logging in' : 'signing'} again.` - : `${waitingStatus === 'connecting' - ? 'Accept connection' - : 'Sign the' - } request in your wallet`} + ? waitingStatus === 'connecting' + ? t('loginRetryMessage') + : t('signingRetryMessage') + : waitingStatus === 'connecting' + ? t('acceptConnection') + : t('signRequestInWallet')}

{/* divider */}
{ {error ? ( ) : ( )}
diff --git a/src/containers/Pages/WrongNetwork/index.tsx b/src/containers/Pages/WrongNetwork/index.tsx index c47fbda..69e8047 100644 --- a/src/containers/Pages/WrongNetwork/index.tsx +++ b/src/containers/Pages/WrongNetwork/index.tsx @@ -1,13 +1,13 @@ import React from 'react'; +import { useLang } from '../../../hooks/useLang'; const WrongNetwork = () => { + const t = useLang(); return (
-

You are on a wrong network.

+

{t('wrongNetworkMessage')}

- ) - + ); }; export default WrongNetwork; - diff --git a/src/context/provider.tsx b/src/context/provider.tsx index f219124..45386db 100644 --- a/src/context/provider.tsx +++ b/src/context/provider.tsx @@ -76,6 +76,7 @@ export const BluxProvider = ({ config: { ...config, explorer: config.explorer || 'stellarchain', + lang: config.lang, appearance: { ...defaultLightTheme, ...config.appearance, diff --git a/src/hooks/useLang.ts b/src/hooks/useLang.ts new file mode 100644 index 0000000..4c77d2e --- /dev/null +++ b/src/hooks/useLang.ts @@ -0,0 +1,11 @@ +import { translate } from '../utils/translate'; +import { useProvider } from '../context/provider'; +import { LanguageKey, TranslationKey } from '../constants/locales'; + +export const useLang = () => { + const { value } = useProvider(); + const lang = value.config.lang as LanguageKey; + + return (key: TranslationKey, vars?: Record) => + translate(key, lang, vars || {}); +}; diff --git a/src/tailwind.css b/src/tailwind.css index f4d3ba2..72800c6 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -1,4 +1,82 @@ -@import 'tailwindcss' prefix(bluxcc); +@layer theme, base, components, utilities; + +@import 'tailwindcss/theme.css' layer(theme) prefix(bluxcc); +@import 'tailwindcss/utilities.css' layer(utilities) prefix(bluxcc) important; + +@source "../node_modules/@bluxcc/react/dist/**/*.{js,jsx,ts,tsx}"; + +[class^='bluxcc'], +[class*=' bluxcc'] { + /* Target all prefixed Tailwind elements */ +} + +[class^='bluxcc'], +[class^='bluxcc']::before, +[class^='bluxcc']::after, +[class^='bluxcc']::backdrop, +[class^='bluxcc']::file-selector-button, +[class*=' bluxcc']::before, +[class*=' bluxcc']::after, +[class*=' bluxcc']::backdrop, +[class*=' bluxcc']::file-selector-button { + margin: 0; + padding: 0; + box-sizing: border-box; + border: 0 solid; +} + +[class^='bluxcc'] h1, +[class^='bluxcc'] h2, +[class^='bluxcc'] h3, +[class^='bluxcc'] h4, +[class^='bluxcc'] h5, +[class^='bluxcc'] h6, +[class*=' bluxcc'] h1, +[class*=' bluxcc'] h2, +[class*=' bluxcc'] h3, +[class*=' bluxcc'] h4, +[class*=' bluxcc'] h5, +[class*=' bluxcc'] h6 { + font-size: inherit; + font-weight: inherit; +} + +[class^='bluxcc'] ol, +[class^='bluxcc'] ul, +[class^='bluxcc'] menu, +[class*=' bluxcc'] ol, +[class*=' bluxcc'] ul, +[class*=' bluxcc'] menu { + list-style: none; +} + +[class^='bluxcc'] img, +[class^='bluxcc'] svg, +[class^='bluxcc'] video, +[class^='bluxcc'] canvas, +[class^='bluxcc'] audio, +[class^='bluxcc'] iframe, +[class^='bluxcc'] embed, +[class^='bluxcc'] object, +[class*=' bluxcc'] img, +[class*=' bluxcc'] svg, +[class*=' bluxcc'] video, +[class*=' bluxcc'] canvas, +[class*=' bluxcc'] audio, +[class*=' bluxcc'] iframe, +[class*=' bluxcc'] embed, +[class*=' bluxcc'] object { + display: block; + vertical-align: middle; +} + +[class^='bluxcc'] img, +[class^='bluxcc'] video, +[class*=' bluxcc'] img, +[class*=' bluxcc'] video { + max-width: 100%; + height: auto; +} @theme { --color-primary-50: #e8e8fd; diff --git a/src/types/index.ts b/src/types/index.ts index abb363e..743215c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,6 +3,7 @@ import { HorizonApi } from '@stellar/stellar-sdk/lib/horizon'; import { Url } from '../utils/network/url'; import { UseAccountResult } from '../useStellar/useAccount'; +import { LanguageKey } from '../constants/locales'; /** * Enum representing the supported wallets in the system. @@ -69,6 +70,7 @@ export interface IProviderConfig { appearance?: Partial; transports?: ITransports; explorer?: IExplorers; + lang?: LanguageKey; showWalletUIs?: boolean; loginMethods?: Array< | 'wallet' diff --git a/src/utils/translate.ts b/src/utils/translate.ts new file mode 100644 index 0000000..dc9e1ba --- /dev/null +++ b/src/utils/translate.ts @@ -0,0 +1,14 @@ +import translations, { LanguageKey } from '../constants/locales'; + +const interpolate = (template: string, vars: Record = {}) => { + return template.replace(/\$\{(\w+)\}/g, (_, key) => vars[key] || ''); +}; + +export const translate = ( + key: keyof typeof translations, + lang: LanguageKey, + vars: Record = {}, +): string => { + const template = translations[key]?.[lang] || translations[key]?.en || ''; + return interpolate(template, vars); +};