From ecaa760c833aeff15f4661a29d9f889b58ff90e2 Mon Sep 17 00:00:00 2001 From: NateWilliams2 Date: Tue, 14 May 2024 15:49:47 -0400 Subject: [PATCH 1/2] Use i18next-browser-languagedetector (fixes #170) --- i18n/index.ts | 45 +++++++++++++++++++++++++++++++-------------- package.json | 2 +- pages/_app.tsx | 8 +++++++- yarn.lock | 25 +++++++++++++++---------- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/i18n/index.ts b/i18n/index.ts index a5308ad6..6f3b6a7f 100644 --- a/i18n/index.ts +++ b/i18n/index.ts @@ -1,28 +1,45 @@ import i18next from 'i18next'; -import translation from './locales'; +import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; const i18n = i18next.createInstance(); export const supportedLanguages = ['ca', 'en', 'es']; -i18n.use(initReactI18next).init({ - debug: process.env.NODE_ENV === 'development', - preload: ['es'], - resources: { - translation, +const resources = { + en: { + translation: require('./locales/en.json'), }, - fallbackLng: 'es', - defaultNS: 'translation', - interpolation: { - escapeValue: false, + es: { + translation: require('./locales/es.json'), }, - returnEmptyString: false, -}); + ca: { + translation: require('./locales/ca.json'), + }, +}; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + debug: process.env.NODE_ENV === 'development', + preload: ['es'], + supportedLngs: supportedLanguages, + resources: resources, + fallbackLng: 'es', + defaultNS: 'translation', + interpolation: { + escapeValue: false, + }, + returnEmptyString: false, + detection: { + order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag', 'path', 'subdomain'], + }, + }); for (const lang of supportedLanguages) { - if (typeof translation[lang] !== 'undefined') { - i18n.addResourceBundle(lang, 'translation', translation[lang]); + if (typeof resources[lang] !== 'undefined') { + i18n.addResourceBundle(lang, 'translation', resources[lang]); } } diff --git a/package.json b/package.json index 5a34bd29..c882946a 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "framer-motion": "^10.0.1", "html-react-parser": "^1.4.5", "i18next": "^22.4.10", - "i18next-browser-languagedetector": "^7.1.0", + "i18next-browser-languagedetector": "^8.0.0", "next": "^13.1.4", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/pages/_app.tsx b/pages/_app.tsx index a8528dcb..39f90629 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import { NextComponentType, NextPageContext } from 'next'; import { AppInitialProps } from 'next/app'; @@ -28,6 +28,7 @@ const VocdoniApp: FC = ({ Component, pageProps }) => { const appDescr = process.env.APP_DESCRIPTION; const apptags = process.env.APP_TAGS; const commitSHA = process.env.COMMIT_SHA; + const [initialRenderComplete, setInitialRenderComplete] = useState(false); // If the current page component defined a custom layout, use it const Layout: FC = Component['Layout'] ? Component['Layout'] : DefaultLayout; @@ -36,6 +37,11 @@ const VocdoniApp: FC = ({ Component, pageProps }) => { console.log('these no has default providers'); } + useEffect(() => { + setInitialRenderComplete(true); + }, []); + + if (!initialRenderComplete) return <>; return ( diff --git a/yarn.lock b/yarn.lock index c9575687..4a20e0c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -281,12 +281,12 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@^7.19.4": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" - integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== +"@babel/runtime@^7.23.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c" + integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g== dependencies: - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" @@ -4323,12 +4323,12 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -i18next-browser-languagedetector@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.1.0.tgz#01876fac51f86b78975e79b48ccb62e2313a2d7d" - integrity sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA== +i18next-browser-languagedetector@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz#b6fdd9b43af67c47f2c26c9ba27710a1eaf31e2f" + integrity sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw== dependencies: - "@babel/runtime" "^7.19.4" + "@babel/runtime" "^7.23.2" i18next-parser@^7.7.0: version "7.7.0" @@ -6381,6 +6381,11 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" From 68facd5f702e2342bcc8b2bca1858af650b90f20 Mon Sep 17 00:00:00 2001 From: NateWilliams2 Date: Tue, 28 May 2024 16:22:46 -0400 Subject: [PATCH 2/2] I18n: Use next-i18nnext --- .eslintrc.js | 1 + components/blocks/RawContent.tsx | 6 +- components/blocks/copy-button.tsx | 7 +- components/blocks/error.tsx | 9 +- components/blocks/language-selector.tsx | 26 +-- components/blocks/loader.tsx | 26 +-- components/blocks/process_time_left.tsx | 17 +- components/blocks/question-results.tsx | 15 +- components/elements/inputs.tsx | 6 +- components/pages/app/footer.tsx | 15 +- components/pages/app/header.tsx | 20 +- .../app/page-templates/list-page-filtered.tsx | 7 +- .../app/page-templates/list-page-jump-to.tsx | 7 +- .../pages/blocks/components/BlockCard.tsx | 23 +- .../pages/blocks/components/BlockFilter.tsx | 7 +- .../details/TransactionListForBlock.tsx | 11 +- components/pages/blocks/details/index.tsx | 16 +- components/pages/blocks/list/index.tsx | 9 +- .../components/ElectionCensusOrigin-badge.tsx | 31 ++- .../components/ElectionEnvelopeExplorer.tsx | 7 +- .../elections/components/ElectionFilter.tsx | 19 +- .../elections/components/ElectionKeys.tsx | 11 +- .../components/ElectionProcessmodeBadge.tsx | 9 +- .../components/ElectionTypeBadge.tsx | 21 +- .../elections/components/ResultsCard.tsx | 6 +- components/pages/elections/details/index.tsx | 38 ++-- components/pages/elections/list/index.tsx | 8 +- .../envelopes/components/EnvelopeCard.tsx | 13 +- .../components/EnvelopeEncryptionKeys.tsx | 6 +- components/pages/envelopes/details/index.tsx | 29 ++- .../home/components/hero-banner/index.tsx | 26 +-- components/pages/not-found.tsx | 17 +- .../components/OrganizationCard.tsx | 12 +- .../components/OrganizationsFilter.tsx | 7 +- .../components/OrganizationsProcessBadge.tsx | 6 +- .../pages/organizations/details/index.tsx | 14 +- components/pages/organizations/list/index.tsx | 8 +- components/pages/stats/index.tsx | 29 ++- .../components/TransactionCard.tsx | 9 +- .../components/TransactionTypeBadge.tsx | 28 +-- .../components/TransactionsFilter.tsx | 7 +- .../pages/transactions/details/index.tsx | 24 +-- components/pages/transactions/list/index.tsx | 8 +- .../validators/components/ValidatorCard.tsx | 10 +- components/pages/validators/list/index.tsx | 9 +- components/pages/verify/index.tsx | 8 +- components/pages/verify/single-page.tsx | 2 +- hooks/use-voconi-sdk.tsx | 6 +- i18n/index.ts | 46 ---- i18n/locales/index.ts | 11 - i18next-parser.config.js | 4 +- lib/date.ts | 202 +++++++++--------- lib/validators/errors/invalid-choice-error.ts | 3 +- .../errors/invalid-description-error.ts | 3 +- .../errors/invalid-question-error.ts | 3 +- lib/validators/errors/invalid-title-error.ts | 3 +- next-i18next.config.js | 10 + next.config.js | 2 + package.json | 4 +- pages/404.tsx | 8 + pages/_app.tsx | 11 +- pages/blocks/index.tsx | 9 + pages/blocks/show.tsx | 16 +- pages/converter.tsx | 35 +-- pages/envelopes/show.tsx | 14 +- pages/index.tsx | 8 + pages/index/featured.tsx | 35 +-- pages/organizations/index.tsx | 10 +- pages/organizations/show.tsx | 15 +- pages/processes/index.tsx | 8 + pages/processes/show.tsx | 15 +- pages/stats/index.tsx | 8 + pages/transactions/index.tsx | 8 + pages/transactions/show.tsx | 15 +- pages/validators/index.tsx | 14 +- pages/verify/index.tsx | 8 + .../ca.json => public/locales/ca/common.json | 7 +- .../locales/ca/common_old.json | 0 .../en.json => public/locales/en/common.json | 7 +- .../locales/en/common_old.json | 0 .../eo.json => public/locales/eo/common.json | 0 .../es.json => public/locales/es/common.json | 7 +- .../locales/es/common_old.json | 0 yarn.lock | 53 +++-- 84 files changed, 655 insertions(+), 603 deletions(-) delete mode 100644 i18n/index.ts delete mode 100644 i18n/locales/index.ts create mode 100644 next-i18next.config.js rename i18n/locales/ca.json => public/locales/ca/common.json (98%) rename i18n/locales/ca_old.json => public/locales/ca/common_old.json (100%) rename i18n/locales/en.json => public/locales/en/common.json (98%) rename i18n/locales/en_old.json => public/locales/en/common_old.json (100%) rename i18n/locales/eo.json => public/locales/eo/common.json (100%) rename i18n/locales/es.json => public/locales/es/common.json (98%) rename i18n/locales/es_old.json => public/locales/es/common_old.json (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 66b976c0..5ca4c325 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { 'node_modules', 'next.config.js', 'i18next-parser.config.js', + 'next-i18next.config.js', 'env-config.*', '.*' ], rules: { diff --git a/components/blocks/RawContent.tsx b/components/blocks/RawContent.tsx index 32292c7b..0a9eb937 100644 --- a/components/blocks/RawContent.tsx +++ b/components/blocks/RawContent.tsx @@ -2,7 +2,7 @@ import { Card } from '@components/elements/cards'; import { OverflowScroll } from '@components/elements/styled-divs'; import { Button } from '@components/elements/button'; import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; interface IRawContentProps { content: unknown; @@ -19,8 +19,8 @@ export const RawContent = ({ content, title }: IRawContentProps) => { }; export const RawContentBtnLabel = () => { - const { i18n } = useTranslation(); - return <>{i18n.t('components.raw_content.label')}; + const { t } = useTranslation(); + return <>{t('components.raw_content.label')}; }; export const RawContentBtn = (props: IRawContentProps) => { diff --git a/components/blocks/copy-button.tsx b/components/blocks/copy-button.tsx index c275a011..d590a9ca 100644 --- a/components/blocks/copy-button.tsx +++ b/components/blocks/copy-button.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import copy from 'copy-to-clipboard'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { IoCopy } from 'react-icons/io5'; import { IconContext } from 'react-icons'; import styled from 'styled-components'; @@ -20,8 +20,7 @@ export const CopyButton = ({ color?: string; copyMessage?: string; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const { setCopiedMessage, message } = useCopiedMessage(); const handleCopy = (e) => { e.cancelBubble = true; @@ -29,7 +28,7 @@ export const CopyButton = ({ e.preventDefault(); e.target.focus(); copy(toCopy); - setCopiedMessage(copyMessage ?? i18n.t('copy.copied_to_the_clipboard')); + setCopiedMessage(copyMessage ?? t('copy.copied_to_the_clipboard')); }; return ( diff --git a/components/blocks/error.tsx b/components/blocks/error.tsx index d212ad24..578f4a66 100644 --- a/components/blocks/error.tsx +++ b/components/blocks/error.tsx @@ -1,7 +1,8 @@ -import i18n from '../../i18n'; +import { useTranslation } from 'next-i18next'; -const ErrorPage = (props: { message?: string }) => ( -
{(props && props.message) || i18n.t('errors.general_error')}
-); +const ErrorPage = (props: { message?: string }) => { + const { t } = useTranslation(); + return
{(props && props.message) || t('errors.general_error')}
; +}; export default ErrorPage; diff --git a/components/blocks/language-selector.tsx b/components/blocks/language-selector.tsx index 93923a15..e21eb082 100644 --- a/components/blocks/language-selector.tsx +++ b/components/blocks/language-selector.tsx @@ -1,8 +1,7 @@ -import { useTranslation } from 'react-i18next'; import { IconButton, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback } from 'react'; import { HiLanguage } from 'react-icons/hi2'; -import LanguageDetector from 'i18next-browser-languagedetector'; +import router from 'next/router'; type LanguageOption = { code: string; @@ -16,23 +15,10 @@ const languages: LanguageOption[] = [ ]; const LanguageSelector = ({ bg = 'transparent', size, color }: { bg?: string; size?: string; color?: string }) => { - const { i18n } = useTranslation(); - - const languageDetector = useMemo(() => new LanguageDetector(), []); - - useEffect(() => { - languageDetector.init(); - const lng = languageDetector.detect(); - i18n.changeLanguage(typeof lng === 'string' ? lng : lng[0]); - }, [i18n, languageDetector]); - - const handleLanguageChange = useCallback( - (languageCode: string) => { - i18n.changeLanguage(languageCode); - languageDetector.cacheUserLanguage(languageCode); - }, - [i18n, languageDetector] - ); + const handleLanguageChange = useCallback((languageCode: string) => { + const { pathname, query } = router; + router.push({ pathname, query }, router.asPath, { locale: languageCode }); + }, []); return ( diff --git a/components/blocks/loader.tsx b/components/blocks/loader.tsx index 77e0c979..232709e8 100644 --- a/components/blocks/loader.tsx +++ b/components/blocks/loader.tsx @@ -1,23 +1,25 @@ import React from 'react'; import styled from 'styled-components'; - -import i18n from '../../i18n'; +import { useTranslation } from 'next-i18next'; interface ILoaderProps { visible: boolean; } -export const Loader = ({ visible }: ILoaderProps) => ( - - - - Vocdoni Logo - +export const Loader = ({ visible }: ILoaderProps) => { + const { t } = useTranslation(); + return ( + + + + Vocdoni Logo + - {i18n.t('dashboard.loading')} - - -); + {t('dashboard.loading')} + + + ); +}; const LogoContainer = styled.div` width: 100px; diff --git a/components/blocks/process_time_left.tsx b/components/blocks/process_time_left.tsx index 32f61367..7c9e039d 100644 --- a/components/blocks/process_time_left.tsx +++ b/components/blocks/process_time_left.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { localizedDateDiff } from '@lib/date'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ItemDate } from '@components/elements/styled-divs'; import { ElectionStatus } from '@vocdoni/sdk'; @@ -13,34 +13,33 @@ export const ProcessTimeLeft = ({ startDate: Date; endDate: Date; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); let date: string; switch (status) { case ElectionStatus.ONGOING: { - date = localizedDateDiff(endDate); + date = localizedDateDiff(endDate, t); break; } case ElectionStatus.RESULTS: { if (new Date(endDate) < new Date()) { - date = i18n.t('dashboard.process_ended'); - } else date = localizedDateDiff(endDate); + date = t('dashboard.process_ended'); + } else date = localizedDateDiff(endDate, t); break; } case ElectionStatus.ENDED: - date = i18n.t('dashboard.process_ended'); + date = t('dashboard.process_ended'); break; case ElectionStatus.PAUSED: case ElectionStatus.UPCOMING: if (new Date(startDate) > new Date() && status === ElectionStatus.PAUSED) { - date = i18n.t('dashboard.process_paused'); + date = t('dashboard.process_paused'); break; } - date = localizedDateDiff(startDate); + date = localizedDateDiff(startDate, t); break; } diff --git a/components/blocks/question-results.tsx b/components/blocks/question-results.tsx index ee8aa56f..a8b93c04 100644 --- a/components/blocks/question-results.tsx +++ b/components/blocks/question-results.tsx @@ -2,7 +2,7 @@ import { Col, Row } from '@components/elements-v2/grid'; import { Spacer } from '@components/elements-v2/spacer'; import { Text } from '@components/elements-v2/text'; import { theme } from '@theme/global'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import styled from 'styled-components'; import { useIsMobile } from '@hooks/use-window-size'; import { BigNumber } from 'ethers'; @@ -29,8 +29,7 @@ type ChoiceResult = { }; export const QuestionResults = (props: QuestionsResultsProps) => { - const { i18n } = useTranslation(); - // const [sortedChoices, setSortedChoices] = useState([]); + const { t } = useTranslation(); // const [sortedChoices, setSortedChoices] = useState([]); // const [hasWinner, setHasWinner] = useState(false); const isMobile = useIsMobile(); // const [showResults, setSetShowResults] = useState(false); @@ -90,7 +89,7 @@ export const QuestionResults = (props: QuestionsResultsProps) => { - {i18n.t('vote.results_question', { index: props.index + 1 })} + {t('vote.results_question', { index: props.index + 1 })} @@ -134,7 +133,7 @@ export const QuestionResults = (props: QuestionsResultsProps) => { - {i18n.t('vote.vote_count', { + {t('vote.vote_count', { count: getResults(choice.votes, decimals).toString() as never, })} @@ -158,7 +157,7 @@ export const QuestionResults = (props: QuestionsResultsProps) => { - {i18n.t('vote.vote_count', { + {t('vote.vote_count', { count: getResults(choice.votes, decimals).toString() as any, })} @@ -172,8 +171,8 @@ export const QuestionResults = (props: QuestionsResultsProps) => { {status !== ElectionStatus.ENDED && !liveResults - ? i18n.t('vote.no_results_live') - : i18n.t('vote.loading_results')} + ? t('vote.no_results_live') + : t('vote.loading_results')} )} diff --git a/components/elements/inputs.tsx b/components/elements/inputs.tsx index ee6d65a0..e0d89492 100644 --- a/components/elements/inputs.tsx +++ b/components/elements/inputs.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import styled, { DefaultTheme, StyledComponentProps } from 'styled-components'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { FiSearch } from 'react-icons/fi'; export interface IInputProps { @@ -42,7 +42,7 @@ enum InputType { } export const InputPassword = (props: StyledComponentProps<'input', DefaultTheme, IInputProps, never>) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const [inputType, setInputType] = useState(InputType.Password); const handleClick = () => { @@ -55,7 +55,7 @@ export const InputPassword = (props: StyledComponentProps<'input', DefaultTheme, - {inputType === InputType.Password ? i18n.t('input.show') : i18n.t('input.hide')} + {inputType === InputType.Password ? t('input.show') : t('input.hide')} ); diff --git a/components/pages/app/footer.tsx b/components/pages/app/footer.tsx index 10022022..16f714e6 100644 --- a/components/pages/app/footer.tsx +++ b/components/pages/app/footer.tsx @@ -2,30 +2,29 @@ import React from 'react'; import styled, { useTheme } from 'styled-components'; import Link from 'next/link'; import { HOME_PATH } from '@const/routes'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; export const Footer = () => { const theme = useTheme(); - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const LINKS: HeaderLink[] = [ { url: 'https://blog.vocdoni.io/', - name: i18n.t('links.blog'), + name: t('links.blog'), external: true, logged: true, guest: true, }, { url: 'https://developer.vocdoni.io', - name: i18n.t('links.docs'), + name: t('links.docs'), external: true, logged: true, guest: true, }, { url: 'https://discord.gg/sM7UhAGY53', - name: i18n.t('links.help'), + name: t('links.help'), external: true, logged: true, guest: true, @@ -33,14 +32,14 @@ export const Footer = () => { { // url: ABOUT_PATH, url: 'https://vocdoni.io', - name: i18n.t('links.about'), + name: t('links.about'), external: false, logged: false, guest: true, }, { url: 'https://discord.gg/sQCxgYs', - name: i18n.t('links.support'), + name: t('links.support'), external: true, logged: true, guest: false, diff --git a/components/pages/app/header.tsx b/components/pages/app/header.tsx index be597057..84751163 100644 --- a/components/pages/app/header.tsx +++ b/components/pages/app/header.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import Link from 'next/link'; import { Unless } from 'react-if'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { HOME_PATH, @@ -33,7 +33,7 @@ interface IHeaderProps { export const Header = ({ children }: IHeaderProps) => { const isMobile = useIsMobile(); const [openMobileMenu, setOpenMobileMenu] = useState(false); - const { i18n } = useTranslation(); + const { t } = useTranslation(); const env = process.env.VOCDONI_ENVIRONMENT; let headerUrl; @@ -51,38 +51,38 @@ export const Header = ({ children }: IHeaderProps) => { const LINKS: HeaderLink[] = [ { - name: i18n.t('links.organizations'), + name: t('links.organizations'), url: ORGANIZATIONS_PATH, }, { - name: i18n.t('links.processes'), + name: t('links.processes'), url: PROCESSES_PATH, }, { - name: i18n.t('links.blocks'), + name: t('links.blocks'), url: BLOCKS_PATH, }, { - name: i18n.t('links.transactions'), + name: t('links.transactions'), url: TRANSACTIONS_PATH, }, { - name: i18n.t('links.validators'), + name: t('links.validators'), url: VALIDATORS_PATH, }, { - name: i18n.t('links.stats'), + name: t('links.stats'), url: STATS_PATH, }, { - name: i18n.t('links.tools'), + name: t('links.tools'), url: TOOLS_PATH, }, ]; const RIGHT_LINKS: HeaderLink[] = [ { - name: i18n.t('links.verify_vote'), + name: t('links.verify_vote'), url: VERIFY, }, ]; diff --git a/components/pages/app/page-templates/list-page-filtered.tsx b/components/pages/app/page-templates/list-page-filtered.tsx index 7be4adab..e96c5090 100644 --- a/components/pages/app/page-templates/list-page-filtered.tsx +++ b/components/pages/app/page-templates/list-page-filtered.tsx @@ -2,7 +2,7 @@ import { PaginatorRouterParams } from '@components/blocks/paginator-router-param import { FlexContainer, FlexAlignItem, FlexJustifyContent } from '@components/elements/flex'; import { Column, ListCardContainer } from '@components/elements/grid'; import { ReactNode, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { Else, If, Then } from 'react-if'; import { renderSkeleton } from './list-page'; @@ -28,8 +28,7 @@ export const FilteredPaginatedList = ({ currentPage, setCurrentPage, }: IPaginatedListTemplateProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const paginator = () => ( ({ -

{i18n.t('paginated_template.no_elements_found')}

+

{t('paginated_template.no_elements_found')}

diff --git a/components/pages/app/page-templates/list-page-jump-to.tsx b/components/pages/app/page-templates/list-page-jump-to.tsx index 79e482e8..75928fba 100644 --- a/components/pages/app/page-templates/list-page-jump-to.tsx +++ b/components/pages/app/page-templates/list-page-jump-to.tsx @@ -1,5 +1,5 @@ import { Column, ListCardContainer } from '@components/elements/grid'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ReactNode, useCallback, useEffect, useState } from 'react'; import { renderSkeleton } from './list-page'; import { FlexAlignItem, FlexContainer, FlexJustifyContent } from '@components/elements/flex'; @@ -31,8 +31,7 @@ export const JumpToPaginatedList = ({ currentPage, setCurrentPage, }: IPaginatedListTemplateProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const paginator = () => ( ({ -

{i18n.t('paginated_template.no_elements_found')}

+

{t('paginated_template.no_elements_found')}

diff --git a/components/pages/blocks/components/BlockCard.tsx b/components/pages/blocks/components/BlockCard.tsx index c0c0af47..99149034 100644 --- a/components/pages/blocks/components/BlockCard.tsx +++ b/components/pages/blocks/components/BlockCard.tsx @@ -1,6 +1,6 @@ import { getPath } from '@components/pages/app/components/get-links'; import { BLOCKS_DETAILS } from '@const/routes'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { localizedDateDiff } from '@lib/date'; import { CardItemTitle, GenericCardWrapper, GenericCardWrapperProps } from '@components/elements/card-generic'; import { ItemDate } from '@components/elements/styled-divs'; @@ -18,31 +18,34 @@ export const BlockCard = ({ blockTime: string; proposer: string; }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const link = blockHeight ? getPath(BLOCKS_DETAILS, { blockHeight: blockHeight.toString(), }) : null; - const Body = () => ( - - {'#' + blockHeight} - {localizedDateDiff(new Date(blockTime))} - - ); + const Body = () => { + const { t } = useTranslation(); + return ( + + {'#' + blockHeight} + {localizedDateDiff(new Date(blockTime), t)} + + ); + }; const Footer = () => { const p = ensure0x(proposer); return (
- {i18n.t('components.block_card.proposer')} {': '} + {t('components.block_card.proposer')} {': '}
); diff --git a/components/pages/blocks/components/BlockFilter.tsx b/components/pages/blocks/components/BlockFilter.tsx index fbbb38c5..1d96a54f 100644 --- a/components/pages/blocks/components/BlockFilter.tsx +++ b/components/pages/blocks/components/BlockFilter.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { InputSearch } from '@components/elements/inputs'; import { InlineFlex } from '@components/elements/flex'; import { DivWithMarginChildren } from '@components/elements/styled-divs'; @@ -12,8 +12,7 @@ export interface IFilterBlocks { } export const BlocksFilter = ({ setFilter }: { setFilter: (IFilterBlocks) => void }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [searchTermIT, setSearchTermIT] = useState(''); const [tempFilter, setTempFilter] = useState({}); @@ -40,7 +39,7 @@ export const BlocksFilter = ({ setFilter }: { setFilter: (IFilterBlocks) => void { if (ev.target.value.length === 0) { diff --git a/components/pages/blocks/details/TransactionListForBlock.tsx b/components/pages/blocks/details/TransactionListForBlock.tsx index c9978748..bedc06b2 100644 --- a/components/pages/blocks/details/TransactionListForBlock.tsx +++ b/components/pages/blocks/details/TransactionListForBlock.tsx @@ -1,6 +1,6 @@ import { TransactionTypeBadge } from '@components/pages/transactions/components/TransactionTypeBadge'; import { GenericListItemWithBadge } from '@components/blocks/list-items'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import React, { useState } from 'react'; import { getTransactionLink } from '@components/pages/app/components/get-links'; import { renderSkeleton } from '@components/pages/app/page-templates/list-page'; @@ -18,8 +18,7 @@ export const TransactionListForBlock = ({ blockHeight?: number; skeletonItems?: number; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [paginatorPage, setPaginatorPage] = useState(1); const { data, loading } = useBlockTransactions({ height: blockHeight, page: paginatorPage - 1 }); @@ -30,12 +29,12 @@ export const TransactionListForBlock = ({ return ( } lg={8} link={getTransactionLink(blockHeight, tx.transactionIndex)} > -

{i18n.t('transaction.card.block') + tx.transactionHash}

+

{t('transaction.card.block') + tx.transactionHash}

); }; @@ -56,7 +55,7 @@ export const TransactionListForBlock = ({ /> ) : ( -

{i18n.t('transactions.no_transactions_found')}

+

{t('transactions.no_transactions_found')}

)} ); diff --git a/components/pages/blocks/details/index.tsx b/components/pages/blocks/details/index.tsx index eebc8735..69979ec8 100644 --- a/components/pages/blocks/details/index.tsx +++ b/components/pages/blocks/details/index.tsx @@ -3,7 +3,7 @@ import { PageCard } from '@components/elements/cards'; import { Column, Grid } from '@components/elements/grid'; import { Typography, TypographyVariant } from '@components/elements/typography'; import { TransactionListForBlock } from './TransactionListForBlock'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import Link from 'next/link'; import { ensure0x, IChainBlockInfoResponse } from '@vocdoni/sdk'; import { getPath } from '@components/pages/app/components/get-links'; @@ -11,14 +11,14 @@ import { BLOCKS_DETAILS } from '@const/routes'; import { RawContentBtn } from '@components/blocks/RawContent'; export const BlockView = ({ blockData }: { blockData: IChainBlockInfoResponse }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const txs = blockData?.data.txs; const blockHeight = blockData?.header.height; return ( - {i18n.t('blocks.details.block_details')} + {t('blocks.details.block_details')} ) : null} <> -

{i18n.t('blocks.transactions', { transactions: txs.length })}

-

{i18n.t('blocks.details.hash', { hash: ensure0x(blockData?.['hash']) })}

+

{t('blocks.transactions', { transactions: txs.length })}

+

{t('blocks.details.hash', { hash: ensure0x(blockData?.['hash']) })}

- {i18n.t('blocks.details.last_block_hash')} + {t('blocks.details.last_block_hash')} {/*I used the link legacyBehavior here because for some reason the NextJS Link component changes the url but the useUrlHash hook doesn't detect the change, so it does not update to previous block data. Is a problem related to useUrlHash hook */} @@ -49,9 +49,9 @@ export const BlockView = ({ blockData }: { blockData: IChainBlockInfoResponse })

-

{i18n.t('blocks.details.proposer', { proposer: ensure0x(blockData?.header.proposerAddress) })}

+

{t('blocks.details.proposer', { proposer: ensure0x(blockData?.header.proposerAddress) })}

- +
); }; diff --git a/components/pages/blocks/list/index.tsx b/components/pages/blocks/list/index.tsx index 608fcfae..be82d26c 100644 --- a/components/pages/blocks/list/index.tsx +++ b/components/pages/blocks/list/index.tsx @@ -1,12 +1,11 @@ import { ListPage } from '@components/pages/app/page-templates/list-page'; import { DashboardBlockList } from './BlockList'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useBlockHeight } from '@hooks/use-voconi-sdk'; export const DashboardShowBlocks = () => { const { blockHeight } = useBlockHeight(); - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const page_size = 10; return ( @@ -15,8 +14,8 @@ export const DashboardShowBlocks = () => { pageSize={page_size} title={ } > diff --git a/components/pages/elections/components/ElectionCensusOrigin-badge.tsx b/components/pages/elections/components/ElectionCensusOrigin-badge.tsx index aedcde85..dd8f0cfc 100644 --- a/components/pages/elections/components/ElectionCensusOrigin-badge.tsx +++ b/components/pages/elections/components/ElectionCensusOrigin-badge.tsx @@ -1,6 +1,6 @@ import { CustomTag } from '@components/elements/CustomTag'; import { CensusTypeEnum } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { Case, Default, Switch } from 'react-if'; interface CensusOriginBadgeProps { @@ -8,50 +8,49 @@ interface CensusOriginBadgeProps { } export const CensusOriginBadge = ({ censusOrigin }: CensusOriginBadgeProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( - {i18n.t('processes.census_origin_badge.OFF_CHAIN')} + {t('processes.census_origin_badge.OFF_CHAIN')} - {i18n.t('processes.census_origin_badge.OFF_CHAIN_TREE_WEIGHTED')} + {t('processes.census_origin_badge.OFF_CHAIN_TREE_WEIGHTED')} - {i18n.t('processes.census_origin_badge.CENSUS_UNKNOWN')} + {t('processes.census_origin_badge.CENSUS_UNKNOWN')} {/**/} - {/* {i18n.t('processes.census_origin_badge.UNRECOGNIZED')}*/} + {/* {t('processes.census_origin_badge.UNRECOGNIZED')}*/} {/**/} - {i18n.t('processes.census_origin_badge.MINI_ME')} + {t('processes.census_origin_badge.MINI_ME')} - {i18n.t('processes.census_origin_badge.ERC1155')} + {t('processes.census_origin_badge.ERC1155')} - {i18n.t('processes.census_origin_badge.ERC20')} + {t('processes.census_origin_badge.ERC20')} - {i18n.t('processes.census_origin_badge.ERC721')} + {t('processes.census_origin_badge.ERC721')} - {i18n.t('processes.census_origin_badge.ERC777')} + {t('processes.census_origin_badge.ERC777')} {/* - {i18n.t('processes.OFF_CHAIN_CA')} + {t('processes.OFF_CHAIN_CA')} - {i18n.t('processes.OFF_CHAIN_TREE')} + {t('processes.OFF_CHAIN_TREE')} - {i18n.t('processes.OFF_CHAIN_TREE_WEIGHTED')} + {t('processes.OFF_CHAIN_TREE_WEIGHTED')} */} - {i18n.t('processes.census_origin_badge.CENSUS_UNKNOWN')} + {t('processes.census_origin_badge.CENSUS_UNKNOWN')} ); diff --git a/components/pages/elections/components/ElectionEnvelopeExplorer.tsx b/components/pages/elections/components/ElectionEnvelopeExplorer.tsx index 74ed7ddd..01ce423a 100644 --- a/components/pages/elections/components/ElectionEnvelopeExplorer.tsx +++ b/components/pages/elections/components/ElectionEnvelopeExplorer.tsx @@ -1,6 +1,6 @@ import { Card } from '@components/elements/cards'; import { Grid } from '@components/elements/grid'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import React, { useState } from 'react'; import { Paginator } from '@components/blocks/paginator'; import { Else, If, Then } from 'react-if'; @@ -14,8 +14,7 @@ interface EnvelopeExplorerProps { } export const EnvelopeExplorer = ({ electionId }: EnvelopeExplorerProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [paginatorPage, setPaginatorPage] = useState(1); const votePage = paginatorPage - 1; @@ -35,7 +34,7 @@ export const EnvelopeExplorer = ({ electionId }: EnvelopeExplorerProps) => { return (

- {i18n.t('processes.envelope_explorer.total_votes', { + {t('processes.envelope_explorer.total_votes', { totalVotes: envelopeCount?.count || 0, })}

diff --git a/components/pages/elections/components/ElectionFilter.tsx b/components/pages/elections/components/ElectionFilter.tsx index 87ce3bd9..162ce523 100644 --- a/components/pages/elections/components/ElectionFilter.tsx +++ b/components/pages/elections/components/ElectionFilter.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { Column, ColumnDiv, Grid } from '@components/elements/grid'; import { colors } from '@theme/colors'; import { InputSearch } from '@components/elements/inputs'; @@ -61,7 +61,7 @@ const CheckBoxAndSearchBar = ({ tempFilter: IFilterProcesses; setTempFilter: { (tempFilter: IFilterProcesses): void }; }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const [searchTerm, setSearchTerm] = useState(''); return ( @@ -74,14 +74,14 @@ const CheckBoxAndSearchBar = ({ tempFilter.withResults = ev.target.checked; setTempFilter(Object.assign({}, tempFilter)); }} - text={i18n.t('processes.filter.show_only_processes_with_results')} + text={t('processes.filter.show_only_processes_with_results')} labelColor={colors.text} /> { setSearchTerm(ev.target.value); @@ -112,23 +112,22 @@ const ProcessStatusSelector = ({ tempFilter: IFilterProcesses; setTempFilter: { (tempFilter: IFilterProcesses): void }; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const opts: IRadioOpts[] = [ { - label: i18n.t('processes.filter.status_selector.all'), + label: t('processes.filter.status_selector.all'), key: null, }, { - label: i18n.t('processes.filter.status_selector.active'), + label: t('processes.filter.status_selector.active'), key: ElectionStatusReady.READY, }, { - label: i18n.t('processes.filter.status_selector.paused'), + label: t('processes.filter.status_selector.paused'), key: ElectionStatus.PAUSED, }, { - label: i18n.t('processes.filter.status_selector.ended'), + label: t('processes.filter.status_selector.ended'), key: ElectionStatus.ENDED, }, ]; diff --git a/components/pages/elections/components/ElectionKeys.tsx b/components/pages/elections/components/ElectionKeys.tsx index 8656dc0b..4d8ea613 100644 --- a/components/pages/elections/components/ElectionKeys.tsx +++ b/components/pages/elections/components/ElectionKeys.tsx @@ -2,25 +2,24 @@ import { StatusCard } from '@components/elements/cards'; import { Grid } from '@components/elements/grid'; import { Typography, TypographyVariant } from '@components/elements/typography'; import { colors } from '@theme/colors'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useElectionKeys } from '@hooks/use-voconi-sdk'; export const EncryptionKeys = ({ electionId }: { electionId: string }) => { - const { i18n } = useTranslation(); - // todo(kon): implement encryption keys hook and interface properly + const { t } = useTranslation(); // todo(kon): implement encryption keys hook and interface properly const { loading, data: processKeys } = useElectionKeys({ electionId: electionId }); return ( <> {!loading && ( <> - {i18n.t('processes.details.election_encryption_keys')} + {t('processes.details.election_encryption_keys')} - +

{processKeys?.publicKeys?.length ?? 0}

- +

{processKeys?.privateKeys?.length ?? 0}

diff --git a/components/pages/elections/components/ElectionProcessmodeBadge.tsx b/components/pages/elections/components/ElectionProcessmodeBadge.tsx index 5591e876..b75b9473 100644 --- a/components/pages/elections/components/ElectionProcessmodeBadge.tsx +++ b/components/pages/elections/components/ElectionProcessmodeBadge.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { CustomTag } from '@components/elements/CustomTag'; interface ProcessModeBadgeProps { @@ -6,13 +6,10 @@ interface ProcessModeBadgeProps { } export const ProcessModeBadge = ({ autostart }: ProcessModeBadgeProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( - {autostart - ? i18n.t('processes.process_mode_badge.autostart') - : i18n.t('processes.process_mode_badge.notAutostart')} + {autostart ? t('processes.process_mode_badge.autostart') : t('processes.process_mode_badge.notAutostart')} ); }; diff --git a/components/pages/elections/components/ElectionTypeBadge.tsx b/components/pages/elections/components/ElectionTypeBadge.tsx index ae6fbcb1..380a662a 100644 --- a/components/pages/elections/components/ElectionTypeBadge.tsx +++ b/components/pages/elections/components/ElectionTypeBadge.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { CustomTag } from '@components/elements/CustomTag'; import React from 'react'; @@ -7,38 +7,37 @@ interface EnvelopeTypeBadgeProps { } export const ElectionTypeBadge = ({ encryptedVotes }: EnvelopeTypeBadgeProps) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( <> {encryptedVotes ? ( - {i18n.t('processes.envelope_type_badge.encrypted_votes')} + {t('processes.envelope_type_badge.encrypted_votes')} ) : ( - {i18n.t('processes.envelope_type_badge.not_encrypted_votes')} + {t('processes.envelope_type_badge.not_encrypted_votes')} )} ); }; export const AnonVoteBadge = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); return ( - {i18n.t('vote.badge.anonymous')} + {t('vote.badge.anonymous')} ); }; export const ArchivedBadge = () => { - const { i18n } = useTranslation(); - return {i18n.t('vote.badge.archived')}; + const { t } = useTranslation(); + return {t('vote.badge.archived')}; }; export const InvalidElectionBadge = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); return ( - {i18n.t('vote.badge.invalid')} + {t('vote.badge.invalid')} ); }; diff --git a/components/pages/elections/components/ResultsCard.tsx b/components/pages/elections/components/ResultsCard.tsx index 955ef802..c98e5d25 100644 --- a/components/pages/elections/components/ResultsCard.tsx +++ b/components/pages/elections/components/ResultsCard.tsx @@ -1,7 +1,7 @@ import { Else, If, Then } from 'react-if'; import { Col, Row } from '@components/elements-v2'; import { NoResultsCard } from '@components/blocks/NoResultsCard'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { QuestionResults } from '@components/blocks/question-results'; import useExtendedElection from '@hooks/use-extended-election'; @@ -9,7 +9,7 @@ export const ResultsCard = () => { const { election, results } = useExtendedElection(); const questions = election.questions; - const { i18n } = useTranslation(); + const { t } = useTranslation(); if (!questions) { return null; } @@ -35,7 +35,7 @@ export const ResultsCard = () => {
- + ); diff --git a/components/pages/elections/details/index.tsx b/components/pages/elections/details/index.tsx index c00313aa..89d81464 100644 --- a/components/pages/elections/details/index.tsx +++ b/components/pages/elections/details/index.tsx @@ -14,7 +14,7 @@ import { DateDiffType, localizedDateDiff, localizedStartEndDateDiff } from '@lib import { colors } from '@theme/colors'; import { ElectionDescription } from '@vocdoni/chakra-components'; import { CensusTypeEnum, ElectionStatus, InvalidElection } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import styled from 'styled-components'; import { CensusOriginBadge } from '../components/ElectionCensusOrigin-badge'; import { EnvelopeExplorer } from '../components/ElectionEnvelopeExplorer'; @@ -22,16 +22,16 @@ import { EncryptionKeys } from '../components/ElectionKeys'; import { ProcessModeBadge } from '../components/ElectionProcessmodeBadge'; import { AnonVoteBadge, ArchivedBadge, ElectionTypeBadge, InvalidElectionBadge } from '../components/ElectionTypeBadge'; import { ResultsCard } from '../components/ResultsCard'; +import { TFunction } from 'next-i18next'; const ProcessesDetailPage = () => { + const { t } = useTranslation(); const { election, electionRaw } = useExtendedElection(); - const dateDiffStr = resolveLocalizedDateDiff(election.startDate, election.endDate, election.status); + const dateDiffStr = resolveLocalizedDateDiff(election.startDate, election.endDate, election.status, t); const id = election.id; const organizationId = election.organizationId; const isInvalid = election instanceof InvalidElection; - const { i18n } = useTranslation(); - const defaultTab = election.status === ElectionStatus.ENDED || election.status === ElectionStatus.ONGOING ? 1 : 0; return ( @@ -42,7 +42,7 @@ const ProcessesDetailPage = () => { logo={} subtitle={ <> - + } /> @@ -50,7 +50,7 @@ const ProcessesDetailPage = () => { {/* Created on and ends on */} - {i18n.t('processes.details.process_details')} + {t('processes.details.process_details')} @@ -60,8 +60,8 @@ const ProcessesDetailPage = () => { )} - {i18n.t('processes.details.created_on')} - {localizedDateDiff(election.startDate)} + {t('processes.details.created_on')} + {localizedDateDiff(election.startDate, t)} {/* Labels and badges */} @@ -79,10 +79,10 @@ const ProcessesDetailPage = () => { {/* Three cards grid with various info */} - +

{election.voteCount || 0}

- +

{election.questions.length}

@@ -92,18 +92,18 @@ const ProcessesDetailPage = () => { {/* Technical details */} - {i18n.t('processes.details.detailed_data')} + {t('processes.details.detailed_data')} - {i18n.t('processes.details.processes_additional_information')} + {t('processes.details.processes_additional_information')} {/* Tabs */} - {i18n.t('processes.details.show_description')} - {i18n.t('processes.details.show_questions')} - {i18n.t('processes.details.show_envelopes')} + {t('processes.details.show_description')} + {t('processes.details.show_questions')} + {t('processes.details.show_envelopes')} @@ -121,7 +121,7 @@ const ProcessesDetailPage = () => { - + @@ -130,16 +130,16 @@ const ProcessesDetailPage = () => { }; // todo: move this somewhere -function resolveLocalizedDateDiff(initDate: Date, endDate: Date, voteStatus: ElectionStatus) { +function resolveLocalizedDateDiff(initDate: Date, endDate: Date, voteStatus: ElectionStatus, t: TFunction) { if ( initDate && (voteStatus == ElectionStatus.ONGOING || voteStatus == ElectionStatus.PAUSED || voteStatus == ElectionStatus.ENDED) ) { const now = new Date(); if (initDate > now) { - return localizedStartEndDateDiff(DateDiffType.Start, initDate); + return localizedStartEndDateDiff(DateDiffType.Start, initDate, t); } else { - return localizedStartEndDateDiff(DateDiffType.End, endDate); + return localizedStartEndDateDiff(DateDiffType.End, endDate, t); } } } diff --git a/components/pages/elections/list/index.tsx b/components/pages/elections/list/index.tsx index be1ce76a..c0fa09e0 100644 --- a/components/pages/elections/list/index.tsx +++ b/components/pages/elections/list/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { DashboardProcessList } from './ElectionsList'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ListPage } from '@components/pages/app/page-templates/list-page'; import { useElectionCount } from '@hooks/use-voconi-sdk'; export const DashboardShowProcesses = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { count } = useElectionCount(); const page_size = 10; @@ -15,8 +15,8 @@ export const DashboardShowProcesses = () => { pageSize={page_size} title={ } /> diff --git a/components/pages/envelopes/components/EnvelopeCard.tsx b/components/pages/envelopes/components/EnvelopeCard.tsx index 98e6cf98..4939bc7f 100644 --- a/components/pages/envelopes/components/EnvelopeCard.tsx +++ b/components/pages/envelopes/components/EnvelopeCard.tsx @@ -1,6 +1,6 @@ import { Card } from '@components/elements/cards'; import { BlockLink, EnvelopeLink, TransactionLink } from '@components/pages/app/components/get-links'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import React, { ReactNode } from 'react'; import { IconContext } from 'react-icons'; import { BiEnvelope } from 'react-icons/bi'; @@ -29,13 +29,12 @@ const EnvelopeCardSkeleton = ({ children }: { children: ReactNode }) => ( ); export const EnvelopeCard = ({ envelope, idx }: { envelope: IElectionVote; idx: number }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( - {i18n.t('processes.envelope_explorer.envelope_n', { + {t('processes.envelope_explorer.envelope_n', { number: idx, // Is not showing tx index, instead show index of map itself })} @@ -55,20 +54,20 @@ export const EnvelopeCard = ({ envelope, idx }: { envelope: IElectionVote; idx:

- {i18n.t('processes.envelope_explorer.block', { + {t('processes.envelope_explorer.block', { block: envelope.blockHeight || 0, })}

- {i18n.t('processes.envelope_explorer.tx_number', { + {t('processes.envelope_explorer.tx_number', { txNumber: envelope.transactionIndex || 0, })}

- {i18n.t('processes.envelope_explorer.details')} + {t('processes.envelope_explorer.details')}

); diff --git a/components/pages/envelopes/components/EnvelopeEncryptionKeys.tsx b/components/pages/envelopes/components/EnvelopeEncryptionKeys.tsx index 5f683d9c..8d474b62 100644 --- a/components/pages/envelopes/components/EnvelopeEncryptionKeys.tsx +++ b/components/pages/envelopes/components/EnvelopeEncryptionKeys.tsx @@ -1,16 +1,16 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { Case, Default, Switch } from 'react-if'; import { CustomTag } from '@components/elements/CustomTag'; export const EncryptionKeysIndexesBadge = ({ type }: { type: number }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); return ( 0}> {type} - {i18n.t('envelopes.encryption_keys.none')} + {t('envelopes.encryption_keys.none')} ); diff --git a/components/pages/envelopes/details/index.tsx b/components/pages/envelopes/details/index.tsx index 3557e798..28883a31 100644 --- a/components/pages/envelopes/details/index.tsx +++ b/components/pages/envelopes/details/index.tsx @@ -2,7 +2,7 @@ import { Card, PageCard } from '@components/elements/cards'; import { BadgeColumn, Column, Grid } from '@components/elements/grid'; import { Typography, TypographyVariant } from '@components/elements/typography'; import { BlockLink, ProcessLink, TransactionLink } from '@components/pages/app/components/get-links'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { colors } from '@theme/colors'; import { BreakWordAll, CenterText, ItemDate, OverflowScroll } from '@components/elements/styled-divs'; import { EncryptionKeysIndexesBadge } from '@components/pages/envelopes/components/EnvelopeEncryptionKeys'; @@ -14,8 +14,7 @@ import styled from 'styled-components'; import { IVoteInfoResponse } from '@vocdoni/sdk'; export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [showRawContent, setShowRawContent] = useState(false); const noLinks = process.env.VERIFY_SINGLE_PAGE; @@ -25,18 +24,18 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) = <> - {i18n.t('envelopes.details.envelope_details')} + {t('envelopes.details.envelope_details')} Vote registered -

{i18n.t('envelopes.details.vote_has_been_registered_correctly')}

+

{t('envelopes.details.vote_has_been_registered_correctly')}

- {i18n.t('envelopes.details.verifier_code')} + {t('envelopes.details.verifier_code')}

@@ -45,10 +44,10 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) =
- {i18n.t('envelopes.details.emitted')} {localizedDateDiff(new Date(envelope.date))} + {t('envelopes.details.emitted')} {localizedDateDiff(new Date(envelope.date), t)} - {i18n.t('envelopes.details.encryption_keys_used')}: + {t('envelopes.details.encryption_keys_used')}: {envelope.encryptionKeys?.length > 0 ? ( envelope.encryptionKeys.map((n) => { @@ -61,14 +60,14 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) = {envelope.overwriteCount > 0 && ( - {i18n.t('envelopes.details.overwrite_count')}: {envelope.overwriteCount} + {t('envelopes.details.overwrite_count')}: {envelope.overwriteCount} )} - {i18n.t('envelopes.details.envelope_weight')}: {envelope.weight} + {t('envelopes.details.envelope_weight')}: {envelope.weight} - {i18n.t('envelopes.details.commited_in_block')}: + {t('envelopes.details.commited_in_block')}: {noLinks ? ( '#' + envelope.blockHeight ) : ( @@ -76,7 +75,7 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) = )} - {i18n.t('envelopes.details.belongs_to_process')} + {t('envelopes.details.belongs_to_process')} {': '} {noLinks ? ( '0x' + envelope.electionID @@ -85,7 +84,7 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) = )} - {i18n.t('envelopes.details.transaction_hash')} + {t('envelopes.details.transaction_hash')} {': '} {noLinks ? ( '0x' + envelope.txHash @@ -101,11 +100,11 @@ export const EnvelopeDetails = ({ envelope }: { envelope: IVoteInfoResponse }) =
{showRawContent && ( -

{i18n.t('transactions.details.raw_contents')}

+

{t('transactions.details.raw_contents')}

{JSON.stringify(envelope, null, 2)}
)} diff --git a/components/pages/home/components/hero-banner/index.tsx b/components/pages/home/components/hero-banner/index.tsx index a4f048e0..b45ff2b5 100644 --- a/components/pages/home/components/hero-banner/index.tsx +++ b/components/pages/home/components/hero-banner/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { BaseParagraphTypography, TextAlign, Typography, TypographyVariant } from '@components/elements/typography'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { sizes } from 'theme/sizes'; import { Column, Grid } from '@components/elements/grid'; @@ -17,40 +17,36 @@ export const HeroBanner = (props: { averageBlockTime: number; envelopes: number; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( <>
- <strong>{i18n.t('home.vocdoni_explorer')}</strong> <br /> + <strong>{t('home.vocdoni_explorer')}</strong> <br /> - {i18n.t('home.the_most_flexible_and_secure_voting_protocol')} + {t('home.the_most_flexible_and_secure_voting_protocol')} - +
diff --git a/components/pages/not-found.tsx b/components/pages/not-found.tsx index 52a64d8d..30482674 100644 --- a/components/pages/not-found.tsx +++ b/components/pages/not-found.tsx @@ -1,10 +1,13 @@ -import i18n from '../../i18n'; +import { useTranslation } from 'next-i18next'; -const NotFound = () => ( -
-

Vocdoni

-

{i18n.t('errors.page_not_found')}

-
-); +const NotFound = () => { + const { t } = useTranslation(); + return ( +
+

Vocdoni

+

{t('errors.page_not_found')}

+
+ ); +}; export default NotFound; diff --git a/components/pages/organizations/components/OrganizationCard.tsx b/components/pages/organizations/components/OrganizationCard.tsx index 5f8a188b..5233da1f 100644 --- a/components/pages/organizations/components/OrganizationCard.tsx +++ b/components/pages/organizations/components/OrganizationCard.tsx @@ -8,7 +8,7 @@ import { theme } from '@theme/global'; import { OrganizationName } from '@vocdoni/chakra-components'; import { useOrganization } from '@vocdoni/react-providers'; import { ensure0x } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import styled from 'styled-components'; export const ReducedOrganizationNameWithIcon = ({ organizationId }: { organizationId: string }) => { @@ -37,10 +37,10 @@ type OrganizationCardMediumProps = { // Wrap a entityId into a link to its entity page and an icon. export const OrganizationCardMedium = ({ organizationId, md }: OrganizationCardMediumProps) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); return ( @@ -68,7 +68,7 @@ export const OrganizationCard = ({ organizationId?: string; electionCount?: number; }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { organization } = useOrganization(); const link = getOrganizationPath(organizationId); @@ -85,11 +85,11 @@ export const OrganizationCard = ({ - {i18n.t('organizations.list.processes')}: {electionIndex} + {t('organizations.list.processes')}: {electionIndex} ); diff --git a/components/pages/organizations/components/OrganizationsFilter.tsx b/components/pages/organizations/components/OrganizationsFilter.tsx index 9015b058..ce8e8502 100644 --- a/components/pages/organizations/components/OrganizationsFilter.tsx +++ b/components/pages/organizations/components/OrganizationsFilter.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { InputSearch } from '@components/elements/inputs'; import { InlineFlex } from '@components/elements/flex'; import { DivWithMarginChildren } from '@components/elements/styled-divs'; @@ -12,8 +12,7 @@ export interface IFilterEntity { } export const OrganizationsFilter = ({ onEnableFilter }: { onEnableFilter: { (tempFilter: IFilterEntity): void } }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [searchTermIT, setSearchTermIT] = useState(''); const [tempFilter, setTempFilter] = useState({}); @@ -35,7 +34,7 @@ export const OrganizationsFilter = ({ onEnableFilter }: { onEnableFilter: { (tem { if (ev.target.value.length === 0) { diff --git a/components/pages/organizations/components/OrganizationsProcessBadge.tsx b/components/pages/organizations/components/OrganizationsProcessBadge.tsx index a37a51ff..d8388bfa 100644 --- a/components/pages/organizations/components/OrganizationsProcessBadge.tsx +++ b/components/pages/organizations/components/OrganizationsProcessBadge.tsx @@ -1,10 +1,10 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { UpcomingBadge, ActiveBadge } from '@components/elements/text-badge'; import { Switch, Case, Default } from 'react-if'; export const NProcessesBadge = ({ processes }: { processes: number }) => { - const { i18n } = useTranslation(); - const text = i18n.t('organizations.upcoming_processes_badge.processes'); + const { t } = useTranslation(); + const text = t('organizations.upcoming_processes_badge.processes'); return ( diff --git a/components/pages/organizations/details/index.tsx b/components/pages/organizations/details/index.tsx index 01d5061c..07c0fa32 100644 --- a/components/pages/organizations/details/index.tsx +++ b/components/pages/organizations/details/index.tsx @@ -14,14 +14,14 @@ import { colors } from '@theme/colors'; import { OrganizationDescription } from '@vocdoni/chakra-components'; import { useOrganization } from '@vocdoni/react-providers'; import { ensure0x } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { When } from 'react-if'; import styled from 'styled-components'; export const OrganizationView = ({ id }: { id: string }) => { const plazaUrl = `${process.env.PLAZA_URL}/organization/${ensure0x(id)}`; - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { organization } = useOrganization(); const orgName = organization?.account?.name?.default || id; @@ -35,7 +35,7 @@ export const OrganizationView = ({ id }: { id: string }) => { - {i18n.t('organizations.details.organization_description')} + {t('organizations.details.organization_description')} @@ -44,13 +44,11 @@ export const OrganizationView = ({ id }: { id: string }) => { - - {i18n.t('organizations.details.organization_address')}{' '} - + {t('organizations.details.organization_address')} - ({i18n.t('organization.home.view_profile')}) + ({t('organization.home.view_profile')}) @@ -58,7 +56,7 @@ export const OrganizationView = ({ id }: { id: string }) => { 0}> - + ); }; diff --git a/components/pages/organizations/list/index.tsx b/components/pages/organizations/list/index.tsx index 827b8315..c8d269c7 100644 --- a/components/pages/organizations/list/index.tsx +++ b/components/pages/organizations/list/index.tsx @@ -1,10 +1,10 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ListPage } from '@components/pages/app/page-templates/list-page'; import { DashboardEntityList } from './OrganizationList'; import { useOrganizationCount } from '@hooks/use-voconi-sdk'; export const DashboardShowEntities = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { data: entitiesCount } = useOrganizationCount(); const count = entitiesCount?.count ?? 0; @@ -16,8 +16,8 @@ export const DashboardShowEntities = () => { pageSize={page_size} title={ } /> diff --git a/components/pages/stats/index.tsx b/components/pages/stats/index.tsx index cb662efe..bfb2aac6 100644 --- a/components/pages/stats/index.tsx +++ b/components/pages/stats/index.tsx @@ -6,7 +6,7 @@ import { Grid } from '@components/elements/grid'; import { Card } from '@components/elements/cards'; import { localizedDateDiff } from '@lib/date'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { BlockContainer, Section } from '@components/elements/styled-divs'; import { BlockCard } from '@components/pages/blocks/components/BlockCard'; import { capitalize } from '@lib/util'; @@ -21,7 +21,7 @@ import { When } from 'react-if'; const BLOCK_LIST_SIZE = 4; const BlockList = ({ blockHeight }: { blockHeight: number }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const firstBlock = blockHeight - (BLOCK_LIST_SIZE - 1); const { data: blocks } = useBlockList({ @@ -42,21 +42,20 @@ const BlockList = ({ blockHeight }: { blockHeight: number }) => { /> )) ) : ( -

{i18n.t('stats.getting_block_info')}

+

{t('stats.getting_block_info')}

)} ); }; const StatsPage = ({ stats }: { stats: IChainGetInfoResponse }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); let blockHeight: number; if (stats && stats?.height) { blockHeight = stats.height; } - const syncing = stats?.syncing ? i18n.t('stats.syncing') : i18n.t('stats.in_sync'); + const syncing = stats?.syncing ? t('stats.syncing') : t('stats.in_sync'); return (
@@ -65,27 +64,27 @@ const StatsPage = ({ stats }: { stats: IChainGetInfoResponse }) => { - }> + }> - {i18n.t('stats.view_all_blocks')} + {t('stats.view_all_blocks')} - }> + }> - {capitalize(stats?.chainId)} - {stats?.height} - {stats?.validatorCount} - {syncing} - - {localizedDateDiff(new Date(stats?.genesisTime))} + {capitalize(stats?.chainId)} + {stats?.height} + {stats?.validatorCount} + {syncing} + + {localizedDateDiff(new Date(stats?.genesisTime), t)} diff --git a/components/pages/transactions/components/TransactionCard.tsx b/components/pages/transactions/components/TransactionCard.tsx index e4a1d414..4283b391 100644 --- a/components/pages/transactions/components/TransactionCard.tsx +++ b/components/pages/transactions/components/TransactionCard.tsx @@ -1,6 +1,6 @@ import { getPath } from '@components/pages/app/components/get-links'; import { TRANSACTIONS_DETAILS } from '@const/routes'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { TransactionTypeBadge } from './TransactionTypeBadge'; import { CardItemTitle, GenericCardWrapper, GenericCardWrapperProps } from '@components/elements/card-generic'; import styled from 'styled-components'; @@ -12,8 +12,7 @@ export const TransactionCard = ({ }: GenericCardWrapperProps & { tx: IChainTxReference; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const link = getPath(TRANSACTIONS_DETAILS, { blockHeight: tx?.blockHeight?.toString(), index: tx?.transactionIndex?.toString() ?? '0', @@ -30,9 +29,9 @@ export const TransactionCard = ({ return (
- {i18n.t('components.transaction_card.hash')} {': '} + {t('components.transaction_card.hash')} {': '}
- +
); }; diff --git a/components/pages/transactions/components/TransactionTypeBadge.tsx b/components/pages/transactions/components/TransactionTypeBadge.tsx index 1efba5b6..789809bc 100644 --- a/components/pages/transactions/components/TransactionTypeBadge.tsx +++ b/components/pages/transactions/components/TransactionTypeBadge.tsx @@ -1,33 +1,33 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ActiveBadge, UpcomingBadge } from '@components/elements/text-badge'; import { TransactionType } from '@vocdoni/sdk'; export const TransactionTypeBadge = ({ type }: { type: TransactionType }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); switch (type) { case TransactionType.VOTE_ENVELOPE: - return {i18n.t('transactions.badge.vote')}; + return {t('transactions.badge.vote')}; case TransactionType.ADMIN_TX: - return {i18n.t('transactions.badge.admin')}; + return {t('transactions.badge.admin')}; case TransactionType.NEW_PROCESS_TX: - return {i18n.t('transactions.badge.new_process')}; + return {t('transactions.badge.new_process')}; case TransactionType.SET_PROCESS_TX: - return {i18n.t('transactions.badge.set_process')}; + return {t('transactions.badge.set_process')}; case TransactionType.REGISTER_KEY_TX: - return {i18n.t('transactions.badge.register_key')}; + return {t('transactions.badge.register_key')}; case TransactionType.MINT_TOKENS_TX: - return {i18n.t('transactions.badge.mint_tokens')}; + return {t('transactions.badge.mint_tokens')}; case TransactionType.SEND_TOKENS_TX: - return {i18n.t('transactions.badge.send_tokens')} ; + return {t('transactions.badge.send_tokens')} ; case TransactionType.SET_TRANSACTION_COSTS_TX: - return {i18n.t('transactions.badge.set_transaction_costs')} ; + return {t('transactions.badge.set_transaction_costs')} ; case TransactionType.SET_ACCOUNT_TX: - return {i18n.t('transactions.badge.set_account_info')} ; + return {t('transactions.badge.set_account_info')} ; case TransactionType.COLLECT_FAUCET_TX: - return {i18n.t('transactions.badge.collect_faucet')}; + return {t('transactions.badge.collect_faucet')}; case TransactionType.SET_KEYKEEPER_TX: - return {i18n.t('transactions.badge.set_key_keeper')}; + return {t('transactions.badge.set_key_keeper')}; default: - return {i18n.t('transactions.badge.unknown')}; + return {t('transactions.badge.unknown')}; } }; diff --git a/components/pages/transactions/components/TransactionsFilter.tsx b/components/pages/transactions/components/TransactionsFilter.tsx index d33894e0..1ae3e44b 100644 --- a/components/pages/transactions/components/TransactionsFilter.tsx +++ b/components/pages/transactions/components/TransactionsFilter.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { InputSearch } from '@components/elements/inputs'; import { InlineFlex } from '@components/elements/flex'; import { DivWithMarginChildren } from '@components/elements/styled-divs'; @@ -12,8 +12,7 @@ export interface IFilterTransactions { } export const TransactionsFilter = ({ setFilter }: { setFilter: (IFilterTransactions) => void }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const [searchTermIT, setSearchTermIT] = useState(''); const [tempFilter, setTempFilter] = useState({}); @@ -40,7 +39,7 @@ export const TransactionsFilter = ({ setFilter }: { setFilter: (IFilterTransacti { if (ev.target.value.length === 0) { diff --git a/components/pages/transactions/details/index.tsx b/components/pages/transactions/details/index.tsx index db10bcac..a6f4196d 100644 --- a/components/pages/transactions/details/index.tsx +++ b/components/pages/transactions/details/index.tsx @@ -4,7 +4,7 @@ import { PageCard } from '@components/elements/cards'; import { Column, Grid } from '@components/elements/grid'; import { Typography, TypographyVariant } from '@components/elements/typography'; import { BlockLink, EntityLink, ProcessLink } from '@components/pages/app/components/get-links'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { localizedDateDiff } from '@lib/date'; import { b64ToHex, objectB64StringsToHex } from '@lib/util'; import { colors } from '@theme/colors'; @@ -22,7 +22,7 @@ export const TransactionDetails = ({ transactionData: Tx; blockHeight: number; }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { data } = useBlockToDate({ height: blockHeight }); const date = new Date(data?.date); @@ -80,22 +80,22 @@ export const TransactionDetails = ({ <> - {i18n.t('transactions.details.transaction_details')} + {t('transactions.details.transaction_details')}

- {i18n.t('transactions.on_block_n', { + {t('transactions.on_block_n', { blockHeight: blockHeight, })}

- {i18n.t('transactions.transaction_index')}: {txIndex + 1}{' '} + {t('transactions.transaction_index')}: {txIndex + 1}{' '} - {i18n.t('transactions.created_on')}: - {localizedDateDiff(date)} + {t('transactions.created_on')}: + {localizedDateDiff(date, t)}
@@ -106,31 +106,31 @@ export const TransactionDetails = ({ } - dateText={localizedDateDiff(date)} + dateText={localizedDateDiff(date, t)} title={ensure0x(transactionData.txInfo.transactionHash)} > {belongsToProcess?.length > 0 && (

- {i18n.t('transactions.details.belongs_to_process')}:{' '} + {t('transactions.details.belongs_to_process')}:{' '} {ensure0x(belongsToProcess)}

)} {belongsToEntity?.length > 0 && (

- {i18n.t('transactions.details.belong_to_organization')}: + {t('transactions.details.belong_to_organization')}: 0x{belongsToEntity}

)} {votePackage?.length > 0 && (

- {i18n.t('transactions.details.vote_package')}:

{votePackage}
+ {t('transactions.details.vote_package')}:
{votePackage}

)}
- {rawTx && } + {rawTx && } ); diff --git a/components/pages/transactions/list/index.tsx b/components/pages/transactions/list/index.tsx index 75d1a1e9..ef609f6b 100644 --- a/components/pages/transactions/list/index.tsx +++ b/components/pages/transactions/list/index.tsx @@ -1,19 +1,17 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { ListPage } from '@components/pages/app/page-templates/list-page'; import { DashboardTransactionsList } from './TransactionList'; import { useTransactionCount } from '@hooks/use-voconi-sdk'; export const DashboardShowTransactions = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { transactionCount } = useTransactionCount(); const count = transactionCount === undefined ? '0' : transactionCount.toString(); return ( - } + title={} > ); }; diff --git a/components/pages/validators/components/ValidatorCard.tsx b/components/pages/validators/components/ValidatorCard.tsx index c3036c3f..5c8aab91 100644 --- a/components/pages/validators/components/ValidatorCard.tsx +++ b/components/pages/validators/components/ValidatorCard.tsx @@ -3,7 +3,7 @@ import { BreakWord } from '@components/elements/styled-divs'; import { StrongAndText } from '@components/elements/text'; import { useIsMobile } from '@hooks/use-window-size'; import { theme } from '@theme/global'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import styled from 'styled-components'; import { CopyButton } from '../../../blocks/copy-button'; import { IChainValidator } from '@vocdoni/sdk'; @@ -14,14 +14,14 @@ export const ValidatorCard = ({ }: GenericCardWrapperProps & { validatorData: IChainValidator; }) => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const isMobile = useIsMobile(); const pubKey = validatorData.pubKey; const Footer = () => { return ( - + {validatorData.name && ( - +
{validatorData.name}
)} - +
{validatorData.power}
diff --git a/components/pages/validators/list/index.tsx b/components/pages/validators/list/index.tsx index 3711b451..887145cb 100644 --- a/components/pages/validators/list/index.tsx +++ b/components/pages/validators/list/index.tsx @@ -1,18 +1,17 @@ -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { InlineTitleChildrenContainer, ListPage } from '@components/pages/app/page-templates/list-page'; import { ValidatorCard } from '@components/pages/validators/components/ValidatorCard'; import { IChainValidatorsListResponse } from '@vocdoni/sdk'; export const DashboardShowValidators = ({ validators }: IChainValidatorsListResponse) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( <> } > diff --git a/components/pages/verify/index.tsx b/components/pages/verify/index.tsx index 7bee29ff..12a94b89 100644 --- a/components/pages/verify/index.tsx +++ b/components/pages/verify/index.tsx @@ -22,7 +22,7 @@ const VerifyPage = ({ minified = false, onSubmit }: { minified?: boolean; onSubm const voteIdInput = ( { setEtVoteId(ev.target.value); @@ -34,13 +34,13 @@ const VerifyPage = ({ minified = false, onSubmit }: { minified?: boolean; onSubm const title = ( - {i18n.t('verify.verify_your_vote')} + {t('verify.verify_your_vote')} ); const VerifyButton = () => ( ); @@ -75,7 +75,7 @@ const VerifyPage = ({ minified = false, onSubmit }: { minified?: boolean; onSubm {logo} {title} - {i18n.t('verify.enter_the_voting_receipt_you_received_after_voting_to_verify_your_vote')} + {t('verify.enter_the_voting_receipt_you_received_after_voting_to_verify_your_vote')} {voteIdInput} diff --git a/components/pages/verify/single-page.tsx b/components/pages/verify/single-page.tsx index fd192816..a51590e2 100644 --- a/components/pages/verify/single-page.tsx +++ b/components/pages/verify/single-page.tsx @@ -28,7 +28,7 @@ const VerifySinglePage = ({ voteId }: { voteId: string }) => { -

{i18n.t('envelopes.details.envelope_not_found')}

+

{t('envelopes.details.envelope_not_found')}

diff --git a/hooks/use-voconi-sdk.tsx b/hooks/use-voconi-sdk.tsx index 63e3e8c4..3d98aaed 100644 --- a/hooks/use-voconi-sdk.tsx +++ b/hooks/use-voconi-sdk.tsx @@ -1,9 +1,9 @@ -import i18n from '@i18n'; import { ExtendedSDKClient } from '@lib/client'; import { useClient } from '@vocdoni/react-providers'; import { IChainGetInfoResponse, IElectionListFilter } from '@vocdoni/sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useAlertMessage } from './message-alert'; +import { useTranslation } from 'next-i18next'; type PromiseReturnType = T extends Promise ? U : never; @@ -24,7 +24,7 @@ function useSDKFunction({ const [loading, setLoading] = useState(false); const [loaded, setLoaded] = useState(false); // True after first load attempt const { setAlertMessage } = useAlertMessage(); - + const { t } = useTranslation(); // Use useMemo to memoize the arguments and recompute only when they change // eslint-disable-next-line react-hooks/exhaustive-deps const memorizedArgs = useMemo(() => args, args); @@ -38,7 +38,7 @@ function useSDKFunction({ }) .catch((err) => { setError(err); - setAlertMessage(i18n.t('error.could_not_fetch_the_details')); + setAlertMessage(t('error.could_not_fetch_the_details')); }) .finally(() => { setLoading(false); diff --git a/i18n/index.ts b/i18n/index.ts deleted file mode 100644 index 6f3b6a7f..00000000 --- a/i18n/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import i18next from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; -import { initReactI18next } from 'react-i18next'; - -const i18n = i18next.createInstance(); - -export const supportedLanguages = ['ca', 'en', 'es']; - -const resources = { - en: { - translation: require('./locales/en.json'), - }, - es: { - translation: require('./locales/es.json'), - }, - ca: { - translation: require('./locales/ca.json'), - }, -}; - -i18n - .use(LanguageDetector) - .use(initReactI18next) - .init({ - debug: process.env.NODE_ENV === 'development', - preload: ['es'], - supportedLngs: supportedLanguages, - resources: resources, - fallbackLng: 'es', - defaultNS: 'translation', - interpolation: { - escapeValue: false, - }, - returnEmptyString: false, - detection: { - order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag', 'path', 'subdomain'], - }, - }); - -for (const lang of supportedLanguages) { - if (typeof resources[lang] !== 'undefined') { - i18n.addResourceBundle(lang, 'translation', resources[lang]); - } -} - -export default i18n; diff --git a/i18n/locales/index.ts b/i18n/locales/index.ts deleted file mode 100644 index 543cdb19..00000000 --- a/i18n/locales/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import ca from './ca.json'; -import en from './en.json'; -import eo from './eo.json'; -import es from './es.json'; - -export default { - ca, - en, - eo, - es, -}; diff --git a/i18next-parser.config.js b/i18next-parser.config.js index 3541df2f..8fd065ec 100644 --- a/i18next-parser.config.js +++ b/i18next-parser.config.js @@ -48,8 +48,8 @@ module.exports = { // Namespace separator used in your translation keys // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. - output: 'i18n/locales/$LOCALE.json', - // output: 'i18n/locales/$LOCALE/$NAMESPACE.json', + output: 'public/locales/$LOCALE/common.json', + // output: 'public/locales/$LOCALE/$NAMESPACE.json', input: [ 'lib/**/*.{ts,tsx}', diff --git a/lib/date.ts b/lib/date.ts index ebb3a348..e9cbe23f 100644 --- a/lib/date.ts +++ b/lib/date.ts @@ -1,21 +1,21 @@ -import i18n from '../i18n'; +import { TFunction } from 'next-i18next'; export enum DateDiffType { Start = 'start-date', End = 'end-date', } -export function localizedStrDateDiff(type: DateDiffType, target: Date): string { +export function localizedStrDateDiff(type: DateDiffType, target: Date, t: TFunction): string { if (!target) return ''; const diff = (target.getTime() - Date.now()) / 1000; - if (diff > 3) return strDiffFutureV2(type, diff); - else if (diff < -3) return strDiffPastV2(type, -diff); - else if (type == DateDiffType.Start) return i18n.t('dates.starting_right_now'); - return i18n.t('dates.ending_right_now'); + if (diff > 3) return strDiffFutureV2(type, diff, t); + else if (diff < -3) return strDiffPastV2(type, -diff, t); + else if (type == DateDiffType.Start) return t('dates.starting_right_now'); + return t('dates.ending_right_now'); } -function strDiffFutureV2(type: DateDiffType, secondDiff: number): string { +function strDiffFutureV2(type: DateDiffType, secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { @@ -23,49 +23,49 @@ function strDiffFutureV2(type: DateDiffType, secondDiff: number): string { num = Math.floor(secondDiff / 60 / 60 / 24); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_days', { num }); - return i18n.t('dates.ending_in_n_days', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_days', { num }); + return t('dates.ending_in_n_days', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_tomorrow'); - return i18n.t('dates.ending_tomorrow'); + if (type == DateDiffType.Start) return t('dates.starting_tomorrow'); + return t('dates.ending_tomorrow'); } } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_hours', { num }); - return i18n.t('dates.ending_in_n_hours', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_hours', { num }); + return t('dates.ending_in_n_hours', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_one_hour'); - return i18n.t('dates.ending_in_one_hour'); + if (type == DateDiffType.Start) return t('dates.starting_in_one_hour'); + return t('dates.ending_in_one_hour'); } } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_minutes', { num }); - return i18n.t('dates.ending_in_n_minutes', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_minutes', { num }); + return t('dates.ending_in_n_minutes', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_one_minute'); - return i18n.t('dates.ending_in_one_minute'); + if (type == DateDiffType.Start) return t('dates.starting_in_one_minute'); + return t('dates.ending_in_one_minute'); } } else { // seconds num = Math.floor(secondDiff); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_seconds', { num }); - return i18n.t('dates.ending_in_n_seconds', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_seconds', { num }); + return t('dates.ending_in_n_seconds', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_right_now'); - return i18n.t('dates.ending_right_now'); + if (type == DateDiffType.Start) return t('dates.starting_right_now'); + return t('dates.ending_right_now'); } } } -function strDiffPastV2(type: DateDiffType, secondDiff: number): string { +function strDiffPastV2(type: DateDiffType, secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { @@ -73,130 +73,130 @@ function strDiffPastV2(type: DateDiffType, secondDiff: number): string { num = Math.floor(secondDiff / 60 / 60 / 24); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_days_ago', { num }); - return i18n.t('dates.ended_n_days_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_days_ago', { num }); + return t('dates.ended_n_days_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_yesterday'); - return i18n.t('dates.ended_yesterday'); + if (type == DateDiffType.Start) return t('dates.started_yesterday'); + return t('dates.ended_yesterday'); } } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_hours_ago', { num }); - return i18n.t('dates.ended_n_hours_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_hours_ago', { num }); + return t('dates.ended_n_hours_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_an_hour_ago'); - return i18n.t('dates.ended_an_hour_ago'); + if (type == DateDiffType.Start) return t('dates.started_an_hour_ago'); + return t('dates.ended_an_hour_ago'); } } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_minutes_ago', { num }); - return i18n.t('dates.ended_n_minutes_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_minutes_ago', { num }); + return t('dates.ended_n_minutes_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_a_minute_ago'); - return i18n.t('dates.ended_a_minute_ago'); + if (type == DateDiffType.Start) return t('dates.started_a_minute_ago'); + return t('dates.ended_a_minute_ago'); } } else { // seconds num = Math.floor(secondDiff); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_seconds_ago', { num }); - return i18n.t('dates.ended_n_seconds_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_seconds_ago', { num }); + return t('dates.ended_n_seconds_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_right_now'); - return i18n.t('dates.ended_right_now'); + if (type == DateDiffType.Start) return t('dates.started_right_now'); + return t('dates.ended_right_now'); } } } -export function localizedDateDiff(target: Date): string { +export function localizedDateDiff(target: Date, t: TFunction): string { if (!target) return ''; const diff = (target.getTime() - Date.now()) / 1000; - if (diff > 3) return strDiffFuture(diff); - else if (diff < -3) return strDiffPast(-diff); - return i18n.t('dates.right_now'); + if (diff > 3) return strDiffFuture(diff, t); + else if (diff < -3) return strDiffPast(-diff, t); + return t('dates.right_now'); } -export function localizedStartEndDateDiff(type: DateDiffType, target: Date): string { +export function localizedStartEndDateDiff(type: DateDiffType, target: Date, t: TFunction): string { if (!target) return ''; const diff = (target.getTime() - Date.now()) / 1000; - if (diff > 3) return strStartEndDiffFuture(type, diff); - else if (diff < -3) return strStartEndDiffPast(type, -diff); - else if (type == DateDiffType.Start) return i18n.t('dates.starting_right_now'); - return i18n.t('dates.ending_right_now'); + if (diff > 3) return strStartEndDiffFuture(type, diff, t); + else if (diff < -3) return strStartEndDiffPast(type, -diff, t); + else if (type == DateDiffType.Start) return t('dates.starting_right_now'); + return t('dates.ending_right_now'); } // Helpers -function strDiffFuture(secondDiff: number): string { +function strDiffFuture(secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { // days num = Math.floor(secondDiff / 60 / 60 / 24); - if (num > 1) return i18n.t('dates.in_n_days', { num }); - return i18n.t('dates.tomorrow'); + if (num > 1) return t('dates.in_n_days', { num }); + return t('dates.tomorrow'); } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); - if (num > 1) return i18n.t('dates.in_n_hours', { num }); - return i18n.t('dates.in_one_hour'); + if (num > 1) return t('dates.in_n_hours', { num }); + return t('dates.in_one_hour'); } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); - if (num > 1) return i18n.t('dates.in_n_minutes', { num }); - return i18n.t('dates.in_one_minute'); + if (num > 1) return t('dates.in_n_minutes', { num }); + return t('dates.in_one_minute'); } else { // seconds num = Math.floor(secondDiff); - if (num > 1) return i18n.t('dates.in_n_seconds', { num }); - return i18n.t('dates.right_now'); + if (num > 1) return t('dates.in_n_seconds', { num }); + return t('dates.right_now'); } } -function strDiffPast(secondDiff: number): string { +function strDiffPast(secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { // days num = Math.floor(secondDiff / 60 / 60 / 24); - if (num > 1) return i18n.t('dates.n_days_ago', { num }); - return i18n.t('dates.yesterday'); + if (num > 1) return t('dates.n_days_ago', { num }); + return t('dates.yesterday'); } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); - if (num > 1) return i18n.t('dates.n_hours_ago', { num }); - return i18n.t('dates.an_hour_ago'); + if (num > 1) return t('dates.n_hours_ago', { num }); + return t('dates.an_hour_ago'); } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); - if (num > 1) return i18n.t('dates.n_minutes_ago', { num }); - return i18n.t('dates.a_minute_ago'); + if (num > 1) return t('dates.n_minutes_ago', { num }); + return t('dates.a_minute_ago'); } else { // seconds num = Math.floor(secondDiff); - if (num > 1) return i18n.t('dates.n_seconds_ago', { num }); - return i18n.t('dates.right_now'); + if (num > 1) return t('dates.n_seconds_ago', { num }); + return t('dates.right_now'); } } -function strStartEndDiffFuture(type: DateDiffType, secondDiff: number): string { +function strStartEndDiffFuture(type: DateDiffType, secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { @@ -204,49 +204,49 @@ function strStartEndDiffFuture(type: DateDiffType, secondDiff: number): string { num = Math.floor(secondDiff / 60 / 60 / 24); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_days', { num }); - return i18n.t('dates.ending_in_n_days', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_days', { num }); + return t('dates.ending_in_n_days', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_tomorrow'); - return i18n.t('dates.ending_tomorrow'); + if (type == DateDiffType.Start) return t('dates.starting_tomorrow'); + return t('dates.ending_tomorrow'); } } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_hours', { num }); - return i18n.t('dates.ending_in_n_hours', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_hours', { num }); + return t('dates.ending_in_n_hours', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_one_hour'); - return i18n.t('dates.ending_in_one_hour'); + if (type == DateDiffType.Start) return t('dates.starting_in_one_hour'); + return t('dates.ending_in_one_hour'); } } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_minutes', { num }); - return i18n.t('dates.ending_in_n_minutes', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_minutes', { num }); + return t('dates.ending_in_n_minutes', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_one_minute'); - return i18n.t('dates.ending_in_one_minute'); + if (type == DateDiffType.Start) return t('dates.starting_in_one_minute'); + return t('dates.ending_in_one_minute'); } } else { // seconds num = Math.floor(secondDiff); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.starting_in_n_seconds', { num }); - return i18n.t('dates.ending_in_n_seconds', { num }); + if (type == DateDiffType.Start) return t('dates.starting_in_n_seconds', { num }); + return t('dates.ending_in_n_seconds', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.starting_right_now'); - return i18n.t('dates.ending_right_now'); + if (type == DateDiffType.Start) return t('dates.starting_right_now'); + return t('dates.ending_right_now'); } } } -function strStartEndDiffPast(type: DateDiffType, secondDiff: number): string { +function strStartEndDiffPast(type: DateDiffType, secondDiff: number, t: TFunction): string { let num: number; if (secondDiff > 60 * 60 * 24) { @@ -254,44 +254,44 @@ function strStartEndDiffPast(type: DateDiffType, secondDiff: number): string { num = Math.floor(secondDiff / 60 / 60 / 24); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_days_ago', { num }); - return i18n.t('dates.ended_n_days_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_days_ago', { num }); + return t('dates.ended_n_days_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_yesterday'); - return i18n.t('dates.ended_yesterday'); + if (type == DateDiffType.Start) return t('dates.started_yesterday'); + return t('dates.ended_yesterday'); } } else if (secondDiff > 60 * 60) { // hours num = Math.floor(secondDiff / 60 / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_hours_ago', { num }); - return i18n.t('dates.ended_n_hours_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_hours_ago', { num }); + return t('dates.ended_n_hours_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_an_hour_ago'); - return i18n.t('dates.ended_an_hour_ago'); + if (type == DateDiffType.Start) return t('dates.started_an_hour_ago'); + return t('dates.ended_an_hour_ago'); } } else if (secondDiff > 60) { // minutes num = Math.floor(secondDiff / 60); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_minutes_ago', { num }); - return i18n.t('dates.ended_n_minutes_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_minutes_ago', { num }); + return t('dates.ended_n_minutes_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_a_minute_ago'); - return i18n.t('dates.ended_a_minute_ago'); + if (type == DateDiffType.Start) return t('dates.started_a_minute_ago'); + return t('dates.ended_a_minute_ago'); } } else { // seconds num = Math.floor(secondDiff); if (num > 1) { - if (type == DateDiffType.Start) return i18n.t('dates.started_n_seconds_ago', { num }); - return i18n.t('dates.ended_n_seconds_ago', { num }); + if (type == DateDiffType.Start) return t('dates.started_n_seconds_ago', { num }); + return t('dates.ended_n_seconds_ago', { num }); } else { - if (type == DateDiffType.Start) return i18n.t('dates.started_right_now'); - return i18n.t('dates.ended_right_now'); + if (type == DateDiffType.Start) return t('dates.started_right_now'); + return t('dates.ended_right_now'); } } } diff --git a/lib/validators/errors/invalid-choice-error.ts b/lib/validators/errors/invalid-choice-error.ts index aab41008..17245332 100644 --- a/lib/validators/errors/invalid-choice-error.ts +++ b/lib/validators/errors/invalid-choice-error.ts @@ -1,7 +1,6 @@ -import i18n from '@i18n'; export class InvalidChoiceError extends Error { constructor(length: number) { - super(i18n.t('error.invalid_choice_error', { length })); + super(t('error.invalid_choice_error', { length })); } } diff --git a/lib/validators/errors/invalid-description-error.ts b/lib/validators/errors/invalid-description-error.ts index c3b92cfe..cbc499ee 100644 --- a/lib/validators/errors/invalid-description-error.ts +++ b/lib/validators/errors/invalid-description-error.ts @@ -1,7 +1,6 @@ -import i18n from '@i18n'; export class InvalidDescriptionError extends Error { constructor(length: number) { - super(i18n.t('error.invalid_description', { length })); + super(t('error.invalid_description', { length })); } } diff --git a/lib/validators/errors/invalid-question-error.ts b/lib/validators/errors/invalid-question-error.ts index 3d6b29a4..efb4394f 100644 --- a/lib/validators/errors/invalid-question-error.ts +++ b/lib/validators/errors/invalid-question-error.ts @@ -1,4 +1,3 @@ -import i18n from '@i18n'; export type QuestionError = { title?: Error; @@ -12,7 +11,7 @@ export class InvalidQuestionsError extends Error { public question: MapQuestionError; constructor(question: MapQuestionError) { - super(i18n.t('error.invalid_question_error')); + super(t('error.invalid_question_error')); this.question = question; } diff --git a/lib/validators/errors/invalid-title-error.ts b/lib/validators/errors/invalid-title-error.ts index ad9514a1..4ce2f897 100644 --- a/lib/validators/errors/invalid-title-error.ts +++ b/lib/validators/errors/invalid-title-error.ts @@ -1,7 +1,6 @@ -import i18n from '@i18n'; export class InvalidTitleError extends Error { constructor(length: number) { - super(i18n.t('error.invalid_title', { length })); + super(t('error.invalid_title', { length })); } } diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 00000000..0e4ee704 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,10 @@ +/** @type {import('next-i18next').UserConfig} */ + +const supportedLanguages = ['ca', 'en', 'es']; + +module.exports = { + i18n: { + defaultLocale: 'en', + locales: supportedLanguages, + }, +}; diff --git a/next.config.js b/next.config.js index 384e7496..237463cb 100644 --- a/next.config.js +++ b/next.config.js @@ -1,9 +1,11 @@ const env = require('./env-config.js'); +const { i18n } = require('./next-i18next.config') module.exports = { // Generate /dashboard/ instead of /dashboard.html trailingSlash: true, env, + i18n, webpack: (config) => { // Fixes npm packages that depend on `fs` module config.resolve.fallback = { diff --git a/package.json b/package.json index c882946a..0ece1709 100644 --- a/package.json +++ b/package.json @@ -46,11 +46,11 @@ "framer-motion": "^10.0.1", "html-react-parser": "^1.4.5", "i18next": "^22.4.10", - "i18next-browser-languagedetector": "^8.0.0", "next": "^13.1.4", + "next-i18next": "^15.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-i18next": "^12.2.0", + "react-i18next": "^14.1.2", "react-icons": "^4.4.0", "react-if": "^4.0.4", "react-markdown": "^8.0.5", diff --git a/pages/404.tsx b/pages/404.tsx index c132bcc6..55c8f9bd 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -1,6 +1,14 @@ import { PageCard } from '../components/elements/cards'; import NotFoundComponent from '../components/pages/not-found'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const NotFound = () => ( diff --git a/pages/_app.tsx b/pages/_app.tsx index 39f90629..6a62580a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,4 +1,5 @@ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC } from 'react'; +import { appWithTranslation } from 'next-i18next'; import { NextComponentType, NextPageContext } from 'next'; import { AppInitialProps } from 'next/app'; @@ -28,7 +29,6 @@ const VocdoniApp: FC = ({ Component, pageProps }) => { const appDescr = process.env.APP_DESCRIPTION; const apptags = process.env.APP_TAGS; const commitSHA = process.env.COMMIT_SHA; - const [initialRenderComplete, setInitialRenderComplete] = useState(false); // If the current page component defined a custom layout, use it const Layout: FC = Component['Layout'] ? Component['Layout'] : DefaultLayout; @@ -37,11 +37,6 @@ const VocdoniApp: FC = ({ Component, pageProps }) => { console.log('these no has default providers'); } - useEffect(() => { - setInitialRenderComplete(true); - }, []); - - if (!initialRenderComplete) return <>; return ( @@ -67,4 +62,4 @@ const VocdoniApp: FC = ({ Component, pageProps }) => { ); }; -export default VocdoniApp; +export default appWithTranslation(VocdoniApp); diff --git a/pages/blocks/index.tsx b/pages/blocks/index.tsx index b92fe921..8f06e8dd 100644 --- a/pages/blocks/index.tsx +++ b/pages/blocks/index.tsx @@ -1,5 +1,14 @@ import React from 'react'; import { DashboardShowBlocks } from '@components/pages/blocks/list'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; + +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const BlocksPage = () => { return ; diff --git a/pages/blocks/show.tsx b/pages/blocks/show.tsx index 460dc3a3..f06a1ef4 100644 --- a/pages/blocks/show.tsx +++ b/pages/blocks/show.tsx @@ -1,11 +1,20 @@ import { BlockView } from '@components/pages/blocks/details'; import { Else, If, Then } from 'react-if'; import { useUrlHash } from 'use-url-hash'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useBlockByHash, useBlockByHeight } from '@hooks/use-voconi-sdk'; import { ensure0x, IChainBlockInfoResponse } from '@vocdoni/sdk'; import LoaderPage from '@components/pages/app/layout/loader-page'; import React from 'react'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; + +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const BlockOrLoadingView = ({ block, @@ -16,14 +25,13 @@ const BlockOrLoadingView = ({ loading: boolean; error: Error; }) => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( diff --git a/pages/converter.tsx b/pages/converter.tsx index 56431581..cfef4b07 100644 --- a/pages/converter.tsx +++ b/pages/converter.tsx @@ -6,7 +6,7 @@ import { Typography, TypographyVariant } from '@components/elements/typography'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { FlexAlignItem, FlexContainer, FlexDirection, FlexJustifyContent } from '@components/elements/flex'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { localizedDateDiff } from '@lib/date'; import { capitalize } from '@lib/util'; @@ -14,10 +14,17 @@ import { FiChevronDown, FiChevronLeft, FiChevronRight, FiChevronUp } from 'react import { useIsMobile } from '@hooks/use-window-size'; import DateTimePicker from '@components/elements/date-picker'; import { useBlockHeight, useBlockToDate, useChainInfo, useDateToBlock } from '@hooks/use-voconi-sdk'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const BlocksPage = () => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const { blockHeight } = useBlockHeight(); const [blockInput, setBlockInput] = useState(); @@ -86,28 +93,26 @@ const BlocksPage = () => { - {i18n.t('converter.date_block_estimation')} - - {i18n.t('converter.calculate_the_conversion_between_Vochain_blocks_and_dates')} - + {t('converter.date_block_estimation')} + {t('converter.calculate_the_conversion_between_Vochain_blocks_and_dates')}
- + {capitalize(enviormentName(process.env.VOCDONI_ENVIRONMENT))}
- - {!loadingStats && localizedDateDiff(genesisDate)} + + {!loadingStats && localizedDateDiff(genesisDate, t)}
- {blockHeight} + {blockHeight}
{/* */} - {i18n.t('converter.set_date')} + {t('converter.set_date')} { - {i18n.t('converter.set_block')} + {t('converter.set_block')} { setBlockInput(+ev.target.value); @@ -155,7 +160,7 @@ const BlocksPage = () => {
- {loading ? {i18n.t('converter.loading_info')} : null} + {loading ? {t('converter.loading_info')} : null}
); diff --git a/pages/envelopes/show.tsx b/pages/envelopes/show.tsx index 6130b831..49cf6548 100644 --- a/pages/envelopes/show.tsx +++ b/pages/envelopes/show.tsx @@ -1,12 +1,20 @@ import { EnvelopeDetails } from '@components/pages/envelopes/details'; import { useUrlHash } from 'use-url-hash'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useVoteInfo } from '@hooks/use-voconi-sdk'; import LoaderPage from '@components/pages/app/layout/loader-page'; import React from 'react'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const EnvelopeDetailPage = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const voteId: string = useUrlHash().slice(1); const { loading, @@ -23,7 +31,7 @@ const EnvelopeDetailPage = () => { loading={isLoading} error={!!error} hasContent={!!envelope} - errorMessage={i18n.t('envelopes.details.envelope_not_found')} + errorMessage={t('envelopes.details.envelope_not_found')} > diff --git a/pages/index.tsx b/pages/index.tsx index 55a3624b..eb46adaa 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -5,7 +5,15 @@ import { Else, If, Then } from 'react-if'; import FeaturedContent from './index/featured'; import { useChainInfo } from '@hooks/use-voconi-sdk'; import { StatsHeroBanner } from '@components/pages/stats/components/stats-hero-banner'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} // MAIN COMPONENT const IndexPage = () => { const { data: stats } = useChainInfo(); diff --git a/pages/index/featured.tsx b/pages/index/featured.tsx index fb3a3ea5..d505af3d 100644 --- a/pages/index/featured.tsx +++ b/pages/index/featured.tsx @@ -5,34 +5,41 @@ import { ImageContainer } from '@components/elements/images'; import { Grid, Column } from '@components/elements/grid'; import { Section, BlockContainer } from '@components/elements/styled-divs'; import styled from 'styled-components'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { HomePageButton } from '@components/elements/button'; import Link from 'next/link'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const FeaturedContent = () => { - const { i18n } = useTranslation(); - + const { t } = useTranslation(); return ( <> - {i18n.t('featured.anonymous_image_alt')} + {t('featured.anonymous_image_alt')} - {i18n.t('featured.open_source_image_alt')} + {t('featured.open_source_image_alt')} - {i18n.t('featured.scalable_image_alt')} + {t('featured.scalable_image_alt')} - {i18n.t('featured.inexpensive_image_alt')} + {t('featured.inexpensive_image_alt')} - {i18n.t('featured.censorship_image_alt')} + {t('featured.censorship_image_alt')} - {i18n.t('featured.verifiable_image_alt')} + {t('featured.verifiable_image_alt')} @@ -41,21 +48,21 @@ const FeaturedContent = () => { - {i18n.t('featured.a_cutting_edge_voting_protocol')} + {t('featured.a_cutting_edge_voting_protocol')} - {i18n.t('featured.a_fully_anonymous_voting_system_ensuring_data_availability')} + {t('featured.a_fully_anonymous_voting_system_ensuring_data_availability')} - {i18n.t('featured.leveraging_on_decentalized_technologies')} + {t('featured.leveraging_on_decentalized_technologies')} - {i18n.t('featured.know_more')} + {t('featured.know_more')} - {i18n.t('featured.edge_protocol_image_alt')} + {t('featured.edge_protocol_image_alt')} diff --git a/pages/organizations/index.tsx b/pages/organizations/index.tsx index aa56ced7..64560117 100644 --- a/pages/organizations/index.tsx +++ b/pages/organizations/index.tsx @@ -4,7 +4,15 @@ import { ViewContext, ViewStrategy } from '@lib/strategy'; import { Loader } from '@components/blocks/loader'; import { DashboardShowEntities } from '@components/pages/organizations/list'; - +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; + +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const EntityPage = () => { const strategies: ViewStrategy[] = []; diff --git a/pages/organizations/show.tsx b/pages/organizations/show.tsx index 9eabc706..e4da0e46 100644 --- a/pages/organizations/show.tsx +++ b/pages/organizations/show.tsx @@ -2,13 +2,20 @@ import LoaderPage from '@components/pages/app/layout/loader-page'; import { OrganizationView } from '@components/pages/organizations/details'; import { OrganizationProvider, useOrganization } from '@vocdoni/react-providers'; import { ensure0x } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useUrlHash } from 'use-url-hash'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const OrganizationsDetailPage = () => { const { organization, loading, errors, loaded } = useOrganization(); - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const error = errors.load?.length > 0 || errors.update?.length > 0; const hasContent = !!organization; const isLoading = loading || !loaded; @@ -18,7 +25,7 @@ const OrganizationsDetailPage = () => { loading={isLoading} error={error} hasContent={hasContent} - errorMessage={i18n.t('organizations.details.organization_not_found')} + errorMessage={t('organizations.details.organization_not_found')} > diff --git a/pages/processes/index.tsx b/pages/processes/index.tsx index a7607e45..88d6a41b 100644 --- a/pages/processes/index.tsx +++ b/pages/processes/index.tsx @@ -1,5 +1,13 @@ import { DashboardShowProcesses } from '@components/pages/elections/list'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const ProcessesPage = () => { return ; }; diff --git a/pages/processes/show.tsx b/pages/processes/show.tsx index 61e792bd..196a33ec 100644 --- a/pages/processes/show.tsx +++ b/pages/processes/show.tsx @@ -2,9 +2,17 @@ import LoaderPage from '@components/pages/app/layout/loader-page'; import ProcessDetailPage from '@components/pages/elections/details'; import { ElectionProvider, OrganizationProvider, useElection } from '@vocdoni/react-providers'; import { ensure0x } from '@vocdoni/sdk'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useUrlHash } from 'use-url-hash'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const ProcessesDetailPage = () => { const { loading: { election: loading }, @@ -12,8 +20,7 @@ const ProcessesDetailPage = () => { errors: { election: error }, loaded, } = useElection(); - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const hasError = error?.length > 0; const hasContent = !!election; const isLoading = loading || !loaded; @@ -23,7 +30,7 @@ const ProcessesDetailPage = () => { loading={isLoading} error={hasError} hasContent={hasContent} - errorMessage={i18n.t('processes.details.process_not_found')} + errorMessage={t('processes.details.process_not_found')} > diff --git a/pages/stats/index.tsx b/pages/stats/index.tsx index 90bc267f..36fb46f8 100644 --- a/pages/stats/index.tsx +++ b/pages/stats/index.tsx @@ -4,7 +4,15 @@ import { Else, If, Then } from 'react-if'; import styled from 'styled-components'; import { useChainInfo } from '@hooks/use-voconi-sdk'; import { StatsHeroBanner } from '@components/pages/stats/components/stats-hero-banner'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const StatsPageIndex = () => { const { loading: loadingStats, data: stats } = useChainInfo(); diff --git a/pages/transactions/index.tsx b/pages/transactions/index.tsx index 31dd1161..605ddbcd 100644 --- a/pages/transactions/index.tsx +++ b/pages/transactions/index.tsx @@ -1,6 +1,14 @@ import React from 'react'; import { DashboardShowTransactions } from '@components/pages/transactions/list/index'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const TransactionsPage = () => { return ; }; diff --git a/pages/transactions/show.tsx b/pages/transactions/show.tsx index 7b77cf7a..6e88325a 100644 --- a/pages/transactions/show.tsx +++ b/pages/transactions/show.tsx @@ -1,10 +1,18 @@ import { Else, If, Then } from 'react-if'; import { useUrlHash } from 'use-url-hash'; import { TransactionDetails } from '@components/pages/transactions/details'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useTxByBlock, useTxByHash } from '@hooks/use-voconi-sdk'; import LoaderPage from '@components/pages/app/layout/loader-page'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const TransactionByHeightAndIndex = ({ blockHeight, txIndex, @@ -22,8 +30,7 @@ const TransactionByHeightAndIndex = ({ loaded, error: errorByBlock, } = useTxByBlock({ blockHeight: blockHeight, txIndex: txIndex }); - const { i18n } = useTranslation(); - + const { t } = useTranslation(); const loading = l || txLoading || !loaded; const hasError = error || !!errorByBlock; @@ -33,7 +40,7 @@ const TransactionByHeightAndIndex = ({ loading={loading} error={hasError} hasContent={!!tx} - errorMessage={i18n.t('transactions.details.transaction_not_found')} + errorMessage={t('transactions.details.transaction_not_found')} > diff --git a/pages/validators/index.tsx b/pages/validators/index.tsx index f83d6873..2e669ada 100644 --- a/pages/validators/index.tsx +++ b/pages/validators/index.tsx @@ -1,11 +1,19 @@ import { Loader } from '@components/blocks/loader'; import { DashboardShowValidators } from '@components/pages/validators/list'; import { Else, If, Then } from 'react-if'; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'next-i18next'; import { useValidators } from '@hooks/use-voconi-sdk'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const Page = () => { - const { i18n } = useTranslation(); + const { t } = useTranslation(); const { loading, data: validatorsResponse } = useValidators(); const validators = validatorsResponse?.validators ?? []; @@ -22,7 +30,7 @@ const Page = () => { -

{i18n.t('validators.no_validators_found')}

+

{t('validators.no_validators_found')}

diff --git a/pages/verify/index.tsx b/pages/verify/index.tsx index 1e32ad1b..3ffbafb6 100644 --- a/pages/verify/index.tsx +++ b/pages/verify/index.tsx @@ -8,7 +8,15 @@ import Router from 'next/router'; import { getPath } from '@components/pages/app/components/get-links'; import { VERIFY_DETAILS } from '@const/routes'; import { useEffect, useState } from 'react'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +export async function getStaticProps({ locale }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} const VerifyIndexPage = () => { const urlVoteId = useUrlHash().slice(1); const [voteId, setVoteId] = useState(''); diff --git a/i18n/locales/ca.json b/public/locales/ca/common.json similarity index 98% rename from i18n/locales/ca.json rename to public/locales/ca/common.json index 0fc7e3e7..0c7d0227 100644 --- a/i18n/locales/ca.json +++ b/public/locales/ca/common.json @@ -170,7 +170,6 @@ "docs": "Documentació", "help": "Ajuda", "organizations": "Organitzacions", - "privacy_policy": "Política de privacitat", "processes": "Processos", "stats": "Estat", "support": "Suport", @@ -217,8 +216,7 @@ "ERC777": "ERC777", "MINI_ME": "Mini me", "OFF_CHAIN": "Off Chain", - "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted", - "UNRECOGNIZED": "Unrecognized" + "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted" }, "details": { "created_on": "Creat el", @@ -349,7 +347,6 @@ "results_question": "Pregunta", "vote_count_one": "", "vote_count_many": "", - "vote_count_other": "", - "vote_count": "{{ count }} vots" + "vote_count_other": "" } } diff --git a/i18n/locales/ca_old.json b/public/locales/ca/common_old.json similarity index 100% rename from i18n/locales/ca_old.json rename to public/locales/ca/common_old.json diff --git a/i18n/locales/en.json b/public/locales/en/common.json similarity index 98% rename from i18n/locales/en.json rename to public/locales/en/common.json index 6b0ba6d5..3c2e1006 100644 --- a/i18n/locales/en.json +++ b/public/locales/en/common.json @@ -170,7 +170,6 @@ "docs": "Docs", "help": "Help", "organizations": "Organizations", - "privacy_policy": "Privacy policy", "processes": "Processes", "stats": "Stats", "support": "Support", @@ -217,8 +216,7 @@ "ERC777": "ERC777", "MINI_ME": "Mini me", "OFF_CHAIN": "Off Chain", - "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted", - "UNRECOGNIZED": "Unrecognized" + "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted" }, "details": { "created_on": "Created ", @@ -348,7 +346,6 @@ "no_results_title": "No results", "results_question": "Question", "vote_count_one": "", - "vote_count_other": "", - "vote_count": "{{ count }} voting power" + "vote_count_other": "" } } diff --git a/i18n/locales/en_old.json b/public/locales/en/common_old.json similarity index 100% rename from i18n/locales/en_old.json rename to public/locales/en/common_old.json diff --git a/i18n/locales/eo.json b/public/locales/eo/common.json similarity index 100% rename from i18n/locales/eo.json rename to public/locales/eo/common.json diff --git a/i18n/locales/es.json b/public/locales/es/common.json similarity index 98% rename from i18n/locales/es.json rename to public/locales/es/common.json index db2eb459..aa9b29f4 100644 --- a/i18n/locales/es.json +++ b/public/locales/es/common.json @@ -170,7 +170,6 @@ "docs": "Documentación", "help": "Ayuda", "organizations": "Organizaciones", - "privacy_policy": "Política de privacidad", "processes": "Procesos", "stats": "Estado", "support": "Soporte", @@ -217,8 +216,7 @@ "ERC777": "ERC777", "MINI_ME": "Mini me", "OFF_CHAIN": "Off Chain", - "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted", - "UNRECOGNIZED": "Unrecognized" + "OFF_CHAIN_TREE_WEIGHTED": "Off Chain Weighted" }, "details": { "created_on": "Creado el", @@ -349,7 +347,6 @@ "results_question": "Pregunta", "vote_count_one": "", "vote_count_many": "", - "vote_count_other": "", - "vote_count": "{{ count }} votos" + "vote_count_other": "" } } diff --git a/i18n/locales/es_old.json b/public/locales/es/common_old.json similarity index 100% rename from i18n/locales/es_old.json rename to public/locales/es/common_old.json diff --git a/yarn.lock b/yarn.lock index 4a20e0c6..5143858d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -288,6 +288,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.9": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" @@ -2218,6 +2225,14 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/hoist-non-react-statics@^3.3.4": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -3139,6 +3154,11 @@ copy-to-clipboard@3.3.3, copy-to-clipboard@^3.3.1: dependencies: toggle-selection "^1.0.6" +core-js@^3: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.1.tgz#d21751ddb756518ac5a00e4d66499df981a62db9" + integrity sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -4261,7 +4281,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4323,12 +4343,10 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -i18next-browser-languagedetector@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz#b6fdd9b43af67c47f2c26c9ba27710a1eaf31e2f" - integrity sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw== - dependencies: - "@babel/runtime" "^7.23.2" +i18next-fs-backend@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.3.1.tgz#0c7d2459ff4a039e2b3228131809fbc0e74ff1a8" + integrity sha512-tvfXskmG/9o+TJ5Fxu54sSO5OkY6d+uMn+K6JiUGLJrwxAVfer+8V3nU8jq3ts9Pe5lXJv4b1N7foIjJ8Iy2Gg== i18next-parser@^7.7.0: version "7.7.0" @@ -5795,6 +5813,17 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +next-i18next@^15.3.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-15.3.0.tgz#b4530c80573854d00f95229af405e1e5beedbf18" + integrity sha512-bq7Cc9XJFcmGOCLnyEtHaeJ3+JJNsI/8Pkj9BaHAnhm4sZ9vNNC4ZsaqYnlRZ7VH5ypSo73fEqLK935jLsmCvQ== + dependencies: + "@babel/runtime" "^7.23.2" + "@types/hoist-non-react-statics" "^3.3.4" + core-js "^3" + hoist-non-react-statics "^3.3.2" + i18next-fs-backend "^2.3.1" + next@^13.1.4: version "13.2.3" resolved "https://registry.yarnpkg.com/next/-/next-13.2.3.tgz#92d170e7aca421321f230ff80c35c4751035f42e" @@ -6254,12 +6283,12 @@ react-focus-lock@^2.9.2: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-i18next@^12.2.0: - version "12.2.0" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.2.0.tgz#010e3f6070b8d700442947233352ebe4b252d7a1" - integrity sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ== +react-i18next@^14.1.2: + version "14.1.2" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.2.tgz#cd57a755f25a32a5fcc3dbe546cf3cc62b4f3ebd" + integrity sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg== dependencies: - "@babel/runtime" "^7.20.6" + "@babel/runtime" "^7.23.9" html-parse-stringify "^3.0.1" react-icons@^4.4.0: