diff --git a/.vscode/settings.json b/.vscode/settings.json index 87978a345..840011518 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,5 +24,6 @@ "i18n-ally.localesPaths": ["./translations"], "i18n-ally.sourceLanguage": "en", "i18n-ally.extract.keygenStyle": "camelCase", - "i18n-ally.sortKeys": true + "i18n-ally.sortKeys": true, + "i18n-ally.keystyle": "nested" } diff --git a/src/Vencord.ts b/src/Vencord.ts index c4c6d4705..973033a23 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -38,6 +38,7 @@ import { patches, PMLogger, startAllPlugins } from "./plugins"; import { localStorage } from "./utils/localStorage"; import { relaunch } from "./utils/native"; import { getCloudSettings, putCloudSettings } from "./utils/settingsSync"; +import { $t } from "./utils/translation"; import { checkForUpdates, update, UpdateLogger } from "./utils/updater"; import { onceReady } from "./webpack"; import { SettingsRouter } from "./webpack/common"; @@ -54,9 +55,8 @@ async function syncSettings() { ) { // show a notification letting them know and tell them how to fix it showNotification({ - title: "Cloud Integrations", - body: "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!", + title: $t("vencord.utils.cloud.integrations.title"), + body: $t("vencord.utils.cloud.integrations.reauthenticate"), color: "var(--yellow-360)", onClick: () => SettingsRouter.open("VencordCloud") }); @@ -76,8 +76,8 @@ async function syncSettings() { // there was an error to notify the user, but besides that we only want to show one notification instead of all // of the possible ones it has (such as when your settings are newer). showNotification({ - title: "Cloud Settings", - body: "Your settings have been updated! Click here to restart to fully apply changes!", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.updated"), color: "var(--green-360)", onClick: relaunch }); @@ -100,8 +100,8 @@ async function init() { await update(); if (Settings.autoUpdateNotification) setTimeout(() => showNotification({ - title: "Vencord has been updated!", - body: "Click here to restart", + title: $t("vencord.update.updated"), + body: $t("vencord.update.clickToRestart"), permanent: true, noPersist: true, onClick: relaunch @@ -110,8 +110,8 @@ async function init() { } setTimeout(() => showNotification({ - title: "A Vencord update is available!", - body: "Click here to view the update", + title: $t("vencord.update.available"), + body: $t("vencord.update.clickToView"), permanent: true, noPersist: true, onClick: openUpdaterModal! diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index ea2e02b51..6a492cf8e 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -19,6 +19,7 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { LazyComponent } from "@utils/react"; +import { $t } from "@utils/translation"; import { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -85,11 +86,11 @@ const ErrorBoundary = LazyComponent(() => { {...this.state} />; - const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console."; + const msg = this.props.message || $t("vencord.components.errorBoundary.error"); return ( -

Oh no!

+

{$t("vencord.components.errorBoundary.ohNo")}

{msg}

{this.state.message} diff --git a/src/components/VencordSettings/AddonCard.tsx b/src/components/VencordSettings/AddonCard.tsx index 1161a6411..774cf39fa 100644 --- a/src/components/VencordSettings/AddonCard.tsx +++ b/src/components/VencordSettings/AddonCard.tsx @@ -21,6 +21,7 @@ import "./addonCard.css"; import { classNameFactory } from "@api/Styles"; import { Badge } from "@components/Badge"; import { Switch } from "@components/Switch"; +import { $t } from "@utils/translation"; import { Text, useRef } from "@webpack/common"; import type { MouseEventHandler, ReactNode } from "react"; @@ -67,7 +68,7 @@ export function AddonCard({ disabled, isNew, name, infoButton, footer, author, e > {name} - {isNew && } + {isNew && } {!!author && ( diff --git a/src/components/handleComponentFailed.ts b/src/components/handleComponentFailed.ts index 43a3ad8b3..7e818c02e 100644 --- a/src/components/handleComponentFailed.ts +++ b/src/components/handleComponentFailed.ts @@ -16,12 +16,11 @@ * along with this program. If not, see . */ +import { $t } from "@utils/translation"; import { maybePromptToUpdate } from "@utils/updater"; export function handleComponentFailed() { maybePromptToUpdate( - "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?" + $t("vencord.components.componentFailed") ); } diff --git a/src/utils/cloud.tsx b/src/utils/cloud.tsx index 508b1c7ef..feb3f2dad 100644 --- a/src/utils/cloud.tsx +++ b/src/utils/cloud.tsx @@ -23,6 +23,7 @@ import { OAuth2AuthorizeModal, UserStore } from "@webpack/common"; import { Logger } from "./Logger"; import { openModal } from "./modal"; +import { $t } from "./translation"; export const cloudLogger = new Logger("Cloud", "#39b7e0"); export const getCloudUrl = () => new URL(Settings.cloud.url); @@ -83,8 +84,8 @@ export async function authorizeCloud() { var { clientId, redirectUri } = await oauthConfiguration.json(); } catch { showNotification({ - title: "Cloud Integration", - body: "Setup failed (couldn't retrieve OAuth configuration)." + title: $t("vencord.utils.cloud.integrations.title"), + body: $t("vencord.utils.cloud.integrations.setupFailure.oauth") }); Settings.cloud.authenticated = false; return; @@ -113,22 +114,22 @@ export async function authorizeCloud() { cloudLogger.info("Authorized with secret"); await setAuthorization(secret); showNotification({ - title: "Cloud Integration", - body: "Cloud integrations enabled!" + title: $t("vencord.utils.cloud.integrations.title"), + body: $t("vencord.utils.cloud.integrations.enabled") }); Settings.cloud.authenticated = true; } else { showNotification({ - title: "Cloud Integration", - body: "Setup failed (no secret returned?)." + title: $t("vencord.utils.cloud.integrations.title"), + body: $t("vencord.utils.cloud.integrations.setupFailure.missingSecret") }); Settings.cloud.authenticated = false; } } catch (e: any) { cloudLogger.error("Failed to authorize", e); showNotification({ - title: "Cloud Integration", - body: `Setup failed (${e.toString()}).` + title: $t("vencord.utils.cloud.integrations.title"), + body: $t("vencord.utils.cloud.integrations.setupFailure.generic", { error: e.toString() }) }); Settings.cloud.authenticated = false; } diff --git a/src/utils/misc.tsx b/src/utils/misc.tsx index fb08c93f6..4077fe576 100644 --- a/src/utils/misc.tsx +++ b/src/utils/misc.tsx @@ -19,6 +19,7 @@ import { Clipboard, Toasts } from "@webpack/common"; import { DevsById } from "./constants"; +import { $t } from "./translation"; /** * Calls .join(" ") on the arguments @@ -35,11 +36,13 @@ export function sleep(ms: number): Promise { return new Promise(r => setTimeout(r, ms)); } -export function copyWithToast(text: string, toastMessage = "Copied to clipboard!") { +export function copyWithToast(text: string, toastMessage?: string) { + toastMessage ??= $t("vencord.utils.misc.clipboard.copied"); + if (Clipboard.SUPPORTS_COPY) { Clipboard.copy(text); } else { - toastMessage = "Your browser does not support copying to clipboard"; + toastMessage = $t("vencord.utils.misc.clipboard.noSupport"); } Toasts.show({ message: toastMessage, diff --git a/src/utils/settingsSync.ts b/src/utils/settingsSync.ts index f19928ac4..81ca92bfc 100644 --- a/src/utils/settingsSync.ts +++ b/src/utils/settingsSync.ts @@ -24,6 +24,7 @@ import { deflateSync, inflateSync } from "fflate"; import { getCloudAuth, getCloudUrl } from "./cloud"; import { Logger } from "./Logger"; import { relaunch } from "./native"; +import { $t } from "./translation"; import { chooseFile, saveFile } from "./web"; export async function importSettings(data: string) { @@ -68,10 +69,10 @@ const toast = (type: number, message: string) => }); const toastSuccess = () => - toast(Toasts.Type.SUCCESS, "Settings successfully imported. Restart to apply changes!"); + toast(Toasts.Type.SUCCESS, $t("vencord.utils.settingsSync.imported")); const toastFailure = (err: any) => - toast(Toasts.Type.FAILURE, `Failed to import settings: ${String(err)}`); + toast(Toasts.Type.FAILURE, $t("vencord.utils.settingsSync.failedToImport", { error: String(err) })); export async function uploadSettingsBackup(showToast = true): Promise { if (IS_DISCORD_DESKTOP) { @@ -128,8 +129,8 @@ export async function putCloudSettings(manual?: boolean) { if (!res.ok) { cloudSettingsLogger.error(`Failed to sync up, API returned ${res.status}`); showNotification({ - title: "Cloud Settings", - body: `Could not synchronize settings to cloud (API returned ${res.status}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.syncErrorUp.api", { status: res.status.toString() }), color: "var(--red-360)" }); return; @@ -143,16 +144,16 @@ export async function putCloudSettings(manual?: boolean) { if (manual) { showNotification({ - title: "Cloud Settings", - body: "Synchronized settings to the cloud!", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.syncSuccess"), noPersist: true, }); } } catch (e: any) { cloudSettingsLogger.error("Failed to sync up", e); showNotification({ - title: "Cloud Settings", - body: `Could not synchronize settings to the cloud (${e.toString()}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.syncErrorUp.generic", { error: e.toString() }), color: "var(--red-360)" }); } @@ -173,8 +174,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { cloudSettingsLogger.info("No settings on the cloud"); if (shouldNotify) showNotification({ - title: "Cloud Settings", - body: "There are no settings in the cloud.", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.nothingOnline"), noPersist: true }); return false; @@ -184,8 +185,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { cloudSettingsLogger.info("Settings up to date"); if (shouldNotify) showNotification({ - title: "Cloud Settings", - body: "Your settings are up to date.", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.upToDate"), noPersist: true }); return false; @@ -194,8 +195,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { if (!res.ok) { cloudSettingsLogger.error(`Failed to sync down, API returned ${res.status}`); showNotification({ - title: "Cloud Settings", - body: `Could not synchronize settings from the cloud (API returned ${res.status}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.syncErrorDown.api", { status: res.status.toString() }), color: "var(--red-360)" }); return false; @@ -208,8 +209,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { if (!force && written < localWritten) { if (shouldNotify) showNotification({ - title: "Cloud Settings", - body: "Your local settings are newer than the cloud ones.", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.localNewer"), noPersist: true, }); return; @@ -227,8 +228,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { cloudSettingsLogger.info("Settings loaded from cloud successfully"); if (shouldNotify) showNotification({ - title: "Cloud Settings", - body: "Your settings have been updated! Click here to restart to fully apply changes!", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.updated"), color: "var(--green-360)", onClick: IS_WEB ? () => location.reload() : relaunch, noPersist: true @@ -238,8 +239,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) { } catch (e: any) { cloudSettingsLogger.error("Failed to sync down", e); showNotification({ - title: "Cloud Settings", - body: `Could not synchronize settings from the cloud (${e.toString()}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.syncErrorDown.generic", { error: e.toString() }), color: "var(--red-360)" }); @@ -257,8 +258,8 @@ export async function deleteCloudSettings() { if (!res.ok) { cloudSettingsLogger.error(`Failed to delete, API returned ${res.status}`); showNotification({ - title: "Cloud Settings", - body: `Could not delete settings (API returned ${res.status}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.deleteError.api", { error: res.status.toString() }), color: "var(--red-360)" }); return; @@ -266,15 +267,15 @@ export async function deleteCloudSettings() { cloudSettingsLogger.info("Settings deleted from cloud successfully"); showNotification({ - title: "Cloud Settings", - body: "Settings deleted from cloud!", + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.deleted"), color: "var(--green-360)" }); } catch (e: any) { cloudSettingsLogger.error("Failed to delete", e); showNotification({ - title: "Cloud Settings", - body: `Could not delete settings (${e.toString()}).`, + title: $t("vencord.utils.cloud.settings.title"), + body: $t("vencord.utils.cloud.settings.deleteError.generic", { error: e.toString() }), color: "var(--red-360)" }); } diff --git a/src/utils/updater.ts b/src/utils/updater.ts index f99c6ca11..26d7e25f9 100644 --- a/src/utils/updater.ts +++ b/src/utils/updater.ts @@ -20,6 +20,7 @@ import gitHash from "~git-hash"; import { Logger } from "./Logger"; import { relaunch } from "./native"; +import { $t } from "./translation"; import { IpcRes } from "./types"; export const UpdateLogger = /* #__PURE__*/ new Logger("Updater", "white"); @@ -70,7 +71,7 @@ export async function maybePromptToUpdate(confirmMessage: string, checkForDev = const isOutdated = await checkForUpdates(); if (isOutdated) { const wantsUpdate = confirm(confirmMessage); - if (wantsUpdate && isNewer) return alert("Your local copy has more recent commits. Please stash or reset them."); + if (wantsUpdate && isNewer) return alert($t("vencord.utils.updater.gitNewer")); if (wantsUpdate) { await update(); relaunch(); @@ -78,6 +79,6 @@ export async function maybePromptToUpdate(confirmMessage: string, checkForDev = } } catch (err) { UpdateLogger.error(err); - alert("That also failed :( Try updating or re-installing with the installer!"); + alert($t("vencord.utils.updater.failed")); } } diff --git a/translations/en/vencord.json b/translations/en/vencord.json index 3ba54e915..3c24ed09b 100644 --- a/translations/en/vencord.json +++ b/translations/en/vencord.json @@ -1,7 +1,69 @@ { - "hello": "Hello {name}!", - "plural": { - "one": "One thing.", - "other": "{count} things." + "components": { + "componentFailed": "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?", + "errorBoundary": { + "error": "An error occurred while rendering this Component. More info can be found below and in your console.", + "ohNo": "Oh no!" + }, + "vencordSettings": { + "addonCard": { + "new": "NEW" + } } + }, + "update": { + "available": "A Vencord update is available!", + "clickToRestart": "Click here to restart", + "clickToView": "Click here to view the update", + "updated": "Vencord has been updated!" + }, + "utils": { + "cloud": { + "integrations": { + "enabled": "Cloud integrations enabled!", + "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)." + }, + "title": "Cloud Integrations" + }, + "settings": { + "deleteError": { + "api": "Could not delete settings (API returned {status}).", + "generic": "Could not delete settings ({error})." + }, + "deleted": "Settings deleted from cloud!", + "localNewer": "Your local settings are newer than the cloud ones.", + "nothingOnline": "There are no settings in the cloud.", + "syncErrorDown": { + "api": "Could not synchronize settings from the cloud (API returned {status}).", + "generic": "Could not synchronize settings from the cloud ({error})." + }, + "syncErrorUp": { + "api": "Could not synchronize settings to the cloud (API returned {status}).", + "generic": "Could not synchronize settings to the cloud ({error})." + }, + "syncSuccess": "Synchronized settings to the cloud!", + "title": "Cloud Settings", + "upToDate": "Your settings are up to date.", + "updated": "Your settings have been updated! Click here to restart to fully apply changes!" + } + }, + "misc": { + "clipboard": { + "copied": "Copied to clipboard!", + "noSupport": "Your browser does not support copying to clipboard" + } + }, + "settingsSync": { + "failedToImport": "Failed to import settings: {error}", + "imported": "Settings successfully imported. Restart to apply changes!" + }, + "updater": { + "failed": "That also failed :( Try updating or re-installing with the installer!", + "gitNewer": "Your local copy has more recent commits. Please stash or reset them." + } + } }