From 75933e70809cfd893242f98873ca31e08fbd0e9f Mon Sep 17 00:00:00 2001 From: William Oldham Date: Mon, 27 Nov 2023 19:19:03 +0000 Subject: [PATCH] More localisation Co-authored-by: mrjvs --- src/assets/locales/en.json | 142 +++++++++++++++++- src/pages/errors/NotFoundPage.tsx | 2 +- src/pages/parts/auth/AccountCreatePart.tsx | 30 +++- src/pages/parts/auth/LoginFormPart.tsx | 20 +-- .../parts/auth/PassphraseGeneratePart.tsx | 12 +- src/pages/parts/auth/TrustBackendPart.tsx | 23 +-- src/pages/parts/auth/VerifyPassphrasePart.tsx | 18 ++- src/pages/parts/errors/ErrorCard.tsx | 8 +- src/pages/parts/errors/ErrorPart.tsx | 10 +- ...{ErrorWrapperPart.tsx => NotFoundPart.tsx} | 17 +-- src/pages/parts/home/BookmarksPart.tsx | 2 +- src/pages/parts/home/HeroPart.tsx | 6 +- src/pages/parts/home/WatchingPart.tsx | 2 +- src/pages/parts/migrations/MigrationPart.tsx | 6 +- src/pages/parts/player/MetaPart.tsx | 32 ++-- src/pages/parts/player/PlaybackErrorPart.tsx | 16 +- src/pages/parts/player/ScrapeErrorPart.tsx | 17 +-- src/pages/parts/search/SearchListPart.tsx | 8 +- .../parts/settings/AccountActionsPart.tsx | 20 +-- src/pages/parts/settings/AccountEditPart.tsx | 17 ++- src/pages/parts/settings/CaptionsPart.tsx | 15 +- 21 files changed, 291 insertions(+), 132 deletions(-) rename src/pages/parts/errors/{ErrorWrapperPart.tsx => NotFoundPart.tsx} (64%) diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 473d7dc0..614e11e7 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -1,4 +1,60 @@ { + "auth": { + "deviceNameLabel": "Device name", + "deviceNamePlaceholder": "Muad'Dib's Nintendo Switch", + "register": { + "information": { + "title": "Account information", + "color1": "First color", + "color2": "Second color", + "icon": "User icon", + "header": "Enter a name for your device and choose a user icon and colours" + } + }, + "login": { + "title": "Login to your account", + "description": "Oh, you're asking for the key to my top-secret lair, also known as The Fortress of Wordsmithery, accessed only by reciting the sacred incantation of the 12-word passphrase!", + "validationError": "Invalid or incomplete passphrase", + "submit": "Login", + "passphraseLabel": "12-Word Passphrase", + "passphrasePlaceholder": "Passphrase" + }, + "generate": { + "title": "Your passphrase", + "description": "If you lose this, you're a silly goose and will be posted on the wall of shame™️" + }, + "trust": { + "title": "Do you trust this host?", + "host": "Do you trust <0>{{hostname}}?", + "failed": { + "title": "Failed to reach backend", + "text": "Did you configure it correctly?" + }, + "yes": "Trust", + "no": "Go back" + }, + "verify": { + "title": "Enter your passphrase", + "description": "If you've already lost it, how will you ever be able to take care of a child?", + "invalidData": "Data is not valid", + "noMatch": "Passphrase doesn't match", + "recaptchaFailed": "ReCaptcha validation failed", + "passphraseLabel": "Your passphrase", + "register": "Register" + } + }, + "errors": { + "details": "Error details", + "reloadPage": "Reload the page", + "badge": "It broke", + "title": "That's an error boss" + }, + "notFound": { + "badge": "Not found", + "title": "Page could not be found", + "message": "Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle movie-web ish twying so hard. Can you find it in your heart to forgive? UwU 💖", + "goHome": "Go home" + }, "global": { "name": "movie-web" }, @@ -9,9 +65,57 @@ }, "episodeDisplay": "S{{season}} E{{episode}}" }, + "player": { + "scraping": { + "notFound": { + "badge": "Not found", + "title": "Goo goo gaa gaa", + "text": "Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle movie-web ish twying so hard. Can you find it in your heart to forgive? UwU 💖", + "homeButton": "Go home" + } + }, + "playbackError": { + "badge": "Not found", + "title": "Goo goo gaa gaa", + "text": "Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle movie-web ish twying so hard. Can you find it in your heart to forgive? UwU 💖", + "homeButton": "Go home" + }, + "metadata": { + "notFound": { + "badge": "Not found", + "title": "This media doesnt exist", + "text": "Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle movie-web ish twying so hard. Can you find it in your heart to forgive? UwU 💖", + "homeButton": "Go home" + }, + "failed": { + "badge": "Failed", + "title": "Failed to load meta data", + "text": "Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle movie-web ish twying so hard. Can you find it in your heart to forgive? UwU 💖", + "homeButton": "Go home" + } + } + }, "home": { "mediaList": { "stopEditing": "Stop editing" + }, + "titles": { + "morning": ["Morning title"], + "day": ["Day title"], + "night": ["Night title"] + }, + "search": { + "sectionTitle": "Search results", + "allResults": "That's all we have!", + "noResults": "We couldn't find anything!", + "failed": "Failed to find media, try again!", + "placeholder": "What do you want to watch?" + }, + "continueWatching": { + "sectionTitle": "Continue Watching" + }, + "bookmarks": { + "sectionTitle": "Bookmarks" } }, "overlays": { @@ -21,16 +125,17 @@ "loadingUser": "Loading your profile", "loadingApp": "Loading application", "loadingUserError": { - "text": "", - "textWithReset": "", + "text": "Failed to load your profile", + "textWithReset": "Failed to load your profile from your custom server, want to reset back to default?", "reset": "Reset custom server" }, "migration": { - "failed": "Failed to migrate your data." + "failed": "Failed to migrate your data.", + "inProgress": "Please hold, we are migrating your data. This shouldn't take long." }, "dmca": { - "title": "", - "text": "" + "title": "DMCA", + "text": "In an effort to address the copyright concerns associated with the website known as \"movie-web,\" the DMCA, or Digital Millennium Copyright Act, has been initiated to safeguard the intellectual property rights of content creators by reporting infringements on this platform, thereby adhering to legal protocols for takedown requests, which, like, you know, it's all about, like, maintaining the integrity of intellectual property, and, um, making sure, like, creators get their fair share, but then, it's, like, this intricate dance of digital legalities, where you have to, uh, like, navigate this labyrinth of code and bytes and, uh, send, you know, these, like, electronic documents that, um, point out the, uh, alleged infringement, and it's, like, this whole, like, teeter-totter of legality, where you're, like, balancing, um, the rights of the, you know, creators and the, um, operation of this, like, online, uh, entity, and, like, the DMCA, it's, like, this, um, powerful tool, but, uh, it's also, like, this, um, complex puzzle, where, you know, you're, like, seeking justice in the digital wilderness, and, uh, striving for harmony amidst the chaos of the internet, and, um, yeah, that's, like, the whole, like, DMCA-ing thing with movie-web, you know?" } }, "navigation": { @@ -46,7 +151,9 @@ } }, "actions": { - "copy": "Copy" + "copy": "Copy", + "copied": "Copied", + "next": "Next" }, "settings": { "unsaved": "You have unsaved changes", @@ -96,6 +203,23 @@ "failed": "Failed to load sessions", "deviceNameLabel": "Device name", "removeDevice": "Remove" + }, + "accountDetails": { + "editProfile": "Edit", + "deviceNameLabel": "Device name", + "deviceNamePlaceholder": "Fremen tablet", + "logoutButton": "Log out" + }, + "actions": { + "title": "Actions", + "delete": { + "title": "Delete account", + "text": "This action is irreversible. All data will be deleted and nothing can be recovered.", + "button": "Delete account", + "confirmTitle": "Are you sure?", + "confirmDescription": "Are you sure you want to delete your account? All your data will be lost!", + "confirmButton": "Delete account" + } } }, "locale": { @@ -104,7 +228,11 @@ "languageDescription": "Language applied to the entire application." }, "captions": { - "title": "Captions" + "title": "Captions", + "previewQuote": "I must not fear. Fear is the mind-killer.", + "backgroundLabel": "Background opacity", + "textSizeLabel": "Text size", + "colorLabel": "Color" }, "connections": { "title": "Connections", diff --git a/src/pages/errors/NotFoundPage.tsx b/src/pages/errors/NotFoundPage.tsx index cf9cfa7e..2155af04 100644 --- a/src/pages/errors/NotFoundPage.tsx +++ b/src/pages/errors/NotFoundPage.tsx @@ -1,4 +1,4 @@ -import { NotFoundPart } from "@/pages/parts/errors/ErrorWrapperPart"; +import { NotFoundPart } from "@/pages/parts/errors/NotFoundPart"; export function NotFoundPage() { return ; diff --git a/src/pages/parts/auth/AccountCreatePart.tsx b/src/pages/parts/auth/AccountCreatePart.tsx index ce7e01c1..a66aff0f 100644 --- a/src/pages/parts/auth/AccountCreatePart.tsx +++ b/src/pages/parts/auth/AccountCreatePart.tsx @@ -1,4 +1,5 @@ import { useCallback, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Button } from "@/components/buttons/Button"; import { ColorPicker } from "@/components/form/ColorPicker"; @@ -30,6 +31,7 @@ export function AccountCreatePart(props: AccountCreatePartProps) { const [colorA, setColorA] = useState("#2E65CF"); const [colorB, setColorB] = useState("#2E65CF"); const [userIcon, setUserIcon] = useState(UserIcons.USER); + const { t } = useTranslation(); // TODO validate device and account before next step const nextStep = useCallback(() => { @@ -47,24 +49,36 @@ export function AccountCreatePart(props: AccountCreatePartProps) { } - title="Account information" + title={t("auth.register.information.title") ?? undefined} > - Set up your account.... OR ELSE! + {t("auth.register.information.header")}
+ + + - - -
diff --git a/src/pages/parts/auth/LoginFormPart.tsx b/src/pages/parts/auth/LoginFormPart.tsx index 038745cf..16cec00b 100644 --- a/src/pages/parts/auth/LoginFormPart.tsx +++ b/src/pages/parts/auth/LoginFormPart.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; import { verifyValidMnemonic } from "@/backend/accounts/crypto"; @@ -24,12 +25,13 @@ export function LoginFormPart(props: LoginFormPartProps) { const { login, restore, importData } = useAuth(); const progressItems = useProgressStore((store) => store.items); const bookmarkItems = useBookmarkStore((store) => store.bookmarks); + const { t } = useTranslation(); const [result, execute] = useAsyncFn( async (inputMnemonic: string, inputdevice: string) => { // TODO verify valid device input if (!verifyValidMnemonic(inputMnemonic)) - throw new Error("Invalid or incomplete passphrase"); + throw new Error(t("auth.login.validationError") ?? undefined); const account = await login({ mnemonic: inputMnemonic, @@ -49,25 +51,23 @@ export function LoginFormPart(props: LoginFormPartProps) { return ( }> - - Oh, you're asking for the key to my top-secret lair, also known as - The Fortress of Wordsmithery, accessed only by reciting the sacred - incantation of the 12-word passphrase! + + {t("auth.login.description")}
{result.error && !result.loading ? (

@@ -82,7 +82,7 @@ export function LoginFormPart(props: LoginFormPartProps) { loading={result.loading} onClick={() => execute(mnemonic, device)} > - LET ME IN! + {t("auth.login.submit")} diff --git a/src/pages/parts/auth/PassphraseGeneratePart.tsx b/src/pages/parts/auth/PassphraseGeneratePart.tsx index e796e5a1..43b4518c 100644 --- a/src/pages/parts/auth/PassphraseGeneratePart.tsx +++ b/src/pages/parts/auth/PassphraseGeneratePart.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { genMnemonic } from "@/backend/accounts/crypto"; import { Button } from "@/components/buttons/Button"; @@ -16,18 +17,21 @@ interface PassphraseGeneratePartProps { export function PassphraseGeneratePart(props: PassphraseGeneratePartProps) { const mnemonic = useMemo(() => genMnemonic(), []); + const { t } = useTranslation(); return ( - }> - If you lose this, you're a silly goose and will be posted on the - wall of shame™️ + } + > + {t("auth.generate.description")} diff --git a/src/pages/parts/auth/TrustBackendPart.tsx b/src/pages/parts/auth/TrustBackendPart.tsx index 27c11332..77a88409 100644 --- a/src/pages/parts/auth/TrustBackendPart.tsx +++ b/src/pages/parts/auth/TrustBackendPart.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { Trans, useTranslation } from "react-i18next"; import { useHistory } from "react-router-dom"; import { useAsync } from "react-use"; @@ -21,18 +22,18 @@ interface TrustBackendPartProps { export function TrustBackendPart(props: TrustBackendPartProps) { const history = useHistory(); const backendUrl = useBackendUrl(); - const backendHostname = useMemo( - () => new URL(backendUrl).hostname, - [backendUrl] - ); + const hostname = useMemo(() => new URL(backendUrl).hostname, [backendUrl]); const result = useAsync(() => { return getBackendMeta(conf().BACKEND_URL); }, [backendUrl]); + const { t } = useTranslation(); let cardContent = ( <> -

Failed to reach backend

-

Did you configure it correctly?

+

+ {t("auth.trust.failed.title")} +

+

{t("auth.trust.failed.text")}

); if (result.loading) cardContent = ; @@ -47,10 +48,12 @@ export function TrustBackendPart(props: TrustBackendPartProps) { return ( } > - Do you trust {backendHostname}? + + {{ hostname }} +
@@ -61,10 +64,10 @@ export function TrustBackendPart(props: TrustBackendPartProps) { theme="purple" onClick={() => result.value && props.onNext?.(result.value)} > - I pledge my life to the United States + {t("auth.trust.yes")} diff --git a/src/pages/parts/auth/VerifyPassphrasePart.tsx b/src/pages/parts/auth/VerifyPassphrasePart.tsx index 4516a14a..35f40b27 100644 --- a/src/pages/parts/auth/VerifyPassphrasePart.tsx +++ b/src/pages/parts/auth/VerifyPassphrasePart.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { useGoogleReCaptcha } from "react-google-recaptcha-v3"; +import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; import { updateSettings } from "@/backend/accounts/settings"; @@ -40,24 +41,26 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) { const applicationTheme = useThemeStore((store) => store.theme); const backendUrl = useBackendUrl(); + const { t } = useTranslation(); const { executeRecaptcha } = useGoogleReCaptcha(); const [result, execute] = useAsyncFn( async (inputMnemonic: string) => { if (!props.mnemonic || !props.userData) - throw new Error("Data is not valid"); + throw new Error(t("auth.verify.invalidData") ?? undefined); let recaptchaToken: string | undefined; if (props.hasCaptcha) { recaptchaToken = executeRecaptcha ? await executeRecaptcha() : undefined; - if (!recaptchaToken) throw new Error("ReCaptcha validation failed"); + if (!recaptchaToken) + throw new Error(t("auth.verify.recaptchaFailed") ?? undefined); } if (inputMnemonic !== props.mnemonic) - throw new Error("Passphrase doesn't match"); + throw new Error(t("auth.verify.noMatch") ?? undefined); const account = await register({ mnemonic: inputMnemonic, @@ -85,13 +88,12 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
} - title="Enter your passphrase" + title={t("auth.verify.title")} > - If you've already lost it, how will you ever be able to take care - of a child? + {t("auth.verify.description")} execute(mnemonic)} > - Register + {t("auth.verify.register")} diff --git a/src/pages/parts/errors/ErrorCard.tsx b/src/pages/parts/errors/ErrorCard.tsx index 28fa4688..85480a08 100644 --- a/src/pages/parts/errors/ErrorCard.tsx +++ b/src/pages/parts/errors/ErrorCard.tsx @@ -1,4 +1,5 @@ import { useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Button } from "@/components/buttons/Button"; import { Icon, Icons } from "@/components/Icon"; @@ -10,6 +11,7 @@ export function ErrorCard(props: { error: DisplayError | string }) { const hasCopiedUnsetDebounce = useRef | null>( null ); + const { t } = useTranslation(); const errorMessage = typeof props.error === "string" ? props.error : props.error.message; @@ -32,7 +34,7 @@ export function ErrorCard(props: { error: DisplayError | string }) { // I didn't put a here because it'd fade out, then jump height weirdly
- Error details + {t("errors.details")}
diff --git a/src/pages/parts/errors/ErrorPart.tsx b/src/pages/parts/errors/ErrorPart.tsx index 0f7dca96..90109232 100644 --- a/src/pages/parts/errors/ErrorPart.tsx +++ b/src/pages/parts/errors/ErrorPart.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from "react-i18next"; + import { ButtonPlain } from "@/components/buttons/Button"; import { Icons } from "@/components/Icon"; import { IconPill } from "@/components/layout/IconPill"; @@ -10,20 +12,22 @@ export function ErrorPart(props: { error: any; errorInfo: any }) { error: props.error, errorInfo: props.errorInfo, }); + const { t } = useTranslation(); + return (
- It broke - Failed to load meta data + {t("errors.badge")} + {t("errors.title")} {data} window.location.reload()} > - Reload the page + {t("errors.reloadPage")} diff --git a/src/pages/parts/errors/ErrorWrapperPart.tsx b/src/pages/parts/errors/NotFoundPart.tsx similarity index 64% rename from src/pages/parts/errors/ErrorWrapperPart.tsx rename to src/pages/parts/errors/NotFoundPart.tsx index 688858cd..cbe93778 100644 --- a/src/pages/parts/errors/ErrorWrapperPart.tsx +++ b/src/pages/parts/errors/NotFoundPart.tsx @@ -15,29 +15,22 @@ export function NotFoundPart() { return (
- {t("notFound.genericTitle")} + {t("notFound.badge")}
- - {t("notFound.genericTitle")} - - Failed to load meta data - - Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost - bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) - Please don't be angwy, wittle movie-web ish twying so hard. - Can you find it in your heart to forgive? UwU 💖 - + {t("notFound.badge")} + {t("notFound.title")} + {t("notFound.message")} diff --git a/src/pages/parts/home/BookmarksPart.tsx b/src/pages/parts/home/BookmarksPart.tsx index c75b374d..848e1aa5 100644 --- a/src/pages/parts/home/BookmarksPart.tsx +++ b/src/pages/parts/home/BookmarksPart.tsx @@ -46,7 +46,7 @@ export function BookmarksPart() { return (
diff --git a/src/pages/parts/home/HeroPart.tsx b/src/pages/parts/home/HeroPart.tsx index f3eceef2..07cef8ec 100644 --- a/src/pages/parts/home/HeroPart.tsx +++ b/src/pages/parts/home/HeroPart.tsx @@ -31,7 +31,7 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) { if (hour < 12) time = "morning"; else if (hour < 19) time = "day"; - const title = t(`search.title.${time}`); + const title = t(`home.titles.${time}`); return ( @@ -51,9 +51,7 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) { onChange={setSearch} value={search} onUnFocus={setSearchUnFocus} - placeholder={ - t("search.placeholder") || "What do you want to watch?" - } + placeholder={t("home.search.placeholder")} />
diff --git a/src/pages/parts/home/WatchingPart.tsx b/src/pages/parts/home/WatchingPart.tsx index 9e69da4e..ee1b1c37 100644 --- a/src/pages/parts/home/WatchingPart.tsx +++ b/src/pages/parts/home/WatchingPart.tsx @@ -44,7 +44,7 @@ export function WatchingPart() { return (
diff --git a/src/pages/parts/migrations/MigrationPart.tsx b/src/pages/parts/migrations/MigrationPart.tsx index fcf7ccbf..6c2c0bc6 100644 --- a/src/pages/parts/migrations/MigrationPart.tsx +++ b/src/pages/parts/migrations/MigrationPart.tsx @@ -1,8 +1,11 @@ +import { useTranslation } from "react-i18next"; + import { BrandPill } from "@/components/layout/BrandPill"; import { Loading } from "@/components/layout/Loading"; import { BlurEllipsis } from "@/pages/layouts/SubPageLayout"; export function MigrationPart() { + const { t } = useTranslation(); return (
{/* Overlaid elements */} @@ -14,8 +17,7 @@ export function MigrationPart() { {/* Content */}

- Please hold, we are migrating your data. This shouldn't take long. - Also, fuck you. + {t("screens.migration.inProgress")}

); diff --git a/src/pages/parts/player/MetaPart.tsx b/src/pages/parts/player/MetaPart.tsx index 291cba9a..2e8ea13e 100644 --- a/src/pages/parts/player/MetaPart.tsx +++ b/src/pages/parts/player/MetaPart.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { useHistory, useParams } from "react-router-dom"; import { useAsync } from "react-use"; import type { AsyncReturnType } from "type-fest"; @@ -18,6 +19,7 @@ export interface MetaPartProps { } export function MetaPart(props: MetaPartProps) { + const { t } = useTranslation(); const params = useParams<{ media: string; episode?: string; @@ -70,21 +72,18 @@ export function MetaPart(props: MetaPartProps) { return ( - Failed to load - Failed to load meta data - - Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost - bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) - Please don't be angwy, wittle movie-web ish twying so hard. Can - you find it in your heart to forgive? UwU 💖 - + + {t("player.metadata.failed.badge")} + + {t("player.metadata.failed.title")} + {t("player.metadata.failed.text")} @@ -95,21 +94,18 @@ export function MetaPart(props: MetaPartProps) { return ( - Not found - This media doesnt exist - - Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost - bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) - Please don't be angwy, wittle movie-web ish twying so hard. Can - you find it in your heart to forgive? UwU 💖 - + + {t("player.metadata.notFound.badge")} + + {t("player.metadata.notFound.title")} + {t("player.metadata.notFound.text")} diff --git a/src/pages/parts/player/PlaybackErrorPart.tsx b/src/pages/parts/player/PlaybackErrorPart.tsx index b8d2fd76..03bf1e26 100644 --- a/src/pages/parts/player/PlaybackErrorPart.tsx +++ b/src/pages/parts/player/PlaybackErrorPart.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from "react-i18next"; + import { Button } from "@/components/buttons/Button"; import { Icons } from "@/components/Icon"; import { IconPill } from "@/components/layout/IconPill"; @@ -9,26 +11,22 @@ import { usePlayerStore } from "@/stores/player/store"; import { ErrorCard } from "../errors/ErrorCard"; export function PlaybackErrorPart() { + const { t } = useTranslation(); const playbackError = usePlayerStore((s) => s.interface.error); return ( - Not found - Goo goo gaa gaa - - Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost - bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) - Please don't be angwy, wittle movie-web ish twying so hard. Can - you find it in your heart to forgive? UwU 💖 - + {t("player.playbackError.badge")} + {t("player.playbackError.title")} + {t("player.playbackError.text")} diff --git a/src/pages/parts/player/ScrapeErrorPart.tsx b/src/pages/parts/player/ScrapeErrorPart.tsx index f32f3bd1..632f9280 100644 --- a/src/pages/parts/player/ScrapeErrorPart.tsx +++ b/src/pages/parts/player/ScrapeErrorPart.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { Button } from "@/components/buttons/Button"; import { Icons } from "@/components/Icon"; @@ -18,6 +19,7 @@ export interface ScrapeErrorPartProps { } export function ScrapeErrorPart(props: ScrapeErrorPartProps) { + const { t } = useTranslation(); const error = useMemo(() => { const data = props.data; const amountError = Object.values(data.sources).filter( @@ -36,21 +38,18 @@ export function ScrapeErrorPart(props: ScrapeErrorPartProps) { return ( - Not found - Goo goo gaa gaa - - Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost - bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) - Please don't be angwy, wittle movie-web ish twying so hard. Can - you find it in your heart to forgive? UwU 💖 - + + {t("player.scraping.notFound.badge")} + + {t("player.scraping.notFound.title")} + {t("player.scraping.notFound.text")} diff --git a/src/pages/parts/search/SearchListPart.tsx b/src/pages/parts/search/SearchListPart.tsx index 5fff2484..270d05f1 100644 --- a/src/pages/parts/search/SearchListPart.tsx +++ b/src/pages/parts/search/SearchListPart.tsx @@ -30,9 +30,9 @@ function SearchSuffix(props: { failed?: boolean; results?: number }) { {!props.failed ? (
{(props.results ?? 0) > 0 ? ( -

{t("search.allResults")}

+

{t("home.search.allResults")}

) : ( -

{t("search.noResults")}

+

{t("home.search.noResults")}

)}
) : null} @@ -40,7 +40,7 @@ function SearchSuffix(props: { failed?: boolean; results?: number }) { {/* Error result */} {props.failed ? (
-

{t("search.allFailed")}

+

{t("home.search.failed")}

) : null}
@@ -72,7 +72,7 @@ export function SearchListPart({ searchQuery }: { searchQuery: string }) { {results.length > 0 ? (
diff --git a/src/pages/parts/settings/AccountActionsPart.tsx b/src/pages/parts/settings/AccountActionsPart.tsx index b1a6df65..e19aa9e9 100644 --- a/src/pages/parts/settings/AccountActionsPart.tsx +++ b/src/pages/parts/settings/AccountActionsPart.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; import { deleteUser } from "@/backend/accounts/user"; @@ -10,6 +11,7 @@ import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; import { useAuthStore } from "@/stores/auth"; export function AccountActionsPart() { + const { t } = useTranslation(); const url = useBackendUrl(); const account = useAuthStore((s) => s.account); const { logout } = useAuthData(); @@ -26,16 +28,15 @@ export function AccountActionsPart() { return (
- Actions + {t("settings.account.actions.title")}
- Delete account + {t("settings.account.actions.delete.title")}

- This action is irreversible. All data will be deleted and nothing - can be recovered. + {t("settings.account.actions.delete.text")}

@@ -44,23 +45,24 @@ export function AccountActionsPart() { loading={deleteResult.loading} onClick={deleteModal.show} > - Delete account + {t("settings.account.actions.delete.button")}
- Are you sure? + + {t("settings.account.actions.delete.confirmTitle")} + - Are you sure you want to delete your account? All your data will be - lost! + {t("settings.account.actions.delete.confirmDescription")} diff --git a/src/pages/parts/settings/AccountEditPart.tsx b/src/pages/parts/settings/AccountEditPart.tsx index 7c8e4549..31cd7375 100644 --- a/src/pages/parts/settings/AccountEditPart.tsx +++ b/src/pages/parts/settings/AccountEditPart.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from "react-i18next"; + import { Avatar } from "@/components/Avatar"; import { Button } from "@/components/buttons/Button"; import { Icon, Icons } from "@/components/Icon"; @@ -18,6 +20,7 @@ export function AccountEditPart(props: { userIcon: UserIcons; setUserIcon: (s: UserIcons) => void; }) { + const { t } = useTranslation(); const { logout } = useAuth(); const profileEditModal = useModal("profile-edit"); @@ -50,7 +53,7 @@ export function AccountEditPart(props: { onClick={profileEditModal.show} > - Edit + {t("settings.account.accountDetails.editProfile")} } /> @@ -58,14 +61,20 @@ export function AccountEditPart(props: {
props.setDeviceName(value)} />
diff --git a/src/pages/parts/settings/CaptionsPart.tsx b/src/pages/parts/settings/CaptionsPart.tsx index 19867304..0f37a7da 100644 --- a/src/pages/parts/settings/CaptionsPart.tsx +++ b/src/pages/parts/settings/CaptionsPart.tsx @@ -1,5 +1,6 @@ import classNames from "classnames"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { Icon, Icons } from "@/components/Icon"; import { @@ -19,6 +20,7 @@ export function CaptionPreview(props: { styling: SubtitleStyling; onToggle: () => void; }) { + const { t } = useTranslation(); return (
@@ -66,15 +68,16 @@ export function CaptionsPart(props: { styling: SubtitleStyling; setStyling: (s: SubtitleStyling) => void; }) { + const { t } = useTranslation(); const [fullscreenPreview, setFullscreenPreview] = useState(false); return (
- Captions + {t("settings.captions.title")}
@@ -84,7 +87,7 @@ export function CaptionsPart(props: { textTransformer={(s) => `${s}%`} /> `${s}%`} @@ -94,7 +97,9 @@ export function CaptionsPart(props: { value={props.styling.size * 100} />
- Color + + {t("settings.captions.colorLabel")} +
{colors.map((v) => (