chore: more localisation

This commit is contained in:
Lewis Crichton 2024-06-09 10:59:03 +01:00
parent 22e5396684
commit a4d4d981e0
No known key found for this signature in database
10 changed files with 215 additions and 131 deletions

View file

@ -10,6 +10,7 @@ usageMatchRegex:
refactorTemplates: refactorTemplates:
- "$t(\"$1\")" - "$t(\"$1\")"
- "{$t(\"$1\")}"
- "<Translate i18nKey=\"$1\" />" - "<Translate i18nKey=\"$1\" />"
monopoly: true monopoly: true

View file

@ -24,6 +24,7 @@ import { proxyLazy } from "@utils/lazy";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc"; import { classes, isObjectEmpty } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
import { $t } from "@utils/translation";
import { OptionType, Plugin } from "@utils/types"; import { OptionType, Plugin } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
@ -129,7 +130,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
function renderSettings() { function renderSettings() {
if (!hasSettings || !plugin.options) { if (!hasSettings || !plugin.options) {
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>; return <Forms.FormText>{$t("vencord.noSettings")}</Forms.FormText>;
} else { } else {
const options = Object.entries(plugin.options).map(([key, setting]) => { const options = Object.entries(plugin.options).map(([key, setting]) => {
if (setting.hidden) return null; if (setting.hidden) return null;
@ -227,7 +228,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
</div> </div>
)} )}
<Forms.FormSection className={Margins.bottom16}> <Forms.FormSection className={Margins.bottom16}>
<Forms.FormTitle tag="h3">Settings</Forms.FormTitle> <Forms.FormTitle tag="h3">{$t("vencord.settings")}</Forms.FormTitle>
{renderSettings()} {renderSettings()}
</Forms.FormSection> </Forms.FormSection>
</ModalContent> </ModalContent>
@ -242,7 +243,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
> >
Cancel Cancel
</Button> </Button>
<Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}> <Tooltip text={$t("vencord.settingsErrors")} shouldShow={!canSubmit()}>
{({ onMouseEnter, onMouseLeave }) => ( {({ onMouseEnter, onMouseLeave }) => (
<Button <Button
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
@ -252,12 +253,12 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
disabled={!canSubmit()} disabled={!canSubmit()}
> >
Save & Close {$t("vencord.saveAndClose")}
</Button> </Button>
)} )}
</Tooltip> </Tooltip>
</Flex> </Flex>
{saveError && <Text variant="text-md/semibold" style={{ color: "var(--text-danger)" }}>Error while saving: {saveError}</Text>} {saveError && <Text variant="text-md/semibold" style={{ color: "var(--text-danger)" }}>{$t("vencord.settingsSaveError", { saveError })}</Text>}
</Flex> </Flex>
</ModalFooter>} </ModalFooter>}
</ModalRoot> </ModalRoot>

View file

@ -33,6 +33,7 @@ import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc"; import { classes, isObjectEmpty } from "@utils/misc";
import { openModalLazy } from "@utils/modal"; import { openModalLazy } from "@utils/modal";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { lowercaseify } from "@utils/text";
import { $t } from "@utils/translation"; import { $t } from "@utils/translation";
import { Plugin } from "@utils/types"; import { Plugin } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
@ -66,19 +67,19 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
<Card className={cl("info-card", { "restart-card": required })}> <Card className={cl("info-card", { "restart-card": required })}>
{required ? ( {required ? (
<> <>
<Forms.FormTitle tag="h5">Restart required!</Forms.FormTitle> <Forms.FormTitle tag="h5">{$t("vencord.pluginHeader.reloadHeader")}</Forms.FormTitle>
<Forms.FormText className={cl("dep-text")}> <Forms.FormText className={cl("dep-text")}>
Restart now to apply new plugins and their settings {$t("vencord.pluginHeader.reloadDescription")}
</Forms.FormText> </Forms.FormText>
<Button onClick={() => location.reload()}> <Button onClick={() => location.reload()}>
Restart {$t("vencord.pluginHeader.restart")}
</Button> </Button>
</> </>
) : ( ) : (
<> <>
<Forms.FormTitle tag="h5">Plugin Management</Forms.FormTitle> <Forms.FormTitle tag="h5">{$t("vencord.pluginHeader.managementHeader")}</Forms.FormTitle>
<Forms.FormText>Press the cog wheel or info icon to get more info on a plugin</Forms.FormText> <Forms.FormText>{$t("vencord.pluginHeader.iconInformation")}</Forms.FormText>
<Forms.FormText>Plugins with a cog wheel have settings you can modify!</Forms.FormText> <Forms.FormText>{$t("vencord.pluginHeader.cogWheel")}</Forms.FormText>
</> </>
)} )}
</Card> </Card>
@ -153,7 +154,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
return ( return (
<AddonCard <AddonCard
name={plugin.name} name={plugin.name}
description={plugin.description} description={$t(`${lowercaseify(plugin.name)}.description`)}
isNew={isNew} isNew={isNew}
enabled={isEnabled()} enabled={isEnabled()}
setEnabled={toggleEnabled} setEnabled={toggleEnabled}
@ -184,10 +185,10 @@ export default function PluginSettings() {
React.useEffect(() => { React.useEffect(() => {
return () => void (changes.hasChanges && Alerts.show({ return () => void (changes.hasChanges && Alerts.show({
title: "Restart required", title: $t("vencord.restartRequired"),
body: ( body: (
<> <>
<p>The following plugins require a restart:</p> <p>$t("vencord.pluginsNeedRestart")</p>
<div>{changes.map((s, i) => ( <div>{changes.map((s, i) => (
<> <>
{i > 0 && ", "} {i > 0 && ", "}
@ -196,8 +197,8 @@ export default function PluginSettings() {
))}</div> ))}</div>
</> </>
), ),
confirmText: "Restart now", confirmText: $t("vencord.restartNow"),
cancelText: "Later!", cancelText: $t("vencord.restartLater"),
onConfirm: () => location.reload() onConfirm: () => location.reload()
})); }));
}, []); }, []);
@ -303,7 +304,7 @@ export default function PluginSettings() {
} }
} else { } else {
plugins = requiredPlugins = <Text variant="text-md/normal">No plugins meet search criteria.</Text>; plugins = requiredPlugins = <Text variant="text-md/normal">{$t("vencord.noSearchResults")}</Text>;
} }
return ( return (
@ -311,18 +312,18 @@ export default function PluginSettings() {
<ReloadRequiredCard required={changes.hasChanges} /> <ReloadRequiredCard required={changes.hasChanges} />
<Forms.FormTitle tag="h5" className={classes(Margins.top20, Margins.bottom8)}> <Forms.FormTitle tag="h5" className={classes(Margins.top20, Margins.bottom8)}>
Filters {$t("vencord.pluginFilters")}
</Forms.FormTitle> </Forms.FormTitle>
<div className={cl("filter-controls")}> <div className={cl("filter-controls")}>
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} className={Margins.bottom20} /> <TextInput autoFocus value={searchValue.value} placeholder={$t("vencord.search.placeholder")} onChange={onSearch} className={Margins.bottom20} />
<div className={InputStyles.inputWrapper}> <div className={InputStyles.inputWrapper}>
<Select <Select
options={[ options={[
{ label: "Show All", value: SearchStatus.ALL, default: true }, { label: $t("vencord.search.all"), value: SearchStatus.ALL, default: true },
{ label: "Show Enabled", value: SearchStatus.ENABLED }, { label: $t("vencord.search.enabled"), value: SearchStatus.ENABLED },
{ label: "Show Disabled", value: SearchStatus.DISABLED }, { label: $t("vencord.search.disabled"), value: SearchStatus.DISABLED },
{ label: "Show New", value: SearchStatus.NEW } { label: $t("vencord.search.new"), value: SearchStatus.NEW }
]} ]}
serialize={String} serialize={String}
select={onStatusChange} select={onStatusChange}
@ -332,7 +333,7 @@ export default function PluginSettings() {
</div> </div>
</div> </div>
<Forms.FormTitle className={Margins.top20}>Plugins</Forms.FormTitle> <Forms.FormTitle className={Margins.top20}>{$t("vencord.plugins")}</Forms.FormTitle>
<div className={cl("grid")}> <div className={cl("grid")}>
{plugins} {plugins}
@ -341,7 +342,7 @@ export default function PluginSettings() {
<Forms.FormDivider className={Margins.top20} /> <Forms.FormDivider className={Margins.top20} />
<Forms.FormTitle tag="h5" className={classes(Margins.top20, Margins.bottom8)}> <Forms.FormTitle tag="h5" className={classes(Margins.top20, Margins.bottom8)}>
Required Plugins {$t("vencord.requiredPlugins")}
</Forms.FormTitle> </Forms.FormTitle>
<div className={cl("grid")}> <div className={cl("grid")}>
{requiredPlugins} {requiredPlugins}
@ -353,7 +354,7 @@ export default function PluginSettings() {
function makeDependencyList(deps: string[]) { function makeDependencyList(deps: string[]) {
return ( return (
<React.Fragment> <React.Fragment>
<Forms.FormText>This plugin is required by:</Forms.FormText> <Forms.FormText>{$t("vencord.pluginRequiredBy")}</Forms.FormText>
{deps.map((dep: string) => <Forms.FormText className={cl("dep-text")}>{dep}</Forms.FormText>)} {deps.map((dep: string) => <Forms.FormText className={cl("dep-text")}>{dep}</Forms.FormText>)}
</React.Fragment> </React.Fragment>
); );

View file

@ -20,6 +20,7 @@ import { Flex } from "@components/Flex";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync"; import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync";
import { $t } from "@utils/translation";
import { Button, Card, Text } from "@webpack/common"; import { Button, Card, Text } from "@webpack/common";
import { SettingsTab, wrapTab } from "./shared"; import { SettingsTab, wrapTab } from "./shared";
@ -29,21 +30,19 @@ function BackupRestoreTab() {
<SettingsTab title="Backup & Restore"> <SettingsTab title="Backup & Restore">
<Card className={classes("vc-settings-card", "vc-backup-restore-card")}> <Card className={classes("vc-settings-card", "vc-backup-restore-card")}>
<Flex flexDirection="column"> <Flex flexDirection="column">
<strong>Warning</strong> <strong>{$t("vencord.warning")}</strong>
<span>Importing a settings file will overwrite your current settings.</span> <span>{$t("vencord.backupAndRestore.importWarning")}</span>
</Flex> </Flex>
</Card> </Card>
<Text variant="text-md/normal" className={Margins.bottom8}> <Text variant="text-md/normal" className={Margins.bottom8}>
You can import and export your Vencord settings as a JSON file. {$t("vencord.backupAndRestore.description")}
This allows you to easily transfer your settings to another device,
or recover your settings after reinstalling Vencord or Discord.
</Text> </Text>
<Text variant="text-md/normal" className={Margins.bottom8}> <Text variant="text-md/normal" className={Margins.bottom8}>
Settings Export contains: {$t("vencord.backupAndRestore.exportContains")}
<ul> <ul>
<li>&mdash; Custom QuickCSS</li> <li>&mdash; {$t("vencord.backupAndRestore.customQuickcss")}</li>
<li>&mdash; Theme Links</li> <li>&mdash; {$t("vencord.backupAndRestore.themeLinks")}</li>
<li>&mdash; Plugin Settings</li> <li>&mdash; {$t("vencord.backupAndRestore.pluginSettings")}</li>
</ul> </ul>
</Text> </Text>
<Flex> <Flex>
@ -51,13 +50,13 @@ function BackupRestoreTab() {
onClick={() => uploadSettingsBackup()} onClick={() => uploadSettingsBackup()}
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
> >
Import Settings {$t("vencord.backupAndRestore.importSettings")}
</Button> </Button>
<Button <Button
onClick={downloadSettingsBackup} onClick={downloadSettingsBackup}
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
> >
Export Settings {$t("vencord.backupAndRestore.exportSettings")}
</Button> </Button>
</Flex> </Flex>
</SettingsTab> </SettingsTab>

View file

@ -23,6 +23,7 @@ import { Link } from "@components/Link";
import { authorizeCloud, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud"; import { authorizeCloud, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync"; import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync";
import { $t, Translate } from "@utils/translation";
import { Alerts, Button, Forms, Switch, Tooltip } from "@webpack/common"; import { Alerts, Button, Forms, Switch, Tooltip } from "@webpack/common";
import { SettingsTab, wrapTab } from "./shared"; import { SettingsTab, wrapTab } from "./shared";
@ -45,8 +46,8 @@ async function eraseAllData() {
if (!res.ok) { if (!res.ok) {
cloudLogger.error(`Failed to erase data, API returned ${res.status}`); cloudLogger.error(`Failed to erase data, API returned ${res.status}`);
showNotification({ showNotification({
title: "Cloud Integrations", title: $t("vencord.cloudIntegrations"),
body: `Could not erase all data (API returned ${res.status}), please contact support.`, body: $t("vencord.cloud.integrations.eraseError", { status: res.status }),
color: "var(--red-360)" color: "var(--red-360)"
}); });
return; return;
@ -56,8 +57,8 @@ async function eraseAllData() {
await deauthorizeCloud(); await deauthorizeCloud();
showNotification({ showNotification({
title: "Cloud Integrations", title: $t("vencord.cloudIntegrations"),
body: "Successfully erased all data.", body: $t("vencord.cloud.integrations.eraseSuccess"),
color: "var(--green-360)" color: "var(--green-360)"
}); });
} }
@ -69,8 +70,7 @@ function SettingsSyncSection() {
return ( return (
<Forms.FormSection title="Settings Sync" className={Margins.top16}> <Forms.FormSection title="Settings Sync" className={Margins.top16}>
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}> <Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
Synchronize your settings to the cloud. This allows easy synchronization across multiple devices with {$t("vencord.cloud.settings.description")}
minimal effort.
</Forms.FormText> </Forms.FormText>
<Switch <Switch
key="cloud-sync" key="cloud-sync"
@ -78,15 +78,15 @@ function SettingsSyncSection() {
value={cloud.settingsSync} value={cloud.settingsSync}
onChange={v => { cloud.settingsSync = v; }} onChange={v => { cloud.settingsSync = v; }}
> >
Settings Sync {$t("vencord.settingsSync")}
</Switch> </Switch>
<div className="vc-cloud-settings-sync-grid"> <div className="vc-cloud-settings-sync-grid">
<Button <Button
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
disabled={!sectionEnabled} disabled={!sectionEnabled}
onClick={() => putCloudSettings(true)} onClick={() => putCloudSettings(true)}
>Sync to Cloud</Button> >{$t("vencord.cloud.settings.syncToCloud")}</Button>
<Tooltip text="This will overwrite your local settings with the ones on the cloud. Use wisely!"> <Tooltip text={$t("vencord.cloud.settings.overwriteWarning")}>
{({ onMouseLeave, onMouseEnter }) => ( {({ onMouseLeave, onMouseEnter }) => (
<Button <Button
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
@ -95,7 +95,7 @@ function SettingsSyncSection() {
color={Button.Colors.RED} color={Button.Colors.RED}
disabled={!sectionEnabled} disabled={!sectionEnabled}
onClick={() => getCloudSettings(true, true)} onClick={() => getCloudSettings(true, true)}
>Sync from Cloud</Button> >{$t("vencord.cloud.settings.syncFromCloud")}</Button>
)} )}
</Tooltip> </Tooltip>
<Button <Button
@ -103,7 +103,7 @@ function SettingsSyncSection() {
color={Button.Colors.RED} color={Button.Colors.RED}
disabled={!sectionEnabled} disabled={!sectionEnabled}
onClick={() => deleteCloudSettings()} onClick={() => deleteCloudSettings()}
>Delete Cloud Settings</Button> >{$t("vencord.cloud.settings.deleteCloudSettings")}</Button>
</div> </div>
</Forms.FormSection> </Forms.FormSection>
); );
@ -116,22 +116,22 @@ function CloudTab() {
<SettingsTab title="Vencord Cloud"> <SettingsTab title="Vencord Cloud">
<Forms.FormSection title="Cloud Settings" className={Margins.top16}> <Forms.FormSection title="Cloud Settings" className={Margins.top16}>
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}> <Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
Vencord comes with a cloud integration that adds goodies like settings sync across devices. <Translate i18nKey="vencord.cloud.integrations.description">
It <Link href="https://vencord.dev/cloud/privacy">respects your privacy</Link>, and <Link href="https://vencord.dev/cloud/privacy" />
the <Link href="https://github.com/Vencord/Backend">source code</Link> is AGPL 3.0 licensed so you <Link href="https://github.com/Vencord/Backend" />
can host it yourself. </Translate>
</Forms.FormText> </Forms.FormText>
<Switch <Switch
key="backend" key="backend"
value={settings.cloud.authenticated} value={settings.cloud.authenticated}
onChange={v => { v && authorizeCloud(); if (!v) settings.cloud.authenticated = v; }} onChange={v => { v && authorizeCloud(); if (!v) settings.cloud.authenticated = v; }}
note="This will request authorization if you have not yet set up cloud integrations." note={$t("vencord.cloud.integrations.authorizationNote")}
> >
Enable Cloud Integrations {$t("vencord.cloud.integrations.enable")}
</Switch> </Switch>
<Forms.FormTitle tag="h5">Backend URL</Forms.FormTitle> <Forms.FormTitle tag="h5">{$t("vencord.cloud.integrations.backendUrl")}</Forms.FormTitle>
<Forms.FormText className={Margins.bottom8}> <Forms.FormText className={Margins.bottom8}>
Which backend to use when using cloud integrations. {$t("vencord.cloud.integrations.backendNote")}
</Forms.FormText> </Forms.FormText>
<CheckedTextInput <CheckedTextInput
key="backendUrl" key="backendUrl"
@ -145,14 +145,14 @@ function CloudTab() {
color={Button.Colors.RED} color={Button.Colors.RED}
disabled={!settings.cloud.authenticated} disabled={!settings.cloud.authenticated}
onClick={() => Alerts.show({ onClick={() => Alerts.show({
title: "Are you sure?", title: $t("vencord.areYouSure"),
body: "Once your data is erased, we cannot recover it. There's no going back!", body: $t("vencord.cloud.integrations.eraseWarning"),
onConfirm: eraseAllData, onConfirm: eraseAllData,
confirmText: "Erase it!", confirmText: $t("vencord.cloud.integrations.eraseIt"),
confirmColor: "vc-cloud-erase-data-danger-btn", confirmColor: "vc-cloud-erase-data-danger-btn",
cancelText: "Nevermind" cancelText: $t("vencord.nevermind")
})} })}
>Erase All Data</Button> >{$t("vencord.cloud.integrations.eraseAllData")}</Button>
<Forms.FormDivider className={Margins.top16} /> <Forms.FormDivider className={Margins.top16} />
</Forms.FormSection > </Forms.FormSection >
<SettingsSyncSection /> <SettingsSyncSection />

View file

@ -29,6 +29,7 @@ import { classes } from "@utils/misc";
import { openModal } from "@utils/modal"; import { openModal } from "@utils/modal";
import { showItemInFolder } from "@utils/native"; import { showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { $t } from "@utils/translation";
import { findByPropsLazy, findLazy } from "@webpack"; import { findByPropsLazy, findLazy } from "@webpack";
import { Button, Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; import { Button, Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import type { ComponentType, Ref, SyntheticEvent } from "react"; import type { ComponentType, Ref, SyntheticEvent } from "react";
@ -202,17 +203,17 @@ function ThemesTab() {
return ( return (
<> <>
<Card className="vc-settings-card"> <Card className="vc-settings-card">
<Forms.FormTitle tag="h5">Find Themes:</Forms.FormTitle> <Forms.FormTitle tag="h5">{$t("vencord.themes.findThemes")}</Forms.FormTitle>
<div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}> <div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}>
<Link style={{ marginRight: ".5em" }} href="https://betterdiscord.app/themes"> <Link style={{ marginRight: ".5em" }} href="https://betterdiscord.app/themes">
BetterDiscord Themes {$t("vencord.themes.betterDiscord")}
</Link> </Link>
<Link href="https://github.com/search?q=discord+theme">GitHub</Link> <Link href="https://github.com/search?q=discord+theme">GitHub</Link>
</div> </div>
<Forms.FormText>If using the BD site, click on "Download" and place the downloaded .theme.css file into your themes folder.</Forms.FormText> <Forms.FormText>{$t("vencord.themes.betterDiscordNote")}</Forms.FormText>
</Card> </Card>
<Forms.FormSection title="Local Themes"> <Forms.FormSection title={$t("vencord.themes.local")}>
<Card className="vc-settings-quick-actions-card"> <Card className="vc-settings-quick-actions-card">
<> <>
{IS_WEB ? {IS_WEB ?
@ -221,7 +222,7 @@ function ThemesTab() {
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
disabled={themeDirPending} disabled={themeDirPending}
> >
Upload Theme {$t("vencord.themes.upload")}
<FileInput <FileInput
ref={fileInputRef} ref={fileInputRef}
onChange={onFileUpload} onChange={onFileUpload}
@ -235,20 +236,20 @@ function ThemesTab() {
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
disabled={themeDirPending} disabled={themeDirPending}
> >
Open Themes Folder {$t("vencord.themes.openFolder")}
</Button> </Button>
)} )}
<Button <Button
onClick={refreshLocalThemes} onClick={refreshLocalThemes}
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
> >
Load missing Themes {$t("vencord.themes.loadMissing")}
</Button> </Button>
<Button <Button
onClick={() => VencordNative.quickCss.openEditor()} onClick={() => VencordNative.quickCss.openEditor()}
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
> >
Edit QuickCSS {$t("vencord.themes.editQuickCss")}
</Button> </Button>
{Vencord.Settings.plugins.ClientTheme.enabled && ( {Vencord.Settings.plugins.ClientTheme.enabled && (
@ -262,7 +263,7 @@ function ThemesTab() {
))} ))}
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
> >
Edit ClientTheme {$t("clientTheme.edit")}
</Button> </Button>
)} )}
</> </>
@ -303,12 +304,12 @@ function ThemesTab() {
return ( return (
<> <>
<Card className="vc-settings-card vc-text-selectable"> <Card className="vc-settings-card vc-text-selectable">
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle> <Forms.FormTitle tag="h5">{$t("vencord.themes.pasteLinks")}</Forms.FormTitle>
<Forms.FormText>One link per line</Forms.FormText> <Forms.FormText>{$t("vencord.themes.oneLinkPerLine")}</Forms.FormText>
<Forms.FormText>Make sure to use direct links to files (raw or github.io)!</Forms.FormText> <Forms.FormText>{$t("vencord.themes.useDirect")}</Forms.FormText>
</Card> </Card>
<Forms.FormSection title="Online Themes" tag="h5"> <Forms.FormSection title={$t("vencord.themes.online")} tag="h5">
<TextArea <TextArea
value={themeText} value={themeText}
onChange={setThemeText} onChange={setThemeText}
@ -337,13 +338,13 @@ function ThemesTab() {
className="vc-settings-tab-bar-item" className="vc-settings-tab-bar-item"
id={ThemeTab.LOCAL} id={ThemeTab.LOCAL}
> >
Local Themes {$t("vencord.themes.local")}
</TabBar.Item> </TabBar.Item>
<TabBar.Item <TabBar.Item
className="vc-settings-tab-bar-item" className="vc-settings-tab-bar-item"
id={ThemeTab.ONLINE} id={ThemeTab.ONLINE}
> >
Online Themes {$t("vencord.themes.online")}
</TabBar.Item> </TabBar.Item>
</TabBar> </TabBar>

View file

@ -37,6 +37,8 @@ export const wordsToPascal = (words: string[]) =>
export const wordsToTitle = (words: string[]) => export const wordsToTitle = (words: string[]) =>
words.map(w => w[0].toUpperCase() + w.slice(1)).join(" "); words.map(w => w[0].toUpperCase() + w.slice(1)).join(" ");
export const lowercaseify = (text: string) => text[0].toLowerCase() + text.slice(1);
const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"] as const; const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"] as const;
type Units = typeof units[number]; type Units = typeof units[number];

View file

@ -0,0 +1,4 @@
{
"description": "Recreation of the old client theme experiment. Add a color to your Discord client theme",
"edit": "Edit ClientTheme"
}

View file

@ -0,0 +1,3 @@
{
"description": "Assists with translating Vencord"
}

View file

@ -1,58 +1,130 @@
{ {
"ohNo": "Oh no!", "areYouSure": "Are you sure?",
"errorBoundaryDescription": "An error occurred while rendering this Component. More info can be found below and in your console.", "backupAndRestore": {
"failureUpdate": "Uh Oh! Failed to render this Page. However, there is an update available that might fix it. Would you like to update and restart now?", "customQuickcss": "Custom QuickCSS",
"pluginRestart": "Restart to apply changes!", "description": "You can import and export your Vencord settings as a JSON file. This allows you to easily transfer your settings to another device, or recover your settings after reinstalling Vencord or Discord.",
"requiredPlugin": "This plugin is required for Vencord to function.", "exportContains": "Settings Export contains:",
"new": "NEW", "exportSettings": "Export Settings",
"pluginContributed": { "importSettings": "Import Settings",
"one": "This person has <0>contributed</0> to one plugin!", "importWarning": "Importing a settings file will overwrite your current settings.",
"other": "This person has <0>contributed</0> to {count} plugins!", "pluginSettings": "Plugin Settings",
"zero": "This person has not made any plugins. They likely <0>contributed</0> to Vencord in other ways!" "themeLinks": "Theme Links"
},
"clipboardNotSupported": "Your browser does not support copying to clipboard",
"cloud": {
"integrations": {
"authorizationNote": "This will request authorization if you have not yet set up cloud integrations.",
"backendNote": "Which backend to use when using cloud integrations.",
"backendUrl": "Backend URL",
"description": "Vencord comes with a cloud integration that adds goodies like settings sync across devices. It <0>respects your privacy</0>, and the <1>source code</1> is AGPL 3.0 licensed so you can host it yourself.",
"enable": "Enable Cloud Integrations",
"enabled": "Cloud integrations enabled!",
"eraseAllData": "Erase All Data",
"eraseError": "Could not erase all data (API returned {status}), please contact support.",
"eraseIt": "Erase it!",
"eraseSuccess": "Successfully erased all data.",
"eraseWarning": "Once your data is erased, we cannot recover it. There's no going back!",
"reauthenticate": "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will need to re-authenticate to continue using them. Click here to go to the settings page to do so!",
"setupFailure": {
"generic": "Setup failed ({error}).",
"missingSecret": "Setup failed (no secret returned?).",
"oauth": "Setup failed (couldn't retrieve OAuth configuration)."
}
}, },
"update": { "settings": {
"available": "A Vencord update is available!", "deleteCloudSettings": "Delete Cloud Settings",
"clickToRestart": "Click here to restart", "deleteError": {
"clickToView": "Click here to view the update", "api": "Could not delete settings (API returned {status}).",
"updated": "Vencord has been updated!" "generic": "Could not delete settings ({error})."
}, },
"cloudIntegrations": "Cloud Integrations", "deleted": "Settings deleted from cloud!",
"cloudSettings": "Cloud Settings", "description": "Synchronize your settings to the cloud. This allows easy synchronization across multiple devices with minimal effort.",
"cloud": { "localNewer": "Your local settings are newer than the cloud ones.",
"integrations": { "nothingOnline": "There are no settings in the cloud.",
"enabled": "Cloud integrations enabled!", "overwriteWarning": "This will overwrite your local settings with the ones on the cloud. Use wisely!",
"reauthenticate": "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will need to re-authenticate to continue using them. Click here to go to the settings page to do so!", "syncErrorDown": {
"setupFailure": { "api": "Could not synchronize settings from the cloud (API returned {status}).",
"generic": "Setup failed ({error}).", "generic": "Could not synchronize settings from the cloud ({error})."
"missingSecret": "Setup failed (no secret returned?).", },
"oauth": "Setup failed (couldn't retrieve OAuth configuration)." "syncErrorUp": {
} "api": "Could not synchronize settings to the cloud (API returned {status}).",
}, "generic": "Could not synchronize settings to the cloud ({error})."
"settings": { },
"deleteError": { "syncFromCloud": "Sync from Cloud",
"api": "Could not delete settings (API returned {status}).", "syncSuccess": "Synchronized settings to the cloud!",
"generic": "Could not delete settings ({error})." "syncToCloud": "Sync to Cloud",
}, "upToDate": "Your settings are up to date.",
"deleted": "Settings deleted from cloud!", "updated": "Your settings have been updated! Click here to restart to fully apply changes!"
"localNewer": "Your local settings are newer than the cloud ones.", }
"nothingOnline": "There are no settings in the cloud.", },
"syncErrorDown": { "cloudIntegrations": "Cloud Integrations",
"api": "Could not synchronize settings from the cloud (API returned {status}).", "cloudSettings": "Cloud Settings",
"generic": "Could not synchronize settings from the cloud ({error})." "copiedToClipboard": "Copied to clipboard!",
}, "errorBoundaryDescription": "An error occurred while rendering this Component. More info can be found below and in your console.",
"syncErrorUp": { "failedToImport": "Failed to import settings: {error}",
"api": "Could not synchronize settings to the cloud (API returned {status}).", "failureUpdate": "Uh Oh! Failed to render this Page. However, there is an update available that might fix it. Would you like to update and restart now?",
"generic": "Could not synchronize settings to the cloud ({error})." "gitCopyNewer": "Your local copy has more recent commits. Please stash or reset them.",
}, "importedSettings": "Settings successfully imported. Restart to apply changes!",
"syncSuccess": "Synchronized settings to the cloud!", "nevermind": "Nevermind",
"upToDate": "Your settings are up to date.", "new": "NEW",
"updated": "Your settings have been updated! Click here to restart to fully apply changes!" "noSearchResults": "No plugins meet search criteria.",
} "noSettings": "There are no settings for this plugin.",
}, "ohNo": "Oh no!",
"copiedToClipboard": "Copied to clipboard!", "pluginContributed": {
"clipboardNotSupported": "Your browser does not support copying to clipboard", "one": "This person has <0>contributed</0> to one plugin!",
"failedToImport": "Failed to import settings: {error}", "other": "This person has <0>contributed</0> to {count} plugins!",
"importedSettings": "Settings successfully imported. Restart to apply changes!", "zero": "This person has not made any plugins. They likely <0>contributed</0> to Vencord in other ways!"
"updaterRepeatFailed": "That also failed :( Try updating or re-installing with the installer!", },
"gitCopyNewer": "Your local copy has more recent commits. Please stash or reset them." "pluginFilters": "Filters",
"pluginHeader": {
"cogWheel": "Plugins with a cog wheel have settings you can modify!",
"iconInformation": "Press the cog wheel or info icon to get more info on a plugin.",
"managementHeader": "Plugin Management",
"reloadDescription": "Restart now to apply new plugins and their settings",
"reloadHeader": "Restart required!",
"restart": "Restart"
},
"pluginRequiredBy": "This plugin is required by:",
"pluginRestart": "Restart to apply changes!",
"plugins": "Plugins",
"pluginsNeedRestart": "The following plugins require a restart:",
"requiredPlugin": "This plugin is required for Vencord to function.",
"requiredPlugins": "Required Plugins",
"restartLater": "Later!",
"restartNow": "Restart now",
"restartRequired": "Restart required",
"saveAndClose": "Save & Close",
"search": {
"all": "Show All",
"disabled": "Show Disabled",
"enabled": "Show Enabled",
"new": "Show New",
"placeholder": "Search for a plugin..."
},
"settings": "Settings",
"settingsErrors": "You must fix all errors before saving",
"settingsSaveError": "Error while saving: {saveError}",
"settingsSync": "Settings Sync",
"themes": {
"betterDiscord": "BetterDiscord Themes",
"betterDiscordNote": "If using the BD site, click on \"Download\" and place the downloaded .theme.css file into your themes folder.",
"editQuickCss": "Edit QuickCSS",
"findThemes": "Find Themes:",
"loadMissing": "Load missing Themes",
"local": "Local Themes",
"oneLinkPerLine": "One link per line.",
"online": "Online Themes",
"openFolder": "Open Themes Folder",
"pasteLinks": "Paste links to css files here",
"upload": "Upload Theme",
"useDirect": "Make sure to use direct links to files (raw or github.io)!"
},
"update": {
"available": "A Vencord update is available!",
"clickToRestart": "Click here to restart",
"clickToView": "Click here to view the update",
"updated": "Vencord has been updated!"
},
"updaterRepeatFailed": "That also failed :( Try updating or re-installing with the installer!",
"warning": "Warning"
} }