Skip to content
16 changes: 15 additions & 1 deletion web/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,25 @@ declare global {
const path: string;
export default path;
}
interface Window {
google: {
translate: {
TranslateElement: new (
config: {
pageLanguage: string;
includedLanguages: string;
},
elementId: string
) => void;
};
};
googleTranslateElementInit: () => void;
}
}

declare module "styled-components" {
type Theme = typeof lightTheme;
//eslint-disable-next-line @typescript-eslint/no-empty-interface

export interface DefaultTheme extends Theme {}
}

Expand Down
125 changes: 64 additions & 61 deletions web/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Loader from "components/Loader";
import Layout from "layout/index";

import ErrorFallback from "./components/ErrorFallback";
import TranslateProvider from "./context/TranslateProvider";
import { SentryRoutes } from "./utils/sentry";

const App: React.FC = () => {
Expand All @@ -37,67 +38,69 @@ const App: React.FC = () => {
<AtlasProvider>
<IsListProvider>
<NewDisputeProvider>
<SentryRoutes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Home />
</Suspense>
}
/>
<Route
path="cases/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Cases />
</Suspense>
}
/>
<Route
path="courts/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Courts />
</Suspense>
}
/>
<Route
path="dashboard/:page/:order/:filter"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Dashboard />
</Suspense>
}
/>
<Route
path="resolver/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<DisputeResolver />
</Suspense>
}
/>
<Route
path="get-pnk/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<GetPnk />
</Suspense>
}
/>
<Route
path="settings/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Settings />
</Suspense>
}
/>
<Route path="*" element={<h1>Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯</h1>} />
</Route>
</SentryRoutes>
<TranslateProvider>
<SentryRoutes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Home />
</Suspense>
}
/>
<Route
path="cases/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Cases />
</Suspense>
}
/>
<Route
path="courts/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Courts />
</Suspense>
}
/>
<Route
path="dashboard/:page/:order/:filter"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Dashboard />
</Suspense>
}
/>
<Route
path="resolver/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<DisputeResolver />
</Suspense>
}
/>
<Route
path="get-pnk/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<GetPnk />
</Suspense>
}
/>
<Route
path="settings/*"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Settings />
</Suspense>
}
/>
<Route path="*" element={<h1>Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯</h1>} />
</Route>
</SentryRoutes>
</TranslateProvider>
</NewDisputeProvider>
</IsListProvider>
</AtlasProvider>
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/ConnectWallet/AccountDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ export const AddressOrName: React.FC<IAddressOrName> = ({ address: propAddress }
chainId: 1,
});

return <label>{data ?? (isAddress(address!) ? shortenAddress(address) : address)}</label>;
return <label className="notranslate">{data ?? (isAddress(address!) ? shortenAddress(address) : address)}</label>;
};

export const ChainDisplay: React.FC = () => {
const chainId = useChainId();
const chain = getChain(chainId);
return <label>{chain?.name}</label>;
return <label className="notranslate">{chain?.name}</label>;
};

