From 5b71aae159a185486b534b3588c7be9f4071e25f Mon Sep 17 00:00:00 2001 From: mrjvs Date: Sun, 26 Nov 2023 16:33:04 +0100 Subject: [PATCH] localize part of settings page --- src/assets/locales/en.json | 77 ++++++++++++++++ src/pages/About.tsx | 89 ++----------------- src/pages/Dmca.tsx | 5 +- src/pages/Settings.tsx | 8 +- src/pages/parts/settings/DeviceListPart.tsx | 13 ++- src/pages/parts/settings/LocalePart.tsx | 15 ++-- src/pages/parts/settings/ProfileEditModal.tsx | 16 ++-- .../parts/settings/RegisterCalloutPart.tsx | 9 +- src/pages/parts/settings/SidebarPart.tsx | 71 +++++++++++---- src/pages/parts/settings/ThemePart.tsx | 22 +++-- 10 files changed, 195 insertions(+), 130 deletions(-) diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 5f50f7a2..7e7e236c 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -27,6 +27,10 @@ }, "migration": { "failed": "Failed to migrate your data." + }, + "dmca": { + "title": "", + "text": "" } }, "navigation": { @@ -44,6 +48,79 @@ "actions": { "copy": "Copy" }, + "settings": { + "unsaved": "You have unsaved changes", + "reset": "Reset", + "save": "Save", + "sidebar": { + "info": { + "title": "App information", + "hostname": "Hostname", + "backendUrl": "Backend URL", + "userId": "User ID", + "notLoggedIn": "Not logged in", + "appVersion": "App version", + "backendVersion": "Backend version", + "unknownVersion": "Unknown", + "secure": "Secure", + "insecure": "Insecure" + } + }, + "appearance": { + "title": "Appearance", + "activeTheme": "Active", + "themes": { + "default": "Default", + "blue": "Blue", + "teal": "Teal", + "red": "Red", + "gray": "Gray" + } + }, + "account": { + "title": "Account", + "register": { + "title": "Sync to the cloud", + "text": "Instantly share your watch progress between devices and keep them synced.", + "cta": "Get started" + }, + "profile": { + "title": "Edit profile picture", + "firstColor": "First color", + "secondColor": "Second color", + "userIcon": "User icon", + "finish": "Finish editing" + }, + "devices": { + "title": "Devices", + "failed": "Failed to load sessions", + "deviceNameLabel": "Device name", + "removeDevice": "Remove" + } + }, + "locale": { + "title": "Locale", + "language": "Application language", + "languageDescription": "Language applied to the entire application." + }, + "captions": { + "title": "Captions" + }, + "connections": { + "title": "Connections" + } + }, + "faq": { + "title": "About us", + "q1": { + "title": "1", + "body": "Body of 1" + }, + "how": { + "title": "1", + "body": "Body of 1" + } + }, "footer": { "tagline": "Watch your favorite shows and movies with this open source streaming app.", "links": { diff --git a/src/pages/About.tsx b/src/pages/About.tsx index c720186d..c57ed706 100644 --- a/src/pages/About.tsx +++ b/src/pages/About.tsx @@ -1,4 +1,4 @@ -/* eslint-disable react/no-unescaped-entities */ +import { useTranslation } from "react-i18next"; import { ThinContainer } from "@/components/layout/ThinContainer"; import { Ol } from "@/components/utils/Ol"; @@ -16,95 +16,18 @@ function Question(props: { title: string; children: React.ReactNode }) { } export function AboutPage() { + const { t } = useTranslation(); return ( - About us + {t("faq.title")}
    - Blue, oh so blue, like the tranquil sky on a summer's day. It's - the color of calm and serenity, a gentle embrace for your senses. - When you think of blue, you think of the vast ocean stretching - endlessly, inviting you to dive deep into its azure depths. - , - - Blue is the color of dreams, where the world slows down, and you - can hear the whispers of the wind in the tall grass. It's a - symphony of peacefulness that resonates with your soul, like a - melody that lingers in your heart. - , - - Blue, like, it's totally, um, the essence of like, everything, you - know? It's like, you look at it, and it's like, it's there, but - it's also not there, and it's like, you're trying to grasp the - concept of blue, but it's like trying to catch a dream in a net - made of spaghetti, you know? It's like, it's the ultimate paradox, - and it's like, it's just blowing your mind, man, like, it's like - trying to find the meaning of life in a jar of peanut butter, but - the peanut butter is made of pure energy, man, and it's like, - whoa. - , + {t("faq.q1.body")}, ]} /> - - Blue, oh so blue, like the tranquil sky on a summer's day. It's the - color of calm and serenity, a gentle embrace for your senses. When you - think of blue, you think of the vast ocean stretching endlessly, - inviting you to dive deep into its azure depths. Blue is the color of - dreams, where the world slows down, and you can hear the whispers of - the wind in the tall grass. It's a symphony of peacefulness that - resonates with your soul, like a melody that lingers in your heart. - - How does it work? - - Blue, well, it's like this cosmic wavelength, man, and it's like the - universe is just vibin', you know? It's like, when you stare at the - blue, it's like you're staring at the secrets of the cosmos, like, - whoa, it's like a trippy trip to another dimension where time doesn't - even matter, and you're just floating in a sea of, like, blue, man. - And it's like, it's not just a color, it's a whole experience, like, - you're in this cosmic rollercoaster ride through the quantum soup of - existence, and you're just riding the blue wave, man. - - - Blue, like, it's totally, um, the essence of like, everything, you - know? It's like, you look at it, and it's like, it's there, but it's - also not there, and it's like, you're trying to grasp the concept of - blue, but it's like trying to catch a dream in a net made of - spaghetti, you know? It's like, it's the ultimate paradox, and it's - like, it's just blowing your mind, man, like, it's like trying to find - the meaning of life in a jar of peanut butter, but the peanut butter - is made of pure energy, man, and it's like, whoa. - - Frequently asked questions - - Blue, blue, b-b-b-bluuuuuueeeeeeeee, zippity zappity zoooooo, it's - like, you know, it's like, blue is like, um, you know, it's like, um, - like a thing, but it's also not a thing, and it's like, whoa, dude, - it's like, it's like trying to juggle invisible watermelons while - riding a unicycle made of rubber bands and ketchup, and it's like, - you're just floating in the cosmic jellyfish of existence, and the - jellyfish are like, playing the accordion, man, and it's like, the - accordion is made of, like, spaghetti and, like, um, interdimensional - cheese, and it's like, whoa, dude, like, whoa. - - - Bloo-bloo-bloo, bleepity-bloop, blibber-blabber, blarble-blurble, blue - is like, um, you know, flibberflabberfloober, like, - zoomity-zamity-zoom, and it's like, um, sproingity-sproing, like, uh, - gibber-gabber-gobblygook, you know, it's like, um, - jibber-jabber-jibberish, like, whatchamacallit, thingamajig, - doodad-doodad-dingdong, like, ploopity-ploop, um, blibbity-blam, - flibbity-floo, like, gobbledygook-gobbledygook, - whoopsy-daisy-dingleberry, and it's like, uh, - flibberflabberflooberzoomity-sproing, um, like, - blibber-gibber-jibber-jabber, thingamajig-whatchamacallit, like, you - know, thingamajig-doodad-doodledee, and it's like, um, - doodad-gobbledygook-doodley-doo, like, - ploopity-whoopsy-doodleberry-flibber, you know, it's like, uh, blue, - man, like, totally, um, blue. - + {t("faq.how.title")} + {t("faq.how.body")} ); diff --git a/src/pages/Dmca.tsx b/src/pages/Dmca.tsx index c74ee85a..883af44f 100644 --- a/src/pages/Dmca.tsx +++ b/src/pages/Dmca.tsx @@ -7,14 +7,15 @@ import { Heading1, Paragraph } from "@/components/utils/Text"; import { SubPageLayout } from "./layouts/SubPageLayout"; +// TODO make email a constant export function DmcaPage() { const { t } = useTranslation(); return ( - {t("dmca.title")} - {t("dmca.description")} + {t("screens.dmca.title")} + {t("screens.dmca.text")} dmca@movie-web.app diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index cb0677f1..30ed4e5e 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -1,5 +1,6 @@ import classNames from "classnames"; import { useCallback, useEffect, useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; import { @@ -96,6 +97,7 @@ export function AccountSettings(props: { } export function SettingsPage() { + const { t } = useTranslation(); const activeTheme = useThemeStore((s) => s.theme); const setTheme = useThemeStore((s) => s.setTheme); @@ -244,21 +246,21 @@ export function SettingsPage() { state.changed ? "opacity-100" : "opacity-0" }`} > -

    You have unsaved changes

    +

    {t("settings.unsaved")}

    diff --git a/src/pages/parts/settings/DeviceListPart.tsx b/src/pages/parts/settings/DeviceListPart.tsx index 2dfbea45..afaea589 100644 --- a/src/pages/parts/settings/DeviceListPart.tsx +++ b/src/pages/parts/settings/DeviceListPart.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; import { SessionResponse } from "@/backend/accounts/auth"; @@ -18,6 +19,7 @@ export function Device(props: { isCurrent?: boolean; onRemove?: () => void; }) { + const { t } = useTranslation(); const url = useBackendUrl(); const token = useAuthStore((s) => s.account?.token); const [result, exec] = useAsyncFn(async () => { @@ -32,12 +34,14 @@ export function Device(props: { paddingClass="px-6 py-4" >
    - Device name + + {t("settings.account.devices.deviceNameLabel")} +

    {props.name}

    {!props.isCurrent ? ( ) : null} @@ -50,6 +54,7 @@ export function DeviceListPart(props: { sessions: SessionResponse[]; onChange?: () => void; }) { + const { t } = useTranslation(); const seed = useAuthStore((s) => s.account?.seed); const sessions = props.sessions; const currentSessionId = useAuthStore((s) => s.account?.sessionId); @@ -75,10 +80,10 @@ export function DeviceListPart(props: { return (
    - Devices + {t("settings.account.devices.title")} {props.error ? ( -

    Failed to load sessions

    +

    {t("settings.account.devices.failed")}

    ) : props.loading ? ( ) : ( diff --git a/src/pages/parts/settings/LocalePart.tsx b/src/pages/parts/settings/LocalePart.tsx index 9443e802..ef9801b9 100644 --- a/src/pages/parts/settings/LocalePart.tsx +++ b/src/pages/parts/settings/LocalePart.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from "react-i18next"; + import { FlagIcon } from "@/components/FlagIcon"; import { Dropdown } from "@/components/form/Dropdown"; import { Heading1 } from "@/components/utils/Text"; @@ -8,7 +10,8 @@ export function LocalePart(props: { language: string; setLanguage: (l: string) => void; }) { - const sorted = sortLangCodes(appLanguageOptions.map((t) => t.code)); + const { t } = useTranslation(); + const sorted = sortLangCodes(appLanguageOptions.map((item) => item.code)); const options = appLanguageOptions .sort((a, b) => sorted.indexOf(a.code) - sorted.indexOf(b.code)) @@ -18,14 +21,16 @@ export function LocalePart(props: { leftIcon: , })); - const selected = options.find((t) => t.id === props.language); + const selected = options.find((item) => item.id === props.language); return (
    - Locale -

    Application language

    + {t("settings.locale.title")} +

    + {t("settings.locale.language")} +

    - Language applied to the entire application. + {t("settings.locale.languageDescription")}

    - Edit profile picture + + {t("settings.account.profile.title")} +
    diff --git a/src/pages/parts/settings/RegisterCalloutPart.tsx b/src/pages/parts/settings/RegisterCalloutPart.tsx index e34951f1..126fc05a 100644 --- a/src/pages/parts/settings/RegisterCalloutPart.tsx +++ b/src/pages/parts/settings/RegisterCalloutPart.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { useHistory } from "react-router-dom"; import { Button } from "@/components/buttons/Button"; @@ -6,6 +7,7 @@ import { Heading3 } from "@/components/utils/Text"; export function RegisterCalloutPart() { const history = useHistory(); + const { t } = useTranslation(); return (
    @@ -14,15 +16,14 @@ export function RegisterCalloutPart() { className="grid grid-cols-2 gap-12 mt-5" >
    - Sync to the cloud + {t("settings.account.register.title")}

    - Instantly share your watch progress between devices and keep them - synced. + {t("settings.account.register.text")}

    diff --git a/src/pages/parts/settings/SidebarPart.tsx b/src/pages/parts/settings/SidebarPart.tsx index c85b3b6b..47bc2bbe 100644 --- a/src/pages/parts/settings/SidebarPart.tsx +++ b/src/pages/parts/settings/SidebarPart.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import Sticky from "react-sticky-el"; import { useAsync } from "react-use"; @@ -14,16 +15,22 @@ import { useAuthStore } from "@/stores/auth"; const rem = 16; function SecureBadge(props: { url: string }) { + const { t } = useTranslation(); const secure = props.url.startsWith("https://"); return (
    - Secure + {t( + secure + ? "settings.sidebar.info.secure" + : "settings.sidebar.info.insecure" + )}
    ); } export function SidebarPart() { + const { t } = useTranslation(); const { isMobile } = useIsMobile(); const { account } = useAuthStore(); // eslint-disable-next-line no-restricted-globals @@ -31,11 +38,31 @@ export function SidebarPart() { const [activeLink, setActiveLink] = useState(""); const settingLinks = [ - { text: "Account", id: "settings-account", icon: Icons.USER }, - { text: "Locale", id: "settings-locale", icon: Icons.BOOKMARK }, - { text: "Appearance", id: "settings-appearance", icon: Icons.GITHUB }, - { text: "Captions", id: "settings-captions", icon: Icons.CAPTIONS }, - { text: "Connections", id: "settings-connection", icon: Icons.LINK }, + { + textKey: "settings.account.title", + id: "settings-account", + icon: Icons.USER, + }, + { + textKey: "settings.locale.title", + id: "settings-locale", + icon: Icons.BOOKMARK, + }, + { + textKey: "settings.appearance.title", + id: "settings-appearance", + icon: Icons.GITHUB, + }, + { + textKey: "settings.captions.title", + id: "settings-captions", + icon: Icons.CAPTIONS, + }, + { + textKey: "settings.connections.title", + id: "settings-connection", + icon: Icons.LINK, + }, ]; const backendUrl = useBackendUrl(); @@ -103,24 +130,29 @@ export function SidebarPart() { onClick={() => scrollTo(v.id)} key={v.id} > - {v.text} + {t(v.textKey)} ))}
    - +
    {/* Hostname */}
    -

    Hostname

    +

    + {t("settings.sidebar.info.hostname")} +

    {hostname}

    {/* Backend URL */}

    - Backend URL + {t("settings.sidebar.info.backendUrl")}

    @@ -130,13 +162,19 @@ export function SidebarPart() { {/* User ID */}

    -

    User ID

    -

    {account?.userId ?? "Not logged in"}

    +

    + {t("settings.sidebar.info.userId")} +

    +

    + {account?.userId ?? t("settings.sidebar.info.notLoggedIn")} +

    {/* App version */}
    -

    App version

    +

    + {t("settings.sidebar.info.appVersion")} +

    {conf().APP_VERSION}

    @@ -144,7 +182,9 @@ export function SidebarPart() { {/* Backend version */}
    -

    Backend version

    +

    + {t("settings.sidebar.info.backendVersion")} +

    {backendMeta.error ? ( ) : ( - backendMeta?.value?.version || "Unknown" + backendMeta?.value?.version || + t("settings.sidebar.info.unknownVersion") )}

    diff --git a/src/pages/parts/settings/ThemePart.tsx b/src/pages/parts/settings/ThemePart.tsx index 3bb73899..38725ba0 100644 --- a/src/pages/parts/settings/ThemePart.tsx +++ b/src/pages/parts/settings/ThemePart.tsx @@ -1,4 +1,5 @@ import classNames from "classnames"; +import { useTranslation } from "react-i18next"; import { Icon, Icons } from "@/components/Icon"; import { Heading1 } from "@/components/utils/Text"; @@ -6,19 +7,19 @@ import { Heading1 } from "@/components/utils/Text"; const availableThemes = [ { id: "blue", - name: "Blue", + key: "settings.themes.blue", }, { id: "teal", - name: "Teal", + key: "settings.themes.teal", }, { id: "red", - name: "Red", + key: "settings.themes.red", }, { id: "gray", - name: "Gray", + key: "settings.themes.gray", }, ]; @@ -28,6 +29,8 @@ function ThemePreview(props: { name: string; onClick?: () => void; }) { + const { t } = useTranslation(); + return (
    {/* Mini movie-web. So Kawaiiiii! */} - {/* ^ can we keep this comment in forever please? - Jip */}
    {/* Background color */} @@ -106,7 +108,7 @@ function ThemePreview(props: { props.active ? "opacity-100" : "opacity-0 pointer-events-none" )} > - Active + {t("settings.appearance.activeTheme")}
    @@ -117,13 +119,15 @@ export function ThemePart(props: { active: string | null; setTheme: (theme: string | null) => void; }) { + const { t } = useTranslation(); + return (
    - Appearance + {t("settings.appearance.title")}
    {/* default theme */} props.setTheme(null)} @@ -132,7 +136,7 @@ export function ThemePart(props: { props.setTheme(v.id)} />