const AccountDisplay: React.FC = () => {
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/EvidenceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const EvidenceCard: React.FC<IEvidenceCard> = ({
<BottomShade>
<AccountContainer>
<Identicon size="24" string={sender} />
<StyledA href={addressExplorerLink} rel="noopener noreferrer" target="_blank">
<StyledA href={addressExplorerLink} rel="noopener noreferrer" target="_blank" className="notranslate">
<p>{shortenAddress(sender)}</p>
</StyledA>
</AccountContainer>
Expand Down
34 changes: 34 additions & 0 deletions web/src/components/TranslateDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";

import { DropdownSelect } from "@kleros/ui-components-library";

import { SupportedLangs, useTranslate } from "context/TranslateProvider";

const Langs: { value: SupportedLangs; text: string }[] = [
{ text: "English", value: "en" },
{ text: "Spanish", value: "es" },
{ text: "Hindi", value: "hi" },
{ text: "Japanese", value: "ja" },
{ text: "Korean", value: "ko" },
{ text: "French", value: "fr" },
];

const TranslateDropdown: React.FC = () => {
const { currentLang, setLang } = useTranslate();
return (
<DropdownSelect
smallButton
simpleButton
items={Langs.map((range) => ({
value: range.value,
text: range.text,
}))}
defaultValue={currentLang}
callback={(val) => {
setLang(val as SupportedLangs);
}}
/>
);
};

export default TranslateDropdown;
73 changes: 73 additions & 0 deletions web/src/context/TranslateProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo } from "react";

import { useLocalStorage } from "hooks/useLocalStorage";

export type SupportedLangs = "en" | "es" | "fr" | "hi" | "ko" | "ja";

interface ITranslate {
currentLang: SupportedLangs;
setLang: (lang: SupportedLangs) => void;
}
const TranslateContext = createContext<ITranslate | undefined>(undefined);

export const useTranslate = () => {
const context = useContext(TranslateContext);
if (!context) {
throw new Error("Context Provider not found.");
}
return context;
};

export const TranslateProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [currentLang, setCurrentLang] = useLocalStorage<SupportedLangs>("lang", "en");

useEffect(() => {
try {
const existingScript = document.querySelector('script[src*="translate.google.com/translate_a/element.js"]');
if (!existingScript) {
const addScript = document.createElement("script");
addScript.setAttribute("src", "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit");
document.body.appendChild(addScript);

window.googleTranslateElementInit = () => {
new window.google.translate.TranslateElement(
{
pageLanguage: "en",
includedLanguages: "en,es,hi,ja,fr,ko",
},
"google_translate_element"
);
};
}
} catch (err) {
// eslint-disable-next-line no-console
console.log("Error injecting google translate script", err);
}
}, []);

const setLang = useCallback(
(cValue: SupportedLangs) => {
setCurrentLang(cValue);
document.cookie = "googtrans" + "=" + `/en/${cValue}` + ";" + "Session" + ";path=/";
if (window.google?.translate?.TranslateElement) {
const select = document.querySelector(".goog-te-combo") as HTMLSelectElement;
if (select) {
select.value = cValue;

select.dispatchEvent(new Event("change", { bubbles: true }));
select.click();
}
}
},
[setCurrentLang]
);

return (
<TranslateContext.Provider value={useMemo(() => ({ currentLang, setLang }), [currentLang, setLang])}>
<div id="google_translate_element"></div>
{children}
</TranslateContext.Provider>
);
};

export default TranslateProvider;
6 changes: 6 additions & 0 deletions web/src/layout/Header/navbar/Menu/Settings/General.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { Button } from "@kleros/ui-components-library";

import { AddressOrName, ChainDisplay, IdenticonOrAvatar } from "components/ConnectWallet/AccountDisplay";
import { EnsureChain } from "components/EnsureChain";
import TranslateDropdown from "components/TranslateDropdown";

const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 12px;
align-items: center;
`;

const StyledChainContainer = styled.div`
Expand Down Expand Up @@ -57,6 +59,9 @@ const StyledButton = styled.div`

const EnsureChainContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
justify-content: center;
padding: 16px;
`;
Expand Down Expand Up @@ -93,6 +98,7 @@ const General: React.FC = () => {

return (
<EnsureChainContainer>
<TranslateDropdown />
<EnsureChain>
<Container>
{address && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import styled, { css } from "styled-components";

import Identicon from "react-identicons";

import { Answer } from "context/NewDisputeContext";
import { DEFAULT_CHAIN, getChain } from "consts/chains";
import { Answer } from "context/NewDisputeContext";
import { getVoteChoice } from "utils/getVoteChoice";
import { isUndefined } from "utils/index";
import { shortenAddress } from "utils/shortenAddress";
Expand Down Expand Up @@ -94,7 +94,7 @@ const AccordionTitle: React.FC<{

return (
<TitleContainer>
<AddressContainer>
<AddressContainer className="notranslate">
<Identicon size="20" string={juror} />
<StyledA href={addressExplorerLink} rel="noopener noreferrer" target="_blank">
<StyledLabel variant="secondaryText">{shortenAddress(juror)}</StyledLabel>
Expand Down
12 changes: 12 additions & 0 deletions web/src/styles/global-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const GlobalStyle = createGlobalStyle`
body {
font-family: "Open Sans", sans-serif;
margin: 0px;
top:0px !important
}

html {
Expand Down Expand Up @@ -120,4 +121,15 @@ export const GlobalStyle = createGlobalStyle`
.hiddenCanvasElement{
display: none;
}

//hide-default-google-translate
.skiptranslate{
display: none !important ;
visibility: hidden !important;
};

font{
background-color: transparent !important;
box-shadow: none !important ;
}
`;
3 changes: 2 additions & 1 deletion web/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
"noImplicitAny": false,
"resolveJsonModule": true,
"lib": [
"ESNext.Array"
"ESNext.Array",
"DOM"
],
"types": [
"vite/client",
Expand Down
Loading