Merge branch 'translation' into l10n_translation

This commit is contained in:
thororen1234 2026-02-15 10:10:55 -05:00
commit f79c5fd566
No known key found for this signature in database
303 changed files with 4613 additions and 1784 deletions

1330
i18n/en-US/equicord.json Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -21,8 +21,8 @@ import "./AddonCard.css";
import { Badge } from "@components/Badge";
import { BaseText } from "@components/BaseText";
import { Switch } from "@components/Switch";
import { t } from "@utils/translation";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import { Tooltip, useRef } from "@webpack/common";
import type { MouseEventHandler, ReactNode } from "react";

View file

@ -35,19 +35,20 @@ export function resolveError(isValidResult: boolean | string) {
interface SettingsSectionProps extends PropsWithChildren {
name: string;
description: string;
description: string | (() => string);
error?: string | null;
inlineSetting?: boolean;
tag?: "label" | "div";
}
export function SettingsSection({ tag: Tag = "div", name, description, error, inlineSetting, children }: SettingsSectionProps) {
const resolvedDescription = typeof description === "function" ? description() : description;
return (
<Tag className={cl("section")}>
<div className={classes(cl("content"), inlineSetting && cl("inline"))}>
<div className={cl("label")}>
{name && <BaseText className={cl("title")} size="md" weight="medium">{wordsToTitle(wordsFromCamel(name))}</BaseText>}
{description && <BaseText className={cl("description")} size="sm">{description}</BaseText>}
{resolvedDescription && <BaseText className={cl("description")} size="sm">{resolvedDescription}</BaseText>}
</div>
{children}
</div>

View file

@ -10,6 +10,7 @@ import { HeadingSecondary } from "@components/Heading";
import { Paragraph } from "@components/Paragraph";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ApplicationAssetUtils, FluxDispatcher, showToast } from "@webpack/common";
@ -51,15 +52,15 @@ interface MediaData {
const settings = definePluginSettings({
serverUrl: {
description: "AudioBookShelf server URL (e.g., https://abs.example.com)",
description: t("absRPC.settings.serverUrl"),
type: OptionType.STRING,
},
username: {
description: "AudioBookShelf username",
description: t("absRPC.settings.username"),
type: OptionType.STRING,
},
password: {
description: "AudioBookShelf password",
description: t("absRPC.settings.password"),
type: OptionType.STRING,
},
});
@ -85,16 +86,16 @@ function setActivity(activity: Activity | null) {
export default definePlugin({
name: "AudioBookShelfRichPresence",
description: "Rich presence for AudioBookShelf media server",
description: t("absRPC.description"),
authors: [EquicordDevs.vmohammad],
settingsAboutComponent: () => (
<>
<HeadingSecondary>How to connect to AudioBookShelf</HeadingSecondary>
<HeadingSecondary>{t("absRPC.about.title")}</HeadingSecondary>
<Paragraph>
Enter your AudioBookShelf server URL, username, and password to display your currently playing audiobooks as Discord Rich Presence.
{t("absRPC.about.description")}
<br /><br />
The plugin will automatically authenticate and fetch your listening progress.
{t("absRPC.about.automaticAuth")}
</Paragraph>
</>
),
@ -114,7 +115,7 @@ export default definePlugin({
async authenticate(): Promise<boolean> {
if (!settings.store.serverUrl || !settings.store.username || !settings.store.password) {
logger.warn("AudioBookShelf server URL, username, or password is not set in settings.");
showToast("AudioBookShelf RPC is not configured.", "failure", {
showToast(t("absRPC.toasts.notConfigured"), "failure", {
duration: 15000,
});
return false;

View file

@ -5,11 +5,12 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "AltKrispSwitch",
description: "Makes the Noise Suppression Popout switch between None and Krisp instead of Krisp and Standard",
description: t("altKrispSwitch.description"),
authors: [Devs.newwares],
patches: [
{

View file

@ -6,11 +6,12 @@
import { Devs } from "@utils/constants";
import { openUserProfile } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "AlwaysExpandProfiles",
description: "Always expands profile popouts to the full modal",
description: t("alwaysExpandProfiles.description"),
authors: [Devs.thororen],
patches: [
{

View file

@ -6,50 +6,51 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
export const settings = definePluginSettings({
dms: {
type: OptionType.BOOLEAN,
default: true,
description: "Remove shops above DMs list",
description: t("anammox.settings.dms"),
restartNeeded: true,
},
quests: {
type: OptionType.BOOLEAN,
default: false,
description: "Remove quests above DMs list",
description: t("anammox.settings.quests"),
restartNeeded: true,
},
serverBoost: {
type: OptionType.BOOLEAN,
default: true,
description: "Remove server boost info above channel list",
description: t("anammox.settings.serverBoost"),
restartNeeded: true,
},
billing: {
type: OptionType.BOOLEAN,
default: true,
description: "Remove billing settings",
description: t("anammox.settings.billing"),
restartNeeded: true,
},
gift: {
type: OptionType.BOOLEAN,
default: true,
description: "Remove gift button",
description: t("anammox.settings.gift"),
restartNeeded: true,
},
emojiList: {
type: OptionType.BOOLEAN,
default: true,
description: "Remove unavailable categories from the emoji picker",
description: t("anammox.settings.emojiList"),
restartNeeded: true,
},
});
export default definePlugin({
name: "Anammox",
description: "Hides various features related to nitro and the store",
description: t("anammox.description"),
authors: [Devs.Kyuuhachi],
settings,

View file

@ -6,57 +6,58 @@
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { SelectedChannelStore, UserStore } from "@webpack/common";
const settings = definePluginSettings({
volume: {
type: OptionType.SLIDER,
description: "Volume of the animalese sound",
description: t("animalese.settings.volume"),
default: 0.5,
markers: [0, 0.1, 0.25, 0.5, 0.6, 0.75, 1],
},
speed: {
type: OptionType.SLIDER,
description: "Speed of the animalese sound",
description: t("animalese.settings.speed"),
default: 1,
markers: [0.5, 0.75, 1, 1.25, 1.5],
},
pitch: {
type: OptionType.SLIDER,
description: "Pitch multiplier",
description: t("animalese.settings.pitch"),
default: 1,
markers: [0.75, 0.8, 0.85, 1, 1.15, 1.25, 1.35, 1.5],
},
messageLengthLimit: {
type: OptionType.NUMBER,
description: "Maximum length of message to process",
description: t("animalese.settings.messageLengthLimit"),
default: 50,
},
processOwnMessages: {
type: OptionType.BOOLEAN,
description: "Enable to yap your own messages too",
description: t("animalese.settings.processOwnMessages"),
default: true,
},
soundQuality: {
type: OptionType.SELECT,
description: "Quality of sound to use",
description: t("animalese.settings.soundQuality"),
options: [
{
label: "High",
label: t("animalese.quality.high"),
value: "high",
default: true
},
{
label: "Medium",
label: t("animalese.quality.medium"),
value: "med"
},
{
label: "Low",
label: t("animalese.quality.low"),
value: "low"
},
{
label: "Lowest",
label: t("animalese.quality.lowest"),
value: "low"
}
]
@ -192,7 +193,7 @@ async function playSound(buffer: AudioBuffer, volume: number) {
export default definePlugin({
name: "Animalese",
description: "Plays animal crossing animalese for every message sent (they yap a lot)",
description: t("animalese.description"),
authors: [EquicordDevs.ryanamay, EquicordDevs.Mocha],
settings,

View file

@ -6,20 +6,21 @@
import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { ChannelStore, GuildMemberStore, SelectedChannelStore, SelectedGuildStore } from "@webpack/common";
export default definePlugin({
name: "AtSomeone",
authors: [Devs.Joona],
description: "Mention someone randomly",
description: t("atSomeone.description"),
patches: [
{
find: ".LAUNCHABLE_APPLICATIONS;",
replacement: [
{
match: /&(\i)\(\)\((\i),\i\(\)\.test\)&&(\i)\.push\(\i\(\)\)/g,
replace: "$&,$1()($2,/someone/.test)&&$3.push({text:'@someone',description:'Mention someone randomly'})"
replace: "$&,$1()($2,/someone/.test)&&$3.push({text:'@someone',description:$self.getSomeoneDescription()})"
},
],
},
@ -31,6 +32,9 @@ export default definePlugin({
}
}
],
getSomeoneDescription() {
return t("atSomeone.contextMenu.someone");
},
start() {
this.preSend = addMessagePreSendListener((_, msg) => {
msg.content = msg.content.replace(/@someone/g, () => `<@${randomUser()}>`);

View file

@ -7,6 +7,7 @@
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, DraftType, SelectedChannelStore, UploadHandler } from "@webpack/common";
import { zipSync } from "fflate";
@ -16,7 +17,7 @@ const logger = new Logger("AutoZipper");
const settings = definePluginSettings({
extensions: {
type: OptionType.STRING,
description: "Comma-separated list of file extensions to auto-zip (e.g., .psd,.blend,.exe,.dmg)",
description: t("autoZipper.settings.extensions"),
default: ".psd,.blend,.exe,.dmg,.app,.apk,.iso",
onChange: () => {
extensionsToZip.clear();
@ -208,7 +209,7 @@ function handlePaste(event: ClipboardEvent) {
export default definePlugin({
name: "AutoZipper",
description: "Automatically zips specified file types and folders before uploading to Discord",
description: t("autoZipper.description"),
authors: [EquicordDevs.SSnowly],
settings,

View file

@ -10,6 +10,7 @@ import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import usrbg from "@plugins/usrbg";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { User } from "@vencord/discord-types";
import { UserProfileStore } from "@webpack/common";
@ -28,12 +29,12 @@ interface Nameplate {
const settings = definePluginSettings({
animate: {
description: "Animate banners",
description: t("bannersEverywhere.settings.animate"),
type: OptionType.BOOLEAN,
default: false
},
preferNameplate: {
description: "prefer nameplate over banner",
description: t("bannersEverywhere.settings.preferNameplate"),
type: OptionType.BOOLEAN,
default: false
},
@ -43,7 +44,7 @@ const DATASTORE_KEY = "bannersEverywhere";
export default definePlugin({
name: "BannersEverywhere",
description: "Displays banners in the member list ",
description: t("bannersEverywhere.description"),
authors: [Devs.ImLvna, Devs.AutumnVN],
settings,
patches: [

View file

@ -25,6 +25,7 @@ import { Heading } from "@components/Heading";
import { EquicordDevs } from "@utils/constants";
import { copyWithToast } from "@utils/discord";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Button, ChannelStore } from "@webpack/common";
@ -68,12 +69,12 @@ function openDecodedBase64Modal(decodedContent) {
<ErrorBoundary>
<ModalRoot {...props} size={ModalSize.LARGE}>
<ModalHeader>
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>Decoded Base64 Content</BaseText>
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>{t("baseDecoder.ui.modalTitle")}</BaseText>
<ModalCloseButton onClick={() => closeModal(key)} />
</ModalHeader>
<ModalContent>
<div style={{ padding: "16px 0" }}>
<Heading>Decoded Content</Heading>
<Heading>{t("baseDecoder.ui.decodedContent")}</Heading>
{decodedContent.map((content, index) => (
<CodeBlock key={index} content={content} lang="" />
))}
@ -82,8 +83,8 @@ function openDecodedBase64Modal(decodedContent) {
<ModalFooter>
<Flex gap={10}>
{decodedContent.map((content, index) => (
<Button key={index} onClick={() => copyWithToast(content, "Decoded content copied to clipboard!")}>
Copy Decoded Content {index + 1}
<Button key={index} onClick={() => copyWithToast(content, t("baseDecoder.ui.copiedToast"))}>
{t("baseDecoder.ui.copyDecoded")} {index + 1}
</Button>
))}
</Flex>
@ -95,18 +96,18 @@ function openDecodedBase64Modal(decodedContent) {
const settings = definePluginSettings({
clickMethod: {
description: "Change the button to decode base64 content of any message.",
description: t("baseDecoder.settings.clickMethod"),
type: OptionType.SELECT,
options: [
{ label: "Left Click to decode the base64 content.", value: "Left", default: true },
{ label: "Right click to decode the base64 content.", value: "Right" }
{ label: t("baseDecoder.settings.clickMethodOptions.left"), value: "Left", default: true },
{ label: t("baseDecoder.settings.clickMethodOptions.right"), value: "Right" }
]
}
});
export default definePlugin({
name: "DecodeBase64",
description: "Decode base64 content of any message and copy the decoded content.",
description: t("baseDecoder.description"),
authors: [EquicordDevs.ThePirateStoner],
settings,
messagePopoverButton: {
@ -137,8 +138,8 @@ export default definePlugin({
};
const label = settings.store.clickMethod === "Right"
? "Copy Decoded (Left Click) / Decode Base64 (Right Click)"
: "Decode Base64 (Left Click) / Copy Decoded (Right Click)";
? t("baseDecoder.ui.tooltipLeft")
: t("baseDecoder.ui.tooltipRight");
return {
label,

View file

@ -8,6 +8,7 @@ import "./styles.css";
import { migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { patchActivityList } from "./patch-helpers/activityList";
@ -18,7 +19,7 @@ migratePluginSettings("BetterActivities", "MemberListActivities");
export default definePlugin({
name: "BetterActivities",
description: "Shows activity icons in the member list and allows showing all activities",
description: t("betterActivities.description"),
authors: [Devs.D3SOX, Devs.Arjix, Devs.AutumnVN, Devs.thororen],
tags: ["activity"],
settings,

View file

@ -5,38 +5,39 @@
*/
import { definePluginSettings } from "@api/Settings";
import { t } from "@utils/translation";
import { OptionType } from "@utils/types";
import { React } from "@webpack/common";
export const settings = definePluginSettings({
memberList: {
type: OptionType.BOOLEAN,
description: "Show activity icons in the member list",
description: t("betterActivities.settings.memberList"),
default: true,
restartNeeded: true,
},
iconSize: {
type: OptionType.SLIDER,
description: "Size of the activity icons",
description: t("betterActivities.settings.iconSize"),
markers: [10, 15, 20],
default: 15,
stickToMarkers: false,
},
specialFirst: {
type: OptionType.BOOLEAN,
description: "Show special activities first (Currently Spotify and Twitch)",
description: t("betterActivities.settings.specialFirst"),
default: true,
restartNeeded: false,
},
renderGifs: {
type: OptionType.BOOLEAN,
description: "Allow rendering GIFs",
description: t("betterActivities.settings.renderGifs"),
default: true,
restartNeeded: false,
},
removeGameActivityStatus: {
type: OptionType.BOOLEAN,
description: "Remove game activity icon and status",
description: t("betterActivities.settings.removeGameActivityStatus"),
default: false,
restartNeeded: true,
},
@ -55,26 +56,26 @@ export const settings = definePluginSettings({
},
userPopout: {
type: OptionType.BOOLEAN,
description: "Show all activities in the profile popout/sidebar",
description: t("betterActivities.settings.userPopout"),
default: true,
restartNeeded: true,
},
hideTooltip: {
type: OptionType.BOOLEAN,
description: "Hides activities in various places",
description: t("betterActivities.settings.hideTooltip"),
default: true,
},
allActivitiesStyle: {
type: OptionType.SELECT,
description: "Style for showing all activities",
description: t("betterActivities.settings.allActivitiesStyle"),
options: [
{
default: true,
label: "Carousel",
label: t("betterActivities.options.carousel"),
value: "carousel",
},
{
label: "List",
label: t("betterActivities.options.list"),
value: "list",
},
]

View file

@ -8,6 +8,7 @@ import "./style.css";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { showToast, Toasts } from "@webpack/common";
@ -245,7 +246,7 @@ function handleColorChange(value, settingKey, defaultValue) {
if (rgb.match(rgbPattern)) {
settings.store[settingKey] = rgb;
} else {
showToast(`Invalid color format for ${settingKey}, make sure it's in the format 'R, G, B' or '#RRGGBB'`, Toasts.Type.FAILURE);
showToast(t("betterAudioPlayer.toasts.invalidColor", { settingKey }), Toasts.Type.FAILURE);
settings.store[settingKey] = defaultValue;
}
} else {
@ -256,39 +257,39 @@ function handleColorChange(value, settingKey, defaultValue) {
const settings = definePluginSettings({
oscilloscope: {
type: OptionType.BOOLEAN,
description: "Enable oscilloscope visualizer",
description: t("betterAudioPlayer.settings.oscilloscope"),
default: true,
},
spectrograph: {
type: OptionType.BOOLEAN,
description: "Enable spectrograph visualizer",
description: t("betterAudioPlayer.settings.spectrograph"),
default: true,
},
oscilloscopeSolidColor: {
type: OptionType.BOOLEAN,
description: "Use solid color for oscilloscope",
description: t("betterAudioPlayer.settings.oscilloscopeSolidColor"),
default: false,
},
oscilloscopeColor: {
type: OptionType.STRING,
description: "Color for oscilloscope",
description: t("betterAudioPlayer.settings.oscilloscopeColor"),
default: "255, 255, 255",
onChange: value => handleColorChange(value, "oscilloscopeColor", "255, 255, 255"),
},
spectrographSolidColor: {
type: OptionType.BOOLEAN,
description: "Use solid color for spectrograph",
description: t("betterAudioPlayer.settings.spectrographSolidColor"),
default: false,
},
spectrographColor: {
type: OptionType.STRING,
description: "Color for spectrograph",
description: t("betterAudioPlayer.settings.spectrographColor"),
default: "33, 150, 243",
onChange: value => handleColorChange(value, "spectrographColor", "33, 150, 243"),
},
forceMoveBelow: {
type: OptionType.BOOLEAN,
description: "Force the visualizer below the audio player",
description: t("betterAudioPlayer.settings.forceMoveBelow"),
default: true,
},
});
@ -297,7 +298,7 @@ let observer: MutationObserver | null = null;
export default definePlugin({
name: "BetterAudioPlayer",
description: "Adds a spectrograph and oscilloscope visualizer to audio attachment players",
description: t("betterAudioPlayer.description"),
authors: [EquicordDevs.creations],
settings,
start() {

View file

@ -11,6 +11,7 @@ import { Heading } from "@components/Heading";
import { DeleteIcon, PlusIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Button, TextInput } from "@webpack/common";
@ -21,7 +22,7 @@ function ReasonsComponent() {
return (
<section>
<Heading>Reasons</Heading>
<Heading>{t("betterBanReasons.ui.reasons")}</Heading>
{reasons.map((r, i) => (
<div
key={i}
@ -33,7 +34,7 @@ function ReasonsComponent() {
reasons[i] = v;
settings.store.reasons = reasons;
}}
placeholder="Reason"
placeholder={t("betterBanReasons.ui.reasonPlaceholder")}
/>
<Button
className={cl("remove-button")}
@ -51,7 +52,7 @@ function ReasonsComponent() {
))}
<div className={cl("reason-wrapper")}>
<Button onClick={() => settings.store.reasons.push("")} className={cl("add-button")} size={Button.Sizes.LARGE} color={Button.Colors.TRANSPARENT}>
<PlusIcon /> Add another reason
<PlusIcon /> {t("betterBanReasons.ui.addAnotherReason")}
</Button>
</div>
</section>
@ -60,20 +61,20 @@ function ReasonsComponent() {
const settings = definePluginSettings({
reasons: {
description: "Your custom reasons",
description: t("betterBanReasons.settings.reasons"),
type: OptionType.COMPONENT,
default: [] as string[],
component: ReasonsComponent,
},
isTextInputDefault: {
type: OptionType.BOOLEAN,
description: 'Shows a text input instead of a select menu by default. (Equivalent to clicking the "Other" option)'
description: t("betterBanReasons.settings.isTextInputDefault")
}
});
export default definePlugin({
name: "BetterBanReasons",
description: "Create custom reasons to use in the Discord ban modal, and/or show a text input by default instead of the options.",
description: t("betterBanReasons.description"),
authors: [Devs.Inbestigator],
patches: [
{

View file

@ -8,6 +8,7 @@ import "./styles.css";
import { EquicordDevs } from "@utils/constants";
import { getIntlMessage, openUserProfile } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Button, React, RelationshipStore, TextInput, UserStore } from "@webpack/common";
@ -16,7 +17,7 @@ let updateFunc = (v: any) => { };
export default definePlugin({
name: "BetterBlockedUsers",
description: "Allows you to search in blocked users list and makes names selectable in settings.",
description: t("betterBlockedUsers.description"),
authors: [EquicordDevs.TheArmagan],
patches: [
{
@ -50,7 +51,7 @@ export default definePlugin({
}, []);
return <TextInput
placeholder="Search users..."
placeholder={t("betterBlockedUsers.ui.searchPlaceholder")}
style={{ width: "200px" }}
onInput={e => {
const search = (e.target as HTMLInputElement).value.toLowerCase().trim();

View file

@ -7,17 +7,18 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { FluxDispatcher, UserStore } from "@webpack/common";
const settings = definePluginSettings({
autoFillArguments: {
description: "Automatically fill command with all arguements instead of just required ones",
description: t("betterCommands.settings.autoFillArguments"),
type: OptionType.BOOLEAN,
default: true,
},
allowNewlinesInCommands: {
description: "Allow newlines in command inputs (CTRL + Shift + Enter)",
description: t("betterCommands.settings.allowNewlinesInCommands"),
type: OptionType.BOOLEAN,
default: true,
}
@ -32,7 +33,7 @@ function fetchIndex(target: object) {
export default definePlugin({
name: "BetterCommands",
description: "Enhances the command system with miscellaneous improvements.",
description: t("betterCommands.description"),
authors: [Devs.thororen],
settings,
patches: [
@ -70,11 +71,11 @@ export default definePlugin({
commands: [
{
name: "refresh",
description: "Refreshes Discord application commands locally",
description: t("betterCommands.commands.refresh.description"),
options: [
{
name: "user",
description: "specific user to try and refresh",
description: t("betterCommands.commands.refresh.userOption"),
type: ApplicationCommandOptionType.USER,
}
],
@ -86,7 +87,7 @@ export default definePlugin({
const user = findOption(opts, "user") as string;
sendBotMessage(ctx.channel.id, {
content: "Refreshing application commands...",
content: t("betterCommands.commands.refresh.refreshing"),
});
fetchIndex({ type: "channel", channelId });
@ -103,12 +104,12 @@ export default definePlugin({
}
sendBotMessage(ctx.channel.id, {
content: "Commands refreshed successfully!",
content: t("betterCommands.commands.refresh.success"),
});
} catch (err) {
console.error("[Refresh Command] Error refreshing commands:", err);
sendBotMessage(ctx.channel.id, {
content: "Failed to refresh commands. Check the console for details.",
content: t("betterCommands.commands.refresh.failed"),
});
}
},

View file

@ -10,6 +10,7 @@ import { InfoIcon } from "@components/Icons";
import { Devs, EquicordDevs } from "@utils/constants";
import { openUserProfile } from "@utils/discord";
import { classes } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { StartAt } from "@utils/types";
import { Guild } from "@vencord/discord-types";
import { findByPropsLazy, findCssClassesLazy } from "@webpack";
@ -33,7 +34,7 @@ function lurk(id: string) {
export default definePlugin({
name: "BetterInvites",
description: "See invites expiration date, view inviter profile and preview servers before joining by clicking the name",
description: t("betterInvites.description"),
authors: [EquicordDevs.iamme, Devs.thororen],
patches: [
{
@ -66,7 +67,9 @@ export default definePlugin({
const timestamp = <>{Parser.parse(`<t:${Math.round(new Date(expires_at).getTime() / 1000)}:R>`)}</>;
const tooltipText = (
<>
This invite will {expires_at ? <>expire {timestamp}</> : <>not expire</>}
{t("betterInvites.ui.expireTooltip", {
expiry: expires_at ? <>{t("betterInvites.ui.expireWill")} {timestamp}</> : <>{t("betterInvites.ui.notExpire")}</>
})}
</>
);
@ -98,7 +101,7 @@ export default definePlugin({
: "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"}
/>
<div className="vc-bi-header-text">
{inviter.global_name || inviter.username} invited you to {guildName}
{t("betterInvites.ui.invitedYouTo", { inviter: inviter.global_name || inviter.username, guild: guildName })}
</div>
</div>
);

View file

@ -5,13 +5,14 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { MessageStore } from "@webpack/common";
export default definePlugin({
name: "BetterPlusReacts",
authors: [Devs.Joona],
description: "The amount of plus before :emoji: is the message to add it to",
description: t("betterPlusReacts.description"),
patches: [
{
find: ".SLASH_COMMAND_USED,",

View file

@ -6,42 +6,43 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
const settings = definePluginSettings({
removeNameplate: {
type: OptionType.BOOLEAN,
description: "Remove nameplate styling.",
description: t("betterUserArea.settings.removeNameplate"),
default: true,
restartNeeded: true
},
removeAudioMenus: {
type: OptionType.BOOLEAN,
description: "Remove menus next to mute and deafen buttons.",
description: t("betterUserArea.settings.removeAudioMenus"),
default: true,
restartNeeded: true
},
alwaysShowUsername: {
type: OptionType.BOOLEAN,
description: "Always show username instead of status.",
description: t("betterUserArea.settings.alwaysShowUsername"),
default: true,
restartNeeded: true
},
removeButtonTooltips: {
type: OptionType.BOOLEAN,
description: "Remove button tooltips.",
description: t("betterUserArea.settings.removeButtonTooltips"),
default: false,
restartNeeded: true
},
removeAvatarDecoration: {
type: OptionType.BOOLEAN,
description: "Remove avatar decoration.",
description: t("betterUserArea.settings.removeAvatarDecoration"),
default: true,
restartNeeded: true
},
removeUsernameStyles: {
type: OptionType.BOOLEAN,
description: "Remove username colors and effects.",
description: t("betterUserArea.settings.removeUsernameStyles"),
default: true,
restartNeeded: true
}
@ -49,7 +50,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "BetterUserArea",
description: "Customize and make the user area more clean.",
description: t("betterUserArea.description"),
authors: [Devs.prism],
settings,
patches: [

View file

@ -6,6 +6,7 @@
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
@ -14,24 +15,24 @@ let blockedKeywords: Array<RegExp>;
const settings = definePluginSettings({
blockedWords: {
type: OptionType.STRING,
description: "Comma-seperated list of words to block",
description: t("blockKeywords.settings.blockedWords"),
default: "",
restartNeeded: true
},
useRegex: {
type: OptionType.BOOLEAN,
description: "Use each value as a regular expression when checking message content (advanced)",
description: t("blockKeywords.settings.useRegex"),
default: false,
restartNeeded: true
},
caseSensitive: {
type: OptionType.BOOLEAN,
description: "Whether to use a case sensitive search or not",
description: t("blockKeywords.settings.caseSensitive"),
default: false,
restartNeeded: true
},
ignoreBlockedMessages: {
description: "Completely ignores (recent) new messages bar",
description: t("blockKeywords.settings.ignoreBlockedMessages"),
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true,
@ -69,7 +70,7 @@ export function containsBlockedKeywords(message: Message) {
export default definePlugin({
name: "BlockKeywords",
description: "Blocks messages containing specific user-defined keywords, as if the user sending them was blocked.",
description: t("blockKeywords.description"),
authors: [EquicordDevs.catcraft],
patches: [
{

View file

@ -5,11 +5,12 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "BlockKrisp",
description: "Prevent Krisp from loading",
description: t("blockKrisp.description"),
authors: [Devs.D3SOX],
patches: [
// Block loading modules on Desktop

View file

@ -5,11 +5,12 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "BypassPinPrompt",
description: "Bypass the pin prompt when using the pin functions",
description: t("bypassPinPrompt.description"),
authors: [Devs.thororen],
patches: [
...[

View file

@ -11,6 +11,7 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import type { Message } from "@vencord/discord-types";
import { ChannelActionCreators, ChannelStore, Menu, MessageStore, NavigationRouter, PresenceStore, UserStore, WindowStore } from "@webpack/common";
@ -79,7 +80,7 @@ function ContextCallback(name: "guild" | "user" | "channel"): NavContextMenuPatc
<Menu.MenuGroup>
<Menu.MenuItem
id={`status-${name}-bypass`}
label={`${enabled ? "Remove" : "Add"} Status Bypass`}
label={enabled ? t("bypassStatus.contextMenu.removeStatusBypass") : t("bypassStatus.contextMenu.addStatusBypass")}
icon={() => Icon(enabled)}
action={() => {
let bypasses: string[] = settings.store[`${name}s`].split(", ");
@ -96,58 +97,58 @@ function ContextCallback(name: "guild" | "user" | "channel"): NavContextMenuPatc
const settings = definePluginSettings({
guilds: {
type: OptionType.STRING,
description: "Guilds to let bypass (notified when pinged anywhere in guild)",
description: t("bypassStatus.settings.guilds"),
default: "",
placeholder: "Separate with commas",
placeholder: t("bypassStatus.placeholder.separateWithCommas"),
onChange: value => settings.store.guilds = processIds(value)
},
channels: {
type: OptionType.STRING,
description: "Channels to let bypass (notified when pinged in that channel)",
description: t("bypassStatus.settings.channels"),
default: "",
placeholder: "Separate with commas",
placeholder: t("bypassStatus.placeholder.separateWithCommas"),
onChange: value => settings.store.channels = processIds(value)
},
users: {
type: OptionType.STRING,
description: "Users to let bypass (notified for all messages sent in DMs)",
description: t("bypassStatus.settings.users"),
default: "",
placeholder: "Separate with commas",
placeholder: t("bypassStatus.placeholder.separateWithCommas"),
onChange: value => settings.store.users = processIds(value)
},
allowOutsideOfDms: {
type: OptionType.BOOLEAN,
description: "Allow selected users to bypass status outside of DMs too (acts like a channel/guild bypass, but it's for all messages sent by the selected users)"
description: t("bypassStatus.settings.allowOutsideOfDms")
},
notificationSound: {
type: OptionType.BOOLEAN,
description: "Whether the notification sound should be played",
description: t("bypassStatus.settings.notificationSound"),
default: true,
},
respectSilentPings: {
type: OptionType.BOOLEAN,
description: "Respect silent pings (@silent / suppress notifications)",
description: t("bypassStatus.settings.respectSilentPings"),
default: true
},
statusToUse: {
type: OptionType.SELECT,
description: "Status to use for whitelist",
description: t("bypassStatus.settings.statusToUse"),
options: [
{
label: "Online",
label: t("bypassStatus.statusOptions.online"),
value: "online",
},
{
label: "Idle",
label: t("bypassStatus.statusOptions.idle"),
value: "idle",
},
{
label: "Do Not Disturb",
label: t("bypassStatus.statusOptions.dnd"),
value: "dnd",
default: true
},
{
label: "Invisible",
label: t("bypassStatus.statusOptions.invisible"),
value: "invisible",
}
]
@ -156,7 +157,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "BypassStatus",
description: "Still get notifications from specific sources when in do not disturb mode. Right-click on users/channels/guilds to set them to bypass do not disturb mode.",
description: t("bypassStatus.description"),
authors: [Devs.Inbestigator],
dependencies: ["AudioPlayerAPI"],
flux: {

View file

@ -7,6 +7,7 @@
import "./style.css";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Channel } from "@vencord/discord-types";
import { GuildStore, React, SelectedGuildStore } from "@webpack/common";
@ -31,7 +32,7 @@ function renderBadge(id: number, title: string) {
export default definePlugin({
name: "ChannelBadges",
description: "Adds badges to channels based on their type",
description: t("channelBadges.description"),
authors: [EquicordDevs.creations, Devs.thororen],
settings,
patches: [
@ -64,9 +65,9 @@ export default definePlugin({
const guild = selectedGuildId ? GuildStore.getGuild(selectedGuildId) : null;
const badgeConditions = [
{ id: 6101, condition: isPrivate, title: "This channel is locked." },
{ id: 6100, condition: isNSFW, title: "This channel is marked as NSFW." },
{ id: 6102, condition: guild?.rulesChannelId === channel.id, title: "This channel is the server rules channel." },
{ id: 6101, condition: isPrivate, title: t("channelBadges.tooltips.lockedChannel") },
{ id: 6100, condition: isNSFW, title: t("channelBadges.tooltips.nsfwChannel") },
{ id: 6102, condition: guild?.rulesChannelId === channel.id, title: t("channelBadges.tooltips.rulesChannel") },
];
let badges: JSX.Element[] = [];

View file

@ -5,270 +5,271 @@
*/
import { definePluginSettings } from "@api/Settings";
import { t } from "@utils/translation";
import { OptionType } from "@utils/types";
export const settings = definePluginSettings({
oneBadgePerChannel: {
type: OptionType.BOOLEAN,
default: false,
description: "Show only one badge per channel",
description: t("channelBadges.settings.oneBadgePerChannel"),
restartNeeded: true,
},
showTextBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Text badge",
description: t("channelBadges.settings.showTextBadge"),
restartNeeded: true,
},
showVoiceBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Voice badge",
description: t("channelBadges.settings.showVoiceBadge"),
restartNeeded: true,
},
showCategoryBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Category badge",
description: t("channelBadges.settings.showCategoryBadge"),
restartNeeded: true,
},
showDirectoryBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Directory badge",
description: t("channelBadges.settings.showDirectoryBadge"),
restartNeeded: true,
},
showAnnouncementThreadBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Announcement Thread badge",
description: t("channelBadges.settings.showAnnouncementThreadBadge"),
restartNeeded: true,
},
showPublicThreadBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Public Thread badge",
description: t("channelBadges.settings.showPublicThreadBadge"),
restartNeeded: true,
},
showPrivateThreadBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Private Thread badge",
description: t("channelBadges.settings.showPrivateThreadBadge"),
restartNeeded: true,
},
showStageBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Stage badge",
description: t("channelBadges.settings.showStageBadge"),
restartNeeded: true,
},
showAnnouncementBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Announcement badge",
description: t("channelBadges.settings.showAnnouncementBadge"),
restartNeeded: true,
},
showForumBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Forum badge",
description: t("channelBadges.settings.showForumBadge"),
restartNeeded: true,
},
showMediaBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Media badge",
description: t("channelBadges.settings.showMediaBadge"),
restartNeeded: true,
},
showNSFWBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show NSFW badge",
description: t("channelBadges.settings.showNSFWBadge"),
restartNeeded: true,
},
showLockedBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Locked badge",
description: t("channelBadges.settings.showLockedBadge"),
restartNeeded: true,
},
showRulesBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Rules badge",
description: t("channelBadges.settings.showRulesBadge"),
restartNeeded: true,
},
showUnknownBadge: {
type: OptionType.BOOLEAN,
default: true,
description: "Show Unknown badge",
description: t("channelBadges.settings.showUnknownBadge"),
restartNeeded: true,
},
textBadgeLabel: {
type: OptionType.STRING,
default: "Text",
description: "Text badge label",
description: t("channelBadges.settings.textBadgeLabel"),
restartNeeded: true,
},
voiceBadgeLabel: {
type: OptionType.STRING,
default: "Voice",
description: "Voice badge label",
description: t("channelBadges.settings.voiceBadgeLabel"),
restartNeeded: true,
},
categoryBadgeLabel: {
type: OptionType.STRING,
default: "Category",
description: "Category badge label",
description: t("channelBadges.settings.categoryBadgeLabel"),
restartNeeded: true,
},
announcementBadgeLabel: {
type: OptionType.STRING,
default: "News",
description: "Announcement badge label",
description: t("channelBadges.settings.announcementBadgeLabel"),
restartNeeded: true,
},
announcementThreadBadgeLabel: {
type: OptionType.STRING,
default: "News Thread",
description: "Announcement Thread badge label",
description: t("channelBadges.settings.announcementThreadBadgeLabel"),
restartNeeded: true,
},
publicThreadBadgeLabel: {
type: OptionType.STRING,
default: "Thread",
description: "Public Thread badge label",
description: t("channelBadges.settings.publicThreadBadgeLabel"),
restartNeeded: true,
},
privateThreadBadgeLabel: {
type: OptionType.STRING,
default: "Private Thread",
description: "Private Thread badge label",
description: t("channelBadges.settings.privateThreadBadgeLabel"),
restartNeeded: true,
},
stageBadgeLabel: {
type: OptionType.STRING,
default: "Stage",
description: "Stage badge label",
description: t("channelBadges.settings.stageBadgeLabel"),
restartNeeded: true,
},
directoryBadgeLabel: {
type: OptionType.STRING,
default: "Directory",
description: "Directory badge label",
description: t("channelBadges.settings.directoryBadgeLabel"),
restartNeeded: true,
},
forumBadgeLabel: {
type: OptionType.STRING,
default: "Forum",
description: "Forum badge label",
description: t("channelBadges.settings.forumBadgeLabel"),
restartNeeded: true,
},
mediaBadgeLabel: {
type: OptionType.STRING,
default: "Media",
description: "Media badge label",
description: t("channelBadges.settings.mediaBadgeLabel"),
restartNeeded: true,
},
nsfwBadgeLabel: {
type: OptionType.STRING,
default: "NSFW",
description: "NSFW badge label",
description: t("channelBadges.settings.nsfwBadgeLabel"),
restartNeeded: true,
},
lockedBadgeLabel: {
type: OptionType.STRING,
default: "Locked",
description: "Locked badge label",
description: t("channelBadges.settings.lockedBadgeLabel"),
restartNeeded: true,
},
rulesBadgeLabel: {
type: OptionType.STRING,
default: "Rules",
description: "Rules badge label",
description: t("channelBadges.settings.rulesBadgeLabel"),
restartNeeded: true,
},
unknownBadgeLabel: {
type: OptionType.STRING,
default: "Unknown",
description: "Unknown badge label",
description: t("channelBadges.settings.unknownBadgeLabel"),
restartNeeded: true,
},
textBadgeColor: {
type: OptionType.STRING,
description: "Text badge color",
description: t("channelBadges.settings.textBadgeColor"),
restartNeeded: true,
},
voiceBadgeColor: {
type: OptionType.STRING,
description: "Voice badge color",
description: t("channelBadges.settings.voiceBadgeColor"),
restartNeeded: true,
},
categoryBadgeColor: {
type: OptionType.STRING,
description: "Category badge color",
description: t("channelBadges.settings.categoryBadgeColor"),
restartNeeded: true,
},
announcementBadgeColor: {
type: OptionType.STRING,
description: "Announcement badge color",
description: t("channelBadges.settings.announcementBadgeColor"),
restartNeeded: true,
},
announcementThreadBadgeColor: {
type: OptionType.STRING,
description: "Announcement Thread badge color",
description: t("channelBadges.settings.announcementThreadBadgeColor"),
restartNeeded: true,
},
publicThreadBadgeColor: {
type: OptionType.STRING,
description: "Public Thread badge color",
description: t("channelBadges.settings.publicThreadBadgeColor"),
restartNeeded: true,
},
privateThreadBadgeColor: {
type: OptionType.STRING,
description: "Private Thread badge color",
description: t("channelBadges.settings.privateThreadBadgeColor"),
restartNeeded: true,
},
stageBadgeColor: {
type: OptionType.STRING,
description: "Stage badge color",
description: t("channelBadges.settings.stageBadgeColor"),
restartNeeded: true,
},
directoryBadgeColor: {
type: OptionType.STRING,
description: "Directory badge color",
description: t("channelBadges.settings.directoryBadgeColor"),
restartNeeded: true,
},
forumBadgeColor: {
type: OptionType.STRING,
description: "Forum badge color",
description: t("channelBadges.settings.forumBadgeColor"),
restartNeeded: true,
},
mediaBadgeColor: {
type: OptionType.STRING,
description: "Media badge color",
description: t("channelBadges.settings.mediaBadgeColor"),
restartNeeded: true,
},
nsfwBadgeColor: {
type: OptionType.STRING,
description: "NSFW badge color",
description: t("channelBadges.settings.nsfwBadgeColor"),
restartNeeded: true,
},
lockedBadgeColor: {
type: OptionType.STRING,
description: "Locked badge color",
description: t("channelBadges.settings.lockedBadgeColor"),
restartNeeded: true,
},
rulesBadgeColor: {
type: OptionType.STRING,
description: "Rules badge color",
description: t("channelBadges.settings.rulesBadgeColor"),
restartNeeded: true,
},
unknownBadgeColor: {
type: OptionType.STRING,
description: "Unknown badge color",
description: t("channelBadges.settings.unknownBadgeColor"),
restartNeeded: true,
},
});

View file

@ -9,6 +9,7 @@ import "./style.css";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Channel, Message } from "@vencord/discord-types";
import { ChannelStore, Menu } from "@webpack/common";
@ -23,7 +24,7 @@ const contextMenuPatch: NavContextMenuPatchCallback = (children, props: { channe
const menuItem = (
<Menu.MenuItem
label="Open in New Tab"
label={t("channelTabs.contextMenu.openInNewTab")}
id="open-link-in-tab"
action={() => createTab({
guildId: channel.guild_id || "@me", // Normalize for DMs/Group Chats
@ -46,7 +47,7 @@ const contextMenuPatch: NavContextMenuPatchCallback = (children, props: { channe
export default definePlugin({
name: "ChannelTabs",
description: "Group your commonly visited channels in tabs, like a browser",
description: t("channelTabs.description"),
authors: [Devs.TheSun, Devs.TheKodeToad, EquicordDevs.keifufu, Devs.Nickyux, EquicordDevs.DiabeloDEV, EquicordDevs.justjxke],
dependencies: ["ContextMenuAPI"],
contextMenus: {

View file

@ -8,20 +8,21 @@ import "./style.css";
import { definePluginSettings } from "@api/Settings";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { UserStore } from "@webpack/common";
const settings = definePluginSettings({
colorEffects: {
type: OptionType.BOOLEAN,
description: "Turn on or off color effects for getting close to the character limit",
description: t("characterCounter.settings.colorEffects"),
default: true,
},
});
export default definePlugin({
name: "CharacterCounter",
description: "Adds a character counter to the chat input",
description: t("characterCounter.description"),
authors: [EquicordDevs.creations, EquicordDevs.Panniku, Devs.thororen],
settings,
patches: [

View file

@ -5,13 +5,14 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Channel } from "@vencord/discord-types";
export default definePlugin({
name: "CleanChannelName",
authors: [Devs.AutumnVN],
description: "remove all emoji and decor shit from channel names",
description: t("cleanChannelName.description"),
patches: [
{
find: "loadAllGuildAndPrivateChannelsFromDisk(){",

View file

@ -5,11 +5,12 @@
*/
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "CleanerChannelGroups",
description: "Hides all channels in collapsed categories, even if they have unread messages.",
description: t("cleanerChannelGroups.description"),
authors: [EquicordDevs.justjxke],
patches: [
{

View file

@ -7,6 +7,7 @@
import { definePluginSettings } from "@api/Settings";
import { Paragraph } from "@components/Paragraph";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { GuildMember } from "@vencord/discord-types";
import { ChannelStore, GuildMemberStore, GuildRoleStore, React, RelationshipStore, UserStore } from "@webpack/common";
@ -14,52 +15,52 @@ import { ChannelStore, GuildMemberStore, GuildRoleStore, React, RelationshipStor
const settings = definePluginSettings({
hideVc: {
type: OptionType.BOOLEAN,
description: "Hide voice channels containing blocked users.",
description: t("clientSideBlock.settings.hideVc"),
default: false,
restartNeeded: true
},
usersToBlock: {
type: OptionType.STRING,
description: "User IDs seperated by a comma and a space",
description: t("clientSideBlock.settings.usersToBlock"),
restartNeeded: true,
default: ""
},
hideBlockedUsers: {
type: OptionType.BOOLEAN,
description: "Should blocked users should also be hidden everywhere",
description: t("clientSideBlock.settings.hideBlockedUsers"),
default: true,
restartNeeded: true
},
hideBlockedMessages: {
type: OptionType.BOOLEAN,
description: "Should messages from blocked users should be hidden fully (same as the old noblockedmessages plugin)",
description: t("clientSideBlock.settings.hideBlockedMessages"),
default: true,
restartNeeded: true
},
hideEmptyRoles: {
type: OptionType.BOOLEAN,
description: "Should role headers be hidden if all of their members are blocked",
description: t("clientSideBlock.settings.hideEmptyRoles"),
restartNeeded: true,
default: true
},
blockedReplyDisplay: {
type: OptionType.SELECT,
description: "What should display instead of the message when someone replies to someone you have hidden",
description: t("clientSideBlock.settings.blockedReplyDisplay"),
restartNeeded: true,
options: [
{ value: "displayText", label: "Display text saying a hidden message was replied to", default: true },
{ value: "hideReply", label: "Literally nothing" }
{ value: "displayText", label: t("clientSideBlock.settings.blockedReplyDisplayOptions.displayText"), default: true },
{ value: "hideReply", label: t("clientSideBlock.settings.blockedReplyDisplayOptions.hideReply") }
]
},
guildBlackList: {
type: OptionType.STRING,
description: "Guild ids to disable functionality in",
description: t("clientSideBlock.settings.guildBlackList"),
restartNeeded: true,
default: ""
},
guildWhiteList: {
type: OptionType.STRING,
description: "Guild ids to enable functionality in",
description: t("clientSideBlock.settings.guildWhiteList"),
restartNeeded: true,
default: ""
}
@ -100,7 +101,7 @@ function hiddenReplyComponent() {
case "displayText":
return <Paragraph style={{ marginTop: "0px", marginBottom: "0px" }}>
<i>
↓ Replying to blocked message
↓ {t("clientSideBlock.reply.replyingToBlocked")}
</i>
</Paragraph>;
case "hideReply":
@ -141,7 +142,7 @@ function activeNowView(cards) {
export default definePlugin({
name: "ClientSideBlock",
description: "Allows you to locally hide almost all content from any user",
description: t("clientSideBlock.description"),
tags: ["blocked", "block", "hide", "hidden", "noblockedmessages"],
authors: [Devs.Samwich, EquicordDevs.KamiRu],
settings,

View file

@ -8,6 +8,7 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { Button } from "@components/Button";
import { Devs, EquicordDevs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { PresenceStore, SettingsRouter, UserStore } from "@webpack/common";
@ -18,34 +19,34 @@ const extraFramerates = [45, 90, 120, 144, 165, 240];
const settings = definePluginSettings({
richPresenceTagging: {
type: OptionType.SELECT,
description: "When should clips be tagged with the current Rich Presence?",
description: t("clipsEnhancements.settings.richPresenceTagging"),
options: [
{ label: "Always", value: "always" },
{ label: "Only when beginning or end of activity name matches", value: "whenMatched", default: true },
{ label: "Never", value: "never" },
{ label: t("clipsEnhancements.settings.richPresenceTaggingOptions.always"), value: "always" },
{ label: t("clipsEnhancements.settings.richPresenceTaggingOptions.whenMatched"), value: "whenMatched", default: true },
{ label: t("clipsEnhancements.settings.richPresenceTaggingOptions.never"), value: "never" },
]
},
enableScreenshotKeybind: {
type: OptionType.BOOLEAN,
description: "Enable the screenshot keybind feature",
description: t("clipsEnhancements.settings.enableScreenshotKeybind"),
default: true,
restartNeeded: true
},
enableVoiceOnlyClips: {
type: OptionType.BOOLEAN,
description: "Enable voice-only clips (audio without video)",
description: t("clipsEnhancements.settings.enableVoiceOnlyClips"),
default: true,
restartNeeded: true
},
enableAdvancedSignals: {
type: OptionType.BOOLEAN,
description: "Enable advanced clip signals (auto-clip triggers)",
description: t("clipsEnhancements.settings.enableAdvancedSignals"),
default: true,
restartNeeded: true
},
ignorePlatformRestriction: {
type: OptionType.BOOLEAN,
description: "Allow Platform Restricted Clipping (may cause save errors)",
description: t("clipsEnhancements.settings.ignorePlatformRestriction"),
default: true,
restartNeeded: true
},
@ -60,7 +61,7 @@ const settings = definePluginSettings({
SettingsRouter.openUserSettings("clips_panel");
}}
>
Change FPS and duration options in Clips settings!
{t("clipsEnhancements.settings.clipsLink")}
</Button >
</>
);
@ -71,7 +72,7 @@ const settings = definePluginSettings({
migratePluginSettings("ClipsEnhancements", "TimelessClips");
export default definePlugin({
name: "ClipsEnhancements",
description: "Add more Clip FPS and duration options, custom clip length, RPC tagging and more",
description: t("clipsEnhancements.description"),
authors: [Devs.niko, Devs.Joona, EquicordDevs.keircn],
settings,
patches: [

View file

@ -8,6 +8,7 @@ import { definePluginSettings } from "@api/Settings";
import { BaseText } from "@components/BaseText";
import { Button } from "@components/Button";
import { EquicordDevs, IS_MAC } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { useEffect, useState } from "@webpack/common";
@ -92,9 +93,9 @@ function KeybindRecorder() {
return (
<div className="vc-command-palette-keybind-input">
<div className="vc-command-palette-keybind-info">
<BaseText size="md" weight="semibold">Command Palette Hotkey</BaseText>
<BaseText size="md" weight="semibold">{t("commandPalette.ui.hotkeyTitle")}</BaseText>
<BaseText size="sm" weight="normal" style={{ color: "var(--text-muted)" }}>
Hotkey used to open the command palette
{t("commandPalette.ui.hotkeyDescription")}
</BaseText>
{error && (
<BaseText size="xs" weight="normal" className="vc-command-palette-keybind-conflict">
@ -111,14 +112,14 @@ function KeybindRecorder() {
>
{isListening ? (
<BaseText size="sm" weight="normal" style={{ color: "var(--white)", opacity: 0.8 }}>
Press any key...
{t("commandPalette.ui.pressAnyKey")}
</BaseText>
) : (
formatKeybind(currentKeybind)
)}
</Button>
<Button size="small" variant="secondary" onClick={handleReset}>
Reset
{t("commandPalette.ui.reset")}
</Button>
</div>
</div>
@ -127,31 +128,31 @@ function KeybindRecorder() {
export const settings = definePluginSettings({
hotkey: {
description: "Hotkey used to open the command palette",
description: t("commandPalette.settings.hotkey"),
type: OptionType.COMPONENT,
default: DEFAULT_KEYS,
component: KeybindRecorder
},
visualStyle: {
description: "Palette appearance",
description: t("commandPalette.settings.visualStyle"),
type: OptionType.SELECT,
options: [
{ label: "Classic", value: "classic", default: true },
{ label: "Polished", value: "polished" }
{ label: t("commandPalette.settings.visualStyleOptions.classic"), value: "classic", default: true },
{ label: t("commandPalette.settings.visualStyleOptions.polished"), value: "polished" }
]
},
showTags: {
description: "Display tag chips for commands",
description: t("commandPalette.settings.showTags"),
type: OptionType.BOOLEAN,
default: true
},
enableTagFilter: {
description: "Show the tag filter bar",
description: t("commandPalette.settings.enableTagFilter"),
type: OptionType.BOOLEAN,
default: true
},
customCommands: {
description: "Manage custom command palette entries",
description: t("commandPalette.settings.customCommands"),
type: OptionType.COMPONENT,
component: CommandPaletteSettingsPanel
}
@ -223,7 +224,7 @@ function hotkeyUsesModifiers() {
export default definePlugin({
name: "CommandPalette",
description: "Quickly run actions through a searchable command palette",
description: t("commandPalette.description"),
authors: [EquicordDevs.justjxke, EquicordDevs.Ethan],
settings,
patches: [

View file

@ -9,6 +9,7 @@ import "./styles.css";
import SettingsPlugin from "@plugins/_core/settings";
import { Devs } from "@utils/constants";
import { removeFromArray } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { StartAt } from "@utils/types";
import { SettingsRouter } from "@webpack/common";
@ -40,7 +41,7 @@ function ComponentsIcon(props: React.SVGProps<SVGSVGElement>) {
export default definePlugin({
name: "Components",
description: "Adds a new tab to settings to browse Discord components.",
description: t("components.description"),
authors: [Devs.prism],
dependencies: ["Settings"],
startAt: StartAt.WebpackReady,

View file

@ -14,6 +14,7 @@ import { DeleteIcon } from "@components/Icons";
import { EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { useForceUpdater } from "@utils/react";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Button, TextInput, useState } from "@webpack/common";
@ -93,7 +94,7 @@ function FlaggedInput({ index, forceUpdate }) {
return (<Flex flexDirection="row">
<div style={{ flexGrow: 1 }}>
<TextInput
placeholder="Word"
placeholder={t("contentWarning.ui.placeholder")}
spellCheck={false}
value={value}
onChange={updateValue}
@ -130,7 +131,7 @@ function FlaggedWords() {
return (
<>
<HeadingTertiary>Flagged Words</HeadingTertiary>
<HeadingTertiary>{t("contentWarning.ui.flaggedWords")}</HeadingTertiary>
{inputs}
</>
);
@ -143,7 +144,7 @@ const settings = definePluginSettings({
},
onClick: {
type: OptionType.BOOLEAN,
description: "Only show trigger content on click instead of hover",
description: t("contentWarning.settings.onClick"),
default: false,
}
});
@ -151,7 +152,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "ContentWarning",
authors: [EquicordDevs.camila314],
description: "Allows you to specify certain trigger words that will be blurred by default. Hovering/Clicking on the blurred content will reveal it.",
description: t("contentWarning.description"),
settings,
patches: [
{

View file

@ -7,6 +7,7 @@
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { copyToClipboard } from "@utils/clipboard";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Menu, Toasts, UserProfileStore } from "@webpack/common";
@ -34,7 +35,7 @@ function copyProfileColors(userId) {
if (!colors) {
Toasts.show({
type: Toasts.Type.FAILURE,
message: "No profile colors found!",
message: t("copyProfileColors.toast.noColorsFound"),
id: Toasts.genId()
});
return;
@ -49,14 +50,14 @@ function copyProfileColors(userId) {
copyToClipboard(formattedColors);
Toasts.show({
type: Toasts.Type.SUCCESS,
message: "Profile colors copied to clipboard!",
message: t("copyProfileColors.toast.copied"),
id: Toasts.genId()
});
} catch (e) {
console.error("Failed to copy to clipboard:", e);
Toasts.show({
type: Toasts.Type.FAILURE,
message: "Error copying profile colors!",
message: t("copyProfileColors.toast.error"),
id: Toasts.genId()
});
}
@ -81,7 +82,7 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }) =
<Menu.MenuItem
id="CopyProfileColors"
icon={ColorIcon}
label="Copy Profile Colors"
label={t("copyProfileColors.contextMenu.copyProfileColors")}
action={() => copyProfileColors(user.id)}
/>
);
@ -89,7 +90,7 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }) =
export default definePlugin({
name: "CopyProfileColors",
description: "A plugin to copy people's profile gradient colors to clipboard.",
description: t("copyProfileColors.description"),
authors: [EquicordDevs.Crxa, EquicordDevs.Cortex],
contextMenus: {
"user-context": userContextMenuPatch,

View file

@ -6,6 +6,7 @@
import { copyToClipboard } from "@utils/clipboard";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { User } from "@vencord/discord-types";
import { findByCodeLazy } from "@webpack";
@ -22,7 +23,7 @@ const getMetadataFromApi: (activity: any, userId: string) => Promise<any> = find
export default definePlugin({
name: "CopyStatusUrls",
description: "Copy the users status url when you right-click it",
description: t("copyStatusUrls.description"),
authors: [Devs.sadan],
patches: [
@ -45,7 +46,7 @@ export default definePlugin({
copyToClipboard(button_urls[index]);
Toasts.show({
id: Toasts.genId(),
message: "Copied URL",
message: t("copyStatusUrls.toast.copied"),
type: Toasts.Type.SUCCESS,
options: {
position: Toasts.Position.TOP
@ -55,7 +56,7 @@ export default definePlugin({
console.error(e);
Toasts.show({
id: Toasts.genId(),
message: "Error copying URL, check console for more info",
message: t("copyStatusUrls.toast.error"),
type: Toasts.Type.FAILURE,
options: {
position: Toasts.Position.TOP

View file

@ -7,6 +7,7 @@
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { copyToClipboard } from "@utils/clipboard";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import type { Channel, User } from "@vencord/discord-types";
import { Menu } from "@webpack/common";
@ -37,7 +38,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U
children.push(
<Menu.MenuItem
id="vc-copy-user-mention"
label="Copy User Mention"
label={t("copyUserMention.contextMenu.copyUserMention")}
action={() => copyToClipboard(`<@${user.id}>`)}
icon={MentionIcon}
/>
@ -47,7 +48,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U
export default definePlugin({
name: "CopyUserMention",
authors: [EquicordDevs.Cortex, Devs.castdrian],
description: "Adds a button to copy user's mention on the user context menu, works best with ValidUser.",
description: t("copyUserMention.description"),
contextMenus: {
"user-context": UserContextMenuPatch
}

View file

@ -12,6 +12,7 @@ import { Divider } from "@components/Divider";
import { Heading } from "@components/Heading";
import { Devs, EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ColorPicker } from "@webpack/common";
@ -54,7 +55,7 @@ function OnekoColorSettings() {
<div>
<div className={cl("color-modal")}>
<div>
<Heading className="form-subtitle">Fur Color</Heading>
<Heading className="form-subtitle">{t("cursorBuddy.ui.furColor")}</Heading>
<div className={cl("color")}>
<ColorPicker
color={parseHexToNumber(furColor) ?? 16777215}
@ -65,7 +66,7 @@ function OnekoColorSettings() {
</div>
<div>
<Heading className="form-subtitle">Outline Color</Heading>
<Heading className="form-subtitle">{t("cursorBuddy.ui.outlineColor")}</Heading>
<div className={cl("color")}>
<ColorPicker
color={parseHexToNumber(outlineColor) ?? 0}
@ -81,30 +82,30 @@ function OnekoColorSettings() {
const settings = definePluginSettings({
buddy: {
description: "Pick a cursor buddy",
description: t("cursorBuddy.settings.buddy"),
type: OptionType.SELECT,
options: [
{
label: "Oneko",
label: t("cursorBuddy.settings.buddyOptions.oneko"),
value: "oneko",
default: true
},
{
label: "Fatass Horse",
label: t("cursorBuddy.settings.buddyOptions.fathorse"),
value: "fathorse"
}
],
onChange: load,
},
speed: {
description: "Speed of your buddy",
description: t("cursorBuddy.settings.speed"),
type: OptionType.NUMBER,
default: 10,
isValid: (value: number) => value >= 0 || "Speed must be bigger than 0",
onChange: load,
},
fps: {
description: "Framerate of your buddy",
description: t("cursorBuddy.settings.fps"),
type: OptionType.NUMBER,
default: 24,
isValid: (value: number) => value > 0 || "Framerate must be bigger than 0",
@ -125,14 +126,14 @@ const settings = definePluginSettings({
component: OnekoColorSettings,
},
furColor: {
description: "Fur hex color for Oneko",
description: t("cursorBuddy.settings.furColor"),
type: OptionType.STRING,
default: "#FFFFFF",
onChange: load,
hidden: true,
},
outlineColor: {
description: "Outline hex color for Oneko",
description: t("cursorBuddy.settings.outlineColor"),
type: OptionType.STRING,
default: "#000000",
onChange: load,
@ -149,26 +150,26 @@ const settings = definePluginSettings({
),
},
size: {
description: "Size of the fatass horse",
description: t("cursorBuddy.settings.size"),
type: OptionType.NUMBER,
default: 120,
isValid: (value: number) => value > 0 || "Size must be bigger than 0",
onChange: load
},
fade: {
description: "If the horse should fade when the cursor is near",
description: t("cursorBuddy.settings.fade"),
type: OptionType.BOOLEAN,
default: true,
onChange: load
},
freeroam: {
description: "If the horse should roam freely when idle",
description: t("cursorBuddy.settings.freeroam"),
type: OptionType.BOOLEAN,
default: true,
onChange: load
},
shake: {
description: "If the horse should shake the window when it's walking",
description: t("cursorBuddy.settings.shake"),
type: OptionType.BOOLEAN,
default: false,
onChange: load,
@ -192,7 +193,7 @@ const settings = definePluginSettings({
disabled() { return this.store.buddy !== "fathorse"; },
},
shake: {
disabled() { return this.store.buddy !== "fathorse"; },
disabled() { return this.store.buddy !== "fathorse"; }
}
});
@ -234,7 +235,7 @@ function load() {
migratePluginSettings("CursorBuddy", "Oneko", "oneko");
export default definePlugin({
name: "CursorBuddy",
description: "Adds a sprite that follows your cursor.",
description: t("cursorBuddy.description"),
authors: [Devs.Ven, Devs.adryd, EquicordDevs.nexpid, EquicordDevs.ZcraftElite],
tags: ["Oneko", "FatassHorse", "Pet"],
settings,

View file

@ -5,6 +5,7 @@
*/
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { makeContextItem } from "./components";
@ -13,7 +14,7 @@ import { folderProp, int2rgba } from "./util";
export default definePlugin({
name: "CustomFolderIcons",
description: "Customize folder icons with any png",
description: t("customFolderIcons.description"),
authors: [EquicordDevs.sadan],
settings,
patches: [

View file

@ -12,6 +12,7 @@ import { definePluginSettings } from "@api/Settings";
import { Heading } from "@components/Heading";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType, StartAt } from "@utils/types";
import { Button, React, showToast, TextInput } from "@webpack/common";
@ -245,7 +246,7 @@ const settings = definePluginSettings({
});
dataUriCache.clear();
setResetTrigger(prev => prev + 1);
showToast("All overrides reset successfully!");
showToast(t("customSounds.toast.resetSuccess"));
};
const triggerFileUpload = () => {
@ -277,10 +278,10 @@ const settings = definePluginSettings({
}
setResetTrigger(prev => prev + 1);
showToast("Settings imported successfully!");
showToast(t("customSounds.toast.importSuccess"));
} catch (error) {
console.error("Error importing settings:", error);
showToast("Error importing settings. Check console for details.");
showToast(t("customSounds.toast.importError"));
}
};
@ -314,7 +315,7 @@ const settings = definePluginSettings({
a.click();
URL.revokeObjectURL(url);
showToast(`Exported ${overrides.length} settings (audio files not included)`);
showToast(t("customSounds.toast.exportSuccess", { count: overrides.length }));
};
const filteredSoundTypes = allSoundTypes.filter(type =>
@ -325,10 +326,10 @@ const settings = definePluginSettings({
return (
<div>
<div className="vc-custom-sounds-buttons">
<Button color={Button.Colors.BRAND} onClick={triggerFileUpload}>Import</Button>
<Button color={Button.Colors.PRIMARY} onClick={downloadSettings}>Export</Button>
<Button color={Button.Colors.RED} onClick={resetOverrides}>Reset All</Button>
<Button color={Button.Colors.WHITE} onClick={debugCustomSounds}>Debug</Button>
<Button color={Button.Colors.BRAND} onClick={triggerFileUpload}>{t("customSounds.settings.import")}</Button>
<Button color={Button.Colors.PRIMARY} onClick={downloadSettings}>{t("customSounds.settings.export")}</Button>
<Button color={Button.Colors.RED} onClick={resetOverrides}>{t("customSounds.settings.resetAll")}</Button>
<Button color={Button.Colors.WHITE} onClick={debugCustomSounds}>{t("customSounds.settings.debug")}</Button>
<input
ref={fileInputRef}
type="file"
@ -339,11 +340,11 @@ const settings = definePluginSettings({
</div>
<div className={cl("search")}>
<Heading>Search Sounds</Heading>
<Heading>{t("customSounds.settings.searchSounds")}</Heading>
<TextInput
value={searchQuery}
onChange={e => setSearchQuery(e)}
placeholder="Search by name or ID"
placeholder={t("customSounds.settings.searchPlaceholder")}
/>
</div>
@ -365,7 +366,7 @@ const settings = definePluginSettings({
await ensureDataURICached(currentOverride.selectedFileId);
} catch (error) {
console.error(`[CustomSounds] Failed to cache data URI for ${type.id}:`, error);
showToast("Error loading custom sound file");
showToast(t("customSounds.toast.loadError"));
}
}
@ -392,7 +393,7 @@ export function findOverride(id: string): SoundOverride | null {
export default definePlugin({
name: "CustomSounds",
description: "Customize Discord's sounds.",
description: t("customSounds.description"),
authors: [Devs.ScattrdBlade, Devs.TheKodeToad],
settings,
startAt: StartAt.Init,

View file

@ -14,6 +14,7 @@ import { Paragraph } from "@components/Paragraph";
import { Devs, EquicordDevs } from "@utils/constants";
import { Margins } from "@utils/margins";
import { useForceUpdater } from "@utils/react";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { moment, TextInput, useEffect, useState } from "@webpack/common";
@ -74,7 +75,7 @@ const TimeRow = (props: TimeRowProps) => {
const settings = definePluginSettings({
formats: {
type: OptionType.COMPONENT,
description: "Customize the timestamp formats",
description: t("customTimestamps.settings.formats"),
component: componentProps => {
const [settingsState, setSettingsState] = useState(useSettings().plugins?.CustomTimestamps?.formats ?? {});
@ -92,9 +93,9 @@ const settings = definePluginSettings({
{key === "sameDayFormat" && (
<div className={Margins.bottom20}>
<Divider style={{ marginBottom: "10px" }} />
<Heading tag="h1">Calendar formats</Heading>
<Heading tag="h1">{t("customTimestamps.settings.calendarFormats")}</Heading>
<Paragraph>
How to format the [calendar] value if used in the above timestamps.
{t("customTimestamps.settings.calendarFormatsDescription")}
</Paragraph>
</div>
)}
@ -124,19 +125,18 @@ const settings = definePluginSettings({
export default definePlugin({
name: "CustomTimestamps",
description: "Custom timestamps on messages and tooltips",
description: t("customTimestamps.description"),
authors: [Devs.Rini, EquicordDevs.nvhhr, EquicordDevs.Suffocate, Devs.Obsidian],
settings,
settingsAboutComponent: () => (
<div className={"vc-cmt-info-card"}>
<HeadingPrimary>How to use:</HeadingPrimary>
<HeadingPrimary>{t("customTimestamps.about.title")}</HeadingPrimary>
<Paragraph>
<Link href="https://momentjs.com/docs/#/displaying/format/">Moment.js formatting documentation</Link>
<div className={Margins.top8}>
Additionally you can use these in your inputs:<br />
<b>[calendar]</b> enables dynamic date formatting such
as &quot;Today&quot; or &quot;Yesterday&quot;.<br />
<b>[relative]</b> gives you times such as &quot;4 hours ago&quot;.<br />
{t("customTimestamps.about.additional")}<br />
<b>[calendar]</b> {t("customTimestamps.about.calendar")}<br />
<b>[relative]</b> {t("customTimestamps.about.relative")}<br />
</div>
</Paragraph>
</div>

View file

@ -11,6 +11,7 @@ import { get } from "@api/DataStore";
import { definePluginSettings, Settings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Channel, User } from "@vencord/discord-types";
import { extractAndLoadChunksLazy } from "@webpack";
@ -46,7 +47,7 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: {
const setCustomColorItem = (
<Menu.MenuItem
label="Set Color"
label={t("customUserColors.contextMenu.setColor")}
id="set-color"
icon={ColorIcon}
action={async () => {
@ -64,7 +65,7 @@ const channelContextMenuPatch: NavContextMenuPatchCallback = (children, { channe
const setCustomColorItem = (
<Menu.MenuItem
label="Set Color"
label={t("customUserColors.contextMenu.setColor")}
id="set-color"
icon={ColorIcon}
action={async () => {
@ -87,19 +88,19 @@ export function getCustomColorString(id: string | undefined, withHash?: boolean)
const settings = definePluginSettings({
dmList: {
type: OptionType.BOOLEAN,
description: "Users with custom colors defined will have their name in the dm list colored",
description: t("customUserColors.settings.dmList"),
default: true,
},
colorInServers: {
type: OptionType.BOOLEAN,
description: "If name colors should be changed within servers",
description: t("customUserColors.settings.colorInServers"),
default: true,
}
});
export default definePlugin({
name: "CustomUserColors",
description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere",
description: t("customUserColors.description"),
authors: [EquicordDevs.mochienya],
contextMenus: {
"user-context": userContextMenuPatch,

View file

@ -5,11 +5,12 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
export default definePlugin({
name: "DisableCameras",
description: "Disables cameras in a call by default",
description: t("disableCameras.description"),
authors: [Devs.Joona],
patches: [
{

View file

@ -7,13 +7,14 @@
import "./styles.css";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { makeDevBanner, settings } from "./components";
export default definePlugin({
name: "DiscordDevBanner",
description: "Enables the Discord developer banner, in which displays the build-ID",
description: t("discordDevBanner.description"),
authors: [EquicordDevs.KrystalSkull, Devs.thororen],
settings,
patches: [

View file

@ -1,26 +1,15 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2024 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { classes } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { findByPropsLazy, findCssClassesLazy } from "@webpack";
import { useDrag, useDrop, useLayoutEffect, useRef, UserSettingsActionCreators } from "@webpack/common";
@ -44,7 +33,7 @@ type EmojiDescriptor = {
export default definePlugin({
name: "DragFavoriteEmotes",
authors: [EquicordDevs.PWall],
description: "Adds the ability to change the order of your favourite emotes",
description: t("dragFavoriteEmotes.description"),
patches: [
{
find: "#{intl::EMOJI_FAVORITE_TOOLTIP}",

View file

@ -10,6 +10,7 @@ import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { getGuildAcronym, insertTextIntoChatInputBox } from "@utils/discord";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import type { Channel } from "@vencord/discord-types";
import { ChannelType } from "@vencord/discord-types/enums";
@ -74,61 +75,61 @@ function shouldIgnoreDrop(key: string): boolean {
const settings = definePluginSettings({
userOutput: {
type: OptionType.SELECT,
description: "User drop output.",
description: t("dragify.settings.userOutput"),
options: [
{ label: "Mention user", value: "mention", default: true },
{ label: "User ID", value: "id" },
{ label: t("dragify.settings.userOutputOptions.mention"), value: "mention", default: true },
{ label: t("dragify.settings.userOutputOptions.id"), value: "id" },
],
},
channelOutput: {
type: OptionType.SELECT,
description: "Channel drop output.",
description: t("dragify.settings.channelOutput"),
options: [
{ label: "#channel mention", value: "mention", default: true },
{ label: "Channel link", value: "link" },
{ label: t("dragify.settings.channelOutputOptions.mention"), value: "mention", default: true },
{ label: t("dragify.settings.channelOutputOptions.link"), value: "link" },
],
},
inviteExpireAfter: {
type: OptionType.SELECT,
description: "Invite expiration.",
description: t("dragify.settings.inviteExpireAfter"),
options: [
{ label: "30 minutes", value: 1800 },
{ label: "1 hour", value: 3600 },
{ label: "6 hours", value: 21600 },
{ label: "12 hours", value: 43200 },
{ label: "1 day", value: 86400 },
{ label: "7 days", value: 604800 },
{ label: "Never", value: 0, default: true },
{ label: t("dragify.settings.inviteExpireAfterOptions.30min"), value: 1800 },
{ label: t("dragify.settings.inviteExpireAfterOptions.1hour"), value: 3600 },
{ label: t("dragify.settings.inviteExpireAfterOptions.6hours"), value: 21600 },
{ label: t("dragify.settings.inviteExpireAfterOptions.12hours"), value: 43200 },
{ label: t("dragify.settings.inviteExpireAfterOptions.1day"), value: 86400 },
{ label: t("dragify.settings.inviteExpireAfterOptions.7days"), value: 604800 },
{ label: t("dragify.settings.inviteExpireAfterOptions.never"), value: 0, default: true },
],
},
inviteMaxUses: {
type: OptionType.SELECT,
description: "Invite max uses.",
description: t("dragify.settings.inviteMaxUses"),
options: [
{ label: "No limit", value: 0, default: true },
{ label: "1 use", value: 1 },
{ label: "5 uses", value: 5 },
{ label: "10 uses", value: 10 },
{ label: "25 uses", value: 25 },
{ label: "50 uses", value: 50 },
{ label: "100 uses", value: 100 },
{ label: t("dragify.settings.inviteMaxUsesOptions.noLimit"), value: 0, default: true },
{ label: t("dragify.settings.inviteMaxUsesOptions.1use"), value: 1 },
{ label: t("dragify.settings.inviteMaxUsesOptions.5uses"), value: 5 },
{ label: t("dragify.settings.inviteMaxUsesOptions.10uses"), value: 10 },
{ label: t("dragify.settings.inviteMaxUsesOptions.25uses"), value: 25 },
{ label: t("dragify.settings.inviteMaxUsesOptions.50uses"), value: 50 },
{ label: t("dragify.settings.inviteMaxUsesOptions.100uses"), value: 100 },
],
},
inviteTemporaryMembership: {
type: OptionType.BOOLEAN,
default: false,
description: "Grant temporary membership.",
description: t("dragify.settings.inviteTemporaryMembership"),
},
reuseExistingInvites: {
type: OptionType.BOOLEAN,
default: false,
description: "Reuse existing invite instead of creating a new one.",
description: t("dragify.settings.reuseExistingInvites"),
},
allowChatBodyDrop: {
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
description: "Allow dropping into the main chat body to insert text.",
description: t("dragify.settings.allowChatBodyDrop"),
},
});
@ -159,7 +160,7 @@ const inviteCache = new Map<string, { code: string; expiresAt: number | null; ma
export default definePlugin({
name: "Dragify",
description: "Drop users, channels, or servers into chat to insert mentions or invites.",
description: t("dragify.description"),
authors: [EquicordDevs.justjxke],
settings,
@ -340,7 +341,7 @@ export default definePlugin({
clearDragState();
} catch (error) {
logger.error("Failed handling drop", error);
showToast("Dragify failed to handle drop.", Toasts.Type.FAILURE);
showToast(t("dragify.toast.dropFailed"), Toasts.Type.FAILURE);
}
},
@ -385,11 +386,11 @@ export default definePlugin({
const inviteChannelId = inviteChannel?.id ?? fallbackChannelId;
if (!inviteChannelId) {
showToast("No channel available for invites.", Toasts.Type.FAILURE);
showToast(t("dragify.toast.noChannelForInvites"), Toasts.Type.FAILURE);
return null;
}
if (inviteChannel && inviteChannel.guild_id !== guildId) {
showToast("No channel available for invites.", Toasts.Type.FAILURE);
showToast(t("dragify.toast.noChannelForInvites"), Toasts.Type.FAILURE);
return null;
}
@ -413,11 +414,11 @@ export default definePlugin({
maxUses: maxUses === 0 ? null : maxUses,
uses: 0,
});
showToast("Invite created.", Toasts.Type.SUCCESS);
showToast(t("dragify.toast.inviteCreated"), Toasts.Type.SUCCESS);
return `https://discord.gg/${code}`;
} catch (error) {
logger.error("Failed to create invite", error);
showToast("Unable to create invite.", Toasts.Type.FAILURE); // uh oh!
showToast(t("dragify.toast.inviteFailed"), Toasts.Type.FAILURE); // uh oh!
return null;
}
},

View file

@ -11,6 +11,7 @@ import { Button } from "@components/Button";
import { Devs, IS_MAC } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { copyWithToast } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy } from "@webpack";
import { useEffect, useState } from "@webpack/common";
@ -79,14 +80,14 @@ function KeybindRecorder() {
className={cl("keybind-button", isListening ? "listening" : "")}
onClick={() => setIsListening(true)}
>
{isListening ? "Recording..." : <KeybindShortcut shortcut={currentKeybind.join("+")} />}
{isListening ? t("elementHighlighter.ui.recording") : <KeybindShortcut shortcut={currentKeybind.join("+")} />}
</button>
<Button
size="small"
variant="secondary"
onClick={() => { settings.store.keybind = DEFAULT_KEYBIND; }}
>
Reset
{t("elementHighlighter.ui.reset")}
</Button>
</div>
);
@ -94,48 +95,48 @@ function KeybindRecorder() {
const settings = definePluginSettings({
keybind: {
description: "Toggle Highlighter",
description: t("elementHighlighter.settings.keybind"),
type: OptionType.COMPONENT,
default: DEFAULT_KEYBIND,
component: KeybindRecorder
},
showClasses: {
description: "Display the element's CSS class names in the tooltip",
description: t("elementHighlighter.settings.showClasses"),
type: OptionType.BOOLEAN,
default: false
},
showId: {
description: "Display the element's ID attribute in the tooltip",
description: t("elementHighlighter.settings.showId"),
type: OptionType.BOOLEAN,
default: false
},
showFont: {
description: "Display the computed font family and font size",
description: t("elementHighlighter.settings.showFont"),
type: OptionType.BOOLEAN,
default: false
},
showPadding: {
description: "Display the element's padding values",
description: t("elementHighlighter.settings.showPadding"),
type: OptionType.BOOLEAN,
default: false
},
showMargin: {
description: "Display the element's margin values",
description: t("elementHighlighter.settings.showMargin"),
type: OptionType.BOOLEAN,
default: false
},
showBorderRadius: {
description: "Display the element's border-radius values",
description: t("elementHighlighter.settings.showBorderRadius"),
type: OptionType.BOOLEAN,
default: false
},
showPosition: {
description: "Display the element's CSS position type and z-index",
description: t("elementHighlighter.settings.showPosition"),
type: OptionType.BOOLEAN,
default: false
},
showDisplay: {
description: "Display the element's display type along with flex or grid properties",
description: t("elementHighlighter.settings.showDisplay"),
type: OptionType.BOOLEAN,
default: false
}
@ -354,7 +355,7 @@ function onClick(e: MouseEvent) {
if (!el || el === overlay || el === tooltip) return;
const colorVar = getColorVar(el);
if (colorVar) copyWithToast(colorVar, "Copied color to clipboard!");
if (colorVar) copyWithToast(colorVar, t("elementHighlighter.ui.copiedColor"));
}
function onKeyDown(e: KeyboardEvent) {
@ -448,7 +449,7 @@ function onToggle(e: KeyboardEvent) {
export default definePlugin({
name: "ElementHighlighter",
description: "Highlight and inspect elements easily.",
description: t("elementHighlighter.description"),
authors: [Devs.prism],
settings,

View file

@ -11,6 +11,7 @@ import { definePluginSettings } from "@api/Settings";
import customRPC from "@plugins/customRPC";
import { Devs, EquicordDevs, GUILD_ID, SUPPORT_CHANNEL_ID, SUPPORT_CHANNEL_IDS, VC_SUPPORT_CHANNEL_IDS } from "@utils/constants";
import { isAnyPluginDev } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { StandingState } from "@vencord/discord-types/enums";
import { findByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
@ -28,11 +29,11 @@ const WarningIcon = findExportedComponentLazy("WarningIcon");
const ShieldIcon = findExportedComponentLazy("ShieldIcon");
const StandingConfig: Record<number, { label: string; hoverColor: string; Icon: ComponentType<any>; }> = {
[StandingState.ALL_GOOD]: { label: "All good!", hoverColor: "var(--status-positive)", Icon: ShieldIcon },
[StandingState.LIMITED]: { label: "Limited", hoverColor: "var(--status-warning)", Icon: WarningIcon },
[StandingState.VERY_LIMITED]: { label: "Very limited", hoverColor: "var(--orange-345)", Icon: WarningIcon },
[StandingState.AT_RISK]: { label: "At risk", hoverColor: "var(--status-danger)", Icon: WarningIcon },
[StandingState.SUSPENDED]: { label: "Suspended", hoverColor: "var(--interactive-muted)", Icon: WarningIcon },
[StandingState.ALL_GOOD]: { label: t("equicordHelper.standing.allGood"), hoverColor: "var(--status-positive)", Icon: ShieldIcon },
[StandingState.LIMITED]: { label: t("equicordHelper.standing.limited"), hoverColor: "var(--status-warning)", Icon: WarningIcon },
[StandingState.VERY_LIMITED]: { label: t("equicordHelper.standing.veryLimited"), hoverColor: "var(--orange-345)", Icon: WarningIcon },
[StandingState.AT_RISK]: { label: t("equicordHelper.standing.atRisk"), hoverColor: "var(--status-danger)", Icon: WarningIcon },
[StandingState.SUSPENDED]: { label: t("equicordHelper.standing.suspended"), hoverColor: "var(--interactive-muted)", Icon: WarningIcon },
};
function StandingButton() {
@ -61,42 +62,42 @@ function StandingButton() {
const settings = definePluginSettings({
noMirroredCamera: {
type: OptionType.BOOLEAN,
description: "Prevents the camera from being mirrored on your screen",
description: t("equicordHelper.settings.noMirroredCamera"),
restartNeeded: true,
default: false,
},
removeActivitySection: {
type: OptionType.BOOLEAN,
description: "Removes the activity section above member list",
description: t("equicordHelper.settings.removeActivitySection"),
restartNeeded: true,
default: false,
},
showYourOwnActivityButtons: {
type: OptionType.BOOLEAN,
description: "Discord hides your own activity buttons for some reason",
description: t("equicordHelper.settings.showYourOwnActivityButtons"),
restartNeeded: true,
default: false,
},
noDefaultHangStatus: {
type: OptionType.BOOLEAN,
description: "Disable the default hang status when joining voice channels",
description: t("equicordHelper.settings.noDefaultHangStatus"),
restartNeeded: true,
default: false,
},
refreshSlashCommands: {
type: OptionType.BOOLEAN,
description: "Refreshes Slash Commands to show newly added commands without restarting your client.",
description: t("equicordHelper.settings.refreshSlashCommands"),
default: false,
},
forceRoleIcon: {
type: OptionType.BOOLEAN,
description: "Forces role icons to display next to messages in compact mode",
description: t("equicordHelper.settings.forceRoleIcon"),
restartNeeded: true,
default: false
},
accountStandingButton: {
type: OptionType.BOOLEAN,
description: "Show an account standing button in the header bar",
description: t("equicordHelper.settings.accountStandingButton"),
default: false,
restartNeeded: true,
}
@ -104,7 +105,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "EquicordHelper",
description: "Used to provide support, fix discord caused crashes, and other misc features.",
description: t("equicordHelper.description"),
authors: [Devs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu, EquicordDevs.keircn, EquicordDevs.SerStars, EquicordDevs.mart],
required: true,
settings,
@ -218,13 +219,13 @@ export default definePlugin({
if (!selfId || isAnyPluginDev(selfId)) return;
if (VC_SUPPORT_CHANNEL_IDS.includes(channelId) && !clicked) {
return Alerts.show({
title: "Vencord Support Channel Warning",
body: "Before asking for help. Check updates and if this issue is actually caused by Equicord!",
confirmText: "Equicord Support",
title: t("equicordHelper.alerts.supportWarningTitle"),
body: t("equicordHelper.alerts.supportWarningBody"),
confirmText: t("equicordHelper.alerts.equicordSupport"),
onConfirm() {
NavigationRouter.transitionTo(`/channels/${GUILD_ID}/${SUPPORT_CHANNEL_ID}`);
},
cancelText: "Okay continue",
cancelText: t("equicordHelper.alerts.okayContinue"),
onCancel() {
clicked = true;
},
@ -235,17 +236,17 @@ export default definePlugin({
commands: [
{
name: "refresh-commands",
description: "Refresh Slash Commands",
description: t("equicordHelper.commands.refreshCommands"),
inputType: ApplicationCommandInputType.BUILT_IN,
predicate: () => settings.store.refreshSlashCommands,
execute: async (opts, ctx) => {
try {
ApplicationCommandIndexStore.indices = {};
sendBotMessage(ctx.channel.id, { content: "Slash Commands refreshed successfully." });
sendBotMessage(ctx.channel.id, { content: t("equicordHelper.commands.refreshSuccess") });
}
catch (e) {
console.error("[refreshSlashCommands] Failed to refresh commands:", e);
sendBotMessage(ctx.channel.id, { content: "Failed to refresh commands. Check console for details." });
sendBotMessage(ctx.channel.id, { content: t("equicordHelper.commands.refreshFailed") });
}
}
}

View file

@ -21,6 +21,7 @@ import "./styles.css";
import { HeaderBarButton } from "@api/HeaderBar";
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Popout, useRef, useState } from "@webpack/common";
@ -30,7 +31,7 @@ export const settings = definePluginSettings({
showPluginMenu: {
type: OptionType.BOOLEAN,
default: true,
description: "Show the plugins menu in the toolbox",
description: t("equicordToolbox.settings.showPluginMenu"),
}
});
@ -64,7 +65,7 @@ function VencordPopoutButton() {
ref={buttonRef}
className="vc-toolbox-btn"
onClick={() => setShow(v => !v)}
tooltip={isShown ? null : "Equicord Toolbox"}
tooltip={isShown ? null : t("equicordToolbox.ui.tooltip")}
icon={Icon}
selected={isShown}
/>
@ -76,7 +77,7 @@ function VencordPopoutButton() {
migratePluginSettings("EquicordToolbox", "VencordToolbox");
export default definePlugin({
name: "EquicordToolbox",
description: "Adds a button next to the inbox button in the channel header that houses Equicord quick actions",
description: t("equicordToolbox.description"),
authors: [Devs.Ven, Devs.AutumnVN],
dependencies: ["HeaderBarAPI"],

View file

@ -10,6 +10,7 @@ import { Settings, useSettings } from "@api/Settings";
import { openPluginModal, openSettingsTabModal, PluginsTab, ThemesTab } from "@components/settings";
import { useAwaiter } from "@utils/react";
import { wordsFromCamel, wordsToTitle } from "@utils/text";
import { t } from "@utils/translation";
import { OptionType, Plugin } from "@utils/types";
import { Menu, showToast, useMemo, useState } from "@webpack/common";
import type { ReactNode } from "react";
@ -27,7 +28,7 @@ function buildPluginMenu() {
return (
<Menu.MenuItem
id="plugins"
label="Plugins"
label={t("equicordToolbox.ui.plugins")}
action={() => openSettingsTabModal(PluginsTab)}
>
{pluginEntries}
@ -103,7 +104,7 @@ export function buildPluginMenuEntries(includeEmpty = false) {
checked={s[key]}
action={() => {
s[key] = !s[key];
if (option.restartNeeded) showToast("Restart to apply the change");
if (option.restartNeeded) showToast(t("equicordToolbox.ui.restartToApply"));
}}
/>
);
@ -120,7 +121,7 @@ export function buildPluginMenuEntries(includeEmpty = false) {
checked={s[key] === opt.value}
action={() => {
s[key] = opt.value;
if (option.restartNeeded) showToast("Restart to apply the change");
if (option.restartNeeded) showToast(t("equicordToolbox.ui.restartToApply"));
}}
/>
))}
@ -188,7 +189,7 @@ export function buildThemeMenu() {
return (
<Menu.MenuItem
id="themes"
label="Themes"
label={t("equicordToolbox.ui.themes")}
action={() => openSettingsTabModal(ThemesTab)}
>
{buildThemeMenuEntries()}
@ -205,19 +206,19 @@ export function buildThemeMenuEntries() {
<Menu.MenuCheckboxItem
id="toggle-quickcss"
checked={useQuickCss}
label={"Enable QuickCSS"}
label={t("equicordToolbox.ui.enableQuickCss")}
action={() => {
Settings.useQuickCss = !useQuickCss;
}}
/>
<Menu.MenuItem
id="edit-quickcss"
label="Edit QuickCSS"
label={t("equicordToolbox.ui.editQuickCss")}
action={() => VencordNative.quickCss.openEditor()}
/>
<Menu.MenuItem
id="manage-themes"
label="Manage Themes"
label={t("equicordToolbox.ui.manageThemes")}
action={() => openSettingsTabModal(ThemesTab)}
/>
{!!themes?.length && (
@ -304,7 +305,7 @@ export function renderPopout(onClose: () => void) {
>
<Menu.MenuItem
id="notifications"
label="Open Notification Log"
label={t("equicordToolbox.ui.openNotificationLog")}
action={openNotificationLogModal}
/>

View file

@ -7,6 +7,7 @@
import { AudioPlayerInterface, createAudioPlayer } from "@api/AudioPlayer";
import { definePluginSettings } from "@api/Settings";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
let clickCount = 0;
@ -25,14 +26,14 @@ function play() {
const settings = definePluginSettings({
amount: {
type: OptionType.NUMBER,
description: "amount of clicks to trigger crossant",
description: t("equissant.settings.amount"),
default: 10,
}
});
export default definePlugin({
name: "Equissant",
description: "Crossant every specified amount of clicks :trolley:",
description: t("equissant.description"),
authors: [EquicordDevs.SomeAspy, Devs.thororen],
dependencies: ["AudioPlayerAPI"],
settings,

View file

@ -12,6 +12,7 @@ import { definePluginSettings } from "@api/Settings";
import { Button } from "@components/Button";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy } from "@webpack";
import { ChannelActions, Constants, GuildStore, IconUtils, MediaEngineStore, Menu, RestAPI, SearchableSelect, SelectedChannelStore, TextInput, Toasts } from "@webpack/common";
@ -31,7 +32,7 @@ function GuildSelector() {
<SearchableSelect
options={options}
value={options.find(o => o.value === soundGuildId)}
placeholder="Select a server..."
placeholder={t("exitSounds.ui.selectServer")}
maxVisibleItems={6}
closeOnSelect={true}
onChange={v => settings.store.soundGuildId = v}
@ -58,7 +59,7 @@ function SoundIdInput() {
<TextInput
value={soundId}
onChange={v => settings.store.soundId = v}
placeholder="Enter sound ID..."
placeholder={t("exitSounds.ui.enterSoundId")}
/>
</div>
<Button
@ -74,12 +75,12 @@ function SoundIdInput() {
const settings = definePluginSettings({
soundGuildId: {
type: OptionType.COMPONENT,
description: "Select the server containing the sound.",
description: t("exitSounds.settings.soundGuildId"),
component: GuildSelector
},
soundId: {
type: OptionType.COMPONENT,
description: "Enter the ID of the sound you want to play.",
description: t("exitSounds.settings.soundId"),
component: SoundIdInput
}
});
@ -89,7 +90,7 @@ const SoundButtonContext: NavContextMenuPatchCallback = (children, { sound }: {
<Menu.MenuGroup>
<Menu.MenuItem
id="set-global-exit-sound"
label="Set as global exit sound"
label={t("exitSounds.ui.setGlobalExitSound")}
action={() => {
settings.store.soundGuildId = sound.guildId;
settings.store.soundId = sound.soundId;
@ -103,7 +104,7 @@ let original: typeof ChannelActions.selectVoiceChannel;
export default definePlugin({
name: "ExitSounds",
description: "Play soundboard sounds when you disconnect from voice.",
description: t("exitSounds.description"),
authors: [Devs.prism],
dependencies: ["AudioPlayerAPI"],
settings,
@ -130,7 +131,7 @@ export default definePlugin({
await new Promise(r => setTimeout(r, 500));
} catch {
Toasts.show({
message: "Oops! Something went wrong.",
message: t("exitSounds.toasts.somethingWentWrong"),
id: Toasts.genId(),
type: Toasts.Type.FAILURE
});

View file

@ -12,6 +12,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { copyToClipboard } from "@utils/clipboard";
import { EquicordDevs } from "@utils/constants";
import { showItemInFolder } from "@utils/native";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { saveFile } from "@utils/web";
import { Message } from "@vencord/discord-types";
@ -22,12 +23,12 @@ import { ContactsList } from "./types";
const settings = definePluginSettings({
openFileAfterExport: {
type: OptionType.BOOLEAN,
description: "Open the exported file in the default file handler after export",
description: t("exportMessages.settings.openFileAfterExport"),
default: true
},
exportContacts: {
type: OptionType.BOOLEAN,
description: "Export a list of friends to your clipboard. Adds a new button to the menu bar for the friends tab.",
description: t("exportMessages.settings.exportContacts"),
default: false
}
});
@ -81,14 +82,14 @@ async function exportMessage(message: Message) {
}
showNotification({
title: "Export Messages",
body: `Message exported successfully as ${filename}`,
title: t("exportMessages.notifications.title"),
body: t("exportMessages.notifications.exportSuccess", { filename }),
icon: "📄"
});
} catch (error) {
showNotification({
title: "Export Messages",
body: "Failed to export message",
title: t("exportMessages.notifications.title"),
body: t("exportMessages.notifications.exportFailed"),
icon: "❌"
});
}
@ -102,7 +103,7 @@ const messageContextMenuPatch = (children: Array<React.ReactElement<any> | null>
children.push(
<Menu.MenuItem
id="export-message"
label="Export Message"
label={t("exportMessages.ui.exportMessage")}
icon={() => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
@ -128,7 +129,7 @@ function getUsernames(contacts: ContactsList[], type: number): string[] {
export default definePlugin({
name: "ExportMessages",
description: "Allows you to export any message to a file",
description: t("exportMessages.description"),
authors: [EquicordDevs.veygax, EquicordDevs.dat_insanity],
settings,
contextMenus: {
@ -165,14 +166,14 @@ export default definePlugin({
},
addExportButton() {
return <ErrorBoundary noop key=".2">
<button className="export-contacts-button" onClick={() => { this.copyContactToClipboard(); console.log("clicked"); }}>Export</button>
<button className="export-contacts-button" onClick={() => { this.copyContactToClipboard(); console.log("clicked"); }}>{t("exportMessages.ui.export")}</button>
</ErrorBoundary>;
},
copyContactToClipboard() {
if (this.contactList) {
copyToClipboard(JSON.stringify(this.contactList));
Toasts.show({
message: "Contacts copied to clipboard successfully.",
message: t("exportMessages.ui.contactsCopied"),
type: Toasts.Type.SUCCESS,
id: Toasts.genId(),
options: {
@ -182,11 +183,8 @@ export default definePlugin({
});
return;
}
// reason why you need to click the all tab is because the data is extracted during
// the request itself when you fetch all your friends. this is done to avoid sending a
// manual request to discord, which may raise suspicion and might even get you terminated.
Toasts.show({
message: "Contact list is undefined. Click on the \"All\" tab before exporting.",
message: t("exportMessages.ui.contactListUndefined"),
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {

View file

@ -6,6 +6,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Constants, PermissionsBits, PermissionStore, React, RestAPI, useCallback, useEffect, useState } from "@webpack/common";
@ -49,23 +50,23 @@ function showIcon() {
// TY ToggleVideoBind
const settings = definePluginSettings({
keyBind: {
description: "The key to toggle trash when pressed.",
description: t("fastDeleteChannels.settings.keyBind"),
type: OptionType.STRING,
default: "KeyZ",
isValid: (value: string) => validKeycodes.includes(value),
},
reqCtrl: {
description: "Require control to be held.",
description: t("fastDeleteChannels.settings.reqCtrl"),
type: OptionType.BOOLEAN,
default: true,
},
reqShift: {
description: "Require shift to be held.",
description: t("fastDeleteChannels.settings.reqShift"),
type: OptionType.BOOLEAN,
default: true,
},
reqAlt: {
description: "Require alt to be held.",
description: t("fastDeleteChannels.settings.reqAlt"),
type: OptionType.BOOLEAN,
default: false,
},
@ -73,7 +74,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "FastDeleteChannels",
description: "Adds a trash icon to delete channels",
description: t("fastDeleteChannels.description"),
authors: [Devs.thororen],
settings,
patches: [

View file

@ -5,6 +5,7 @@
*/
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Embed } from "@vencord/discord-types";
import { findComponentByCodeLazy, findCssClassesLazy, proxyLazyWebpack } from "@webpack";
@ -44,7 +45,7 @@ const EmbedContext = proxyLazyWebpack(() => React.createContext<null | Embed>(nu
export default definePlugin({
name: "FavouriteAnything",
description: "Favourite any image",
description: t("favouriteAnything.description"),
authors: [Devs.nin0dev, EquicordDevs.davri],
patches: [
{

View file

@ -6,6 +6,7 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Menu } from "@webpack/common";
@ -31,7 +32,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =
group.push(
<Menu.MenuItem
label={`Upload to ${serviceName}`}
label={t("fileUpload.ui.uploadTo", { serviceName })}
key="file-upload"
id="file-upload"
action={() => uploadFile(url)}
@ -56,7 +57,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =>
children.push(
<Menu.MenuGroup id="file-upload-group">
<Menu.MenuItem
label={`Upload to ${serviceName}`}
label={t("fileUpload.ui.uploadTo", { serviceName })}
key="file-upload"
id="file-upload"
action={() => uploadFile(url)}
@ -67,7 +68,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =>
export default definePlugin({
name: "FileUpload",
description: "Upload images and videos to file hosting services like Zipline and Nest",
description: t("fileUpload.description"),
authors: [EquicordDevs.creations, EquicordDevs.keircn],
settings,
contextMenus: {

View file

@ -19,6 +19,7 @@
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
@ -68,19 +69,19 @@ function findReplies(message: Message) {
const settings = definePluginSettings({
includePings: {
type: OptionType.BOOLEAN,
description: "Will also search for messages that @ the author directly",
description: t("findReply.settings.includePings"),
default: false,
restartNeeded: false
},
includeAuthor: {
type: OptionType.BOOLEAN,
description: "Will also search for messages that reply to the author in general, not just that exact message",
description: t("findReply.settings.includeAuthor"),
default: false,
restartNeeded: false
},
hideButtonIfNoReply: {
type: OptionType.BOOLEAN,
description: "Hides the button if there are no replies to the message",
description: t("findReply.settings.hideButtonIfNoReply"),
default: true,
restartNeeded: true
}
@ -88,7 +89,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "FindReply",
description: "Jumps to the earliest reply to a message in a channel (lets you follow past conversations more easily).",
description: t("findReply.description"),
authors: [Devs.newwares],
settings,
messagePopoverButton: {
@ -98,7 +99,7 @@ export default definePlugin({
const replies = findReplies(message);
if (settings.store.hideButtonIfNoReply && !replies.length) return null;
return {
label: "Jump to Reply",
label: t("findReply.ui.jumpToReply"),
icon: FindReplyIcon,
message,
channel: ChannelStore.getChannel(message.channel_id),
@ -115,14 +116,14 @@ export default definePlugin({
if (replies.length > 1) {
Toasts.show({
id: Toasts.genId(),
message: "Use the bottom panel to navigate between replies.",
message: t("findReply.toasts.useBottomPanel"),
type: Toasts.Type.MESSAGE
});
const container = document.querySelector("[class*=channelBottomBarArea_]");
if (!container) {
Toasts.show({
id: Toasts.genId(),
message: "Couldn't find the container element.",
message: t("findReply.toasts.containerNotFound"),
type: Toasts.Type.FAILURE
});
return;
@ -139,7 +140,7 @@ export default definePlugin({
} else {
Toasts.show({
id: Toasts.genId(),
message: "Couldn't find a reply.",
message: t("findReply.toasts.noReplyFound"),
type: Toasts.Type.FAILURE
});
}

View file

@ -7,6 +7,7 @@
import { isPluginEnabled } from "@api/PluginManager";
import anonymiseFileNames, { tarExtMatcher } from "@plugins/anonymiseFileNames";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { ReporterTestable } from "@utils/types";
import { CloudUpload } from "@vencord/discord-types";
@ -28,7 +29,7 @@ export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [ta
export default definePlugin({
name: "FixFileExtensions",
authors: [Devs.thororen],
description: "Fixes file extensions by renaming them to a compatible supported format if possible",
description: t("fixFileExtensions.description"),
reporterTestable: ReporterTestable.None,
patches: [
// Taken from AnonymiseFileNames

View file

@ -8,6 +8,7 @@ import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { definePluginSettings } from "@api/Settings";
import { Notice } from "@components/Notice";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Channel, User, VoiceState } from "@vencord/discord-types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
@ -34,12 +35,12 @@ const settings = definePluginSettings({
onlyWhenInVoice: {
type: OptionType.BOOLEAN,
default: true,
description: "Only follow the user when you are in a voice channel"
description: t("followVoiceUser.settings.onlyWhenInVoice")
},
leaveWhenUserLeaves: {
type: OptionType.BOOLEAN,
default: false,
description: "Leave the voice channel when the user leaves. (That can cause you to sometimes enter infinite leave/join loop)"
description: t("followVoiceUser.settings.leaveWhenUserLeaves")
}
});
@ -52,7 +53,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { channel,
<Menu.MenuSeparator />,
<Menu.MenuCheckboxItem
id="fvu-follow-user"
label="Follow User"
label={t("followVoiceUser.ui.followUser")}
checked={checked}
action={() => {
if (followedUserInfo?.userId === user.id) {
@ -73,12 +74,12 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { channel,
export default definePlugin({
name: "FollowVoiceUser",
description: "Follow a friend in voice chat.",
description: t("followVoiceUser.description"),
authors: [EquicordDevs.TheArmagan],
settings,
settingsAboutComponent: () => (
<Notice.Info>
This Plugin is used to follow a Friend/Friends into voice chat(s).
{t("followVoiceUser.info")}
</Notice.Info>
),
flux: {

View file

@ -14,6 +14,7 @@ import { debounce } from "@shared/debounce";
import { EquicordDevs } from "@utils/constants";
import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { React, TextInput } from "@webpack/common";
@ -134,13 +135,13 @@ function GoogleFontSearch({ onSelect }: { onSelect: (font: GoogleFontMetadata) =
return (
<section>
<HeadingSecondary>Search Google Fonts</HeadingSecondary>
<Paragraph>Click on any font to apply it.</Paragraph>
<HeadingSecondary>{t("fontLoader.ui.searchGoogleFonts")}</HeadingSecondary>
<Paragraph>{t("fontLoader.ui.clickToApply")}</Paragraph>
<TextInput
value={query}
onChange={e => handleSearch(e)}
placeholder="Search fonts..."
placeholder={t("fontLoader.ui.searchFonts")}
disabled={loading}
className={Margins.bottom16}
/>
@ -159,7 +160,7 @@ function GoogleFontSearch({ onSelect }: { onSelect: (font: GoogleFontMetadata) =
</div>
{font.authors?.length && (
<Paragraph className={Margins.top8} style={{ opacity: 0.7 }}>
by {font.authors.join(", ")}
{t("fontLoader.ui.by", { authors: font.authors.join(", ") })}
</Paragraph>
)}
</Card>
@ -192,14 +193,14 @@ const settings = definePluginSettings({
},
applyOnCodeBlocks: {
type: OptionType.BOOLEAN,
description: "Apply the font to code blocks",
description: t("fontLoader.settings.applyOnCodeBlocks"),
default: false
}
});
export default definePlugin({
name: "FontLoader",
description: "Loads any font from Google Fonts",
description: t("fontLoader.description"),
authors: [EquicordDevs.vmohammad],
settings,

View file

@ -7,24 +7,25 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { sendMessage } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Channel, Message } from "@vencord/discord-types";
// Taken From Signature :)
const settings = definePluginSettings({
forwardPreface: {
description: "What should forwarded from be prefaced with",
description: t("forwardAnywhere.settings.forwardPreface"),
type: OptionType.SELECT,
options: [
{ label: ">", value: ">", default: true },
{ label: "-#", value: "-#" }
{ label: t("forwardAnywhere.options.greaterThan"), value: ">", default: true },
{ label: t("forwardAnywhere.options.hash"), value: "-#" }
]
}
});
export default definePlugin({
name: "ForwardAnywhere",
description: "If a forward fails send it as a normal message also allows nsfw forwards",
description: t("forwardAnywhere.description"),
authors: [Devs.thororen],
settings,
patches: [

View file

@ -5,6 +5,7 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { ChannelStore, UserSettingsActionCreators } from "@webpack/common";
@ -36,7 +37,7 @@ function generateSearchResults(query) {
export default definePlugin({
name: "FrequentQuickSwitcher",
description: "Rewrites and filters the quick switcher results to be your most frequent channels",
description: t("frequentQuickSwitcher.description"),
authors: [Devs.Samwich],
generateSearchResults: generateSearchResults,
patches: [

View file

@ -19,6 +19,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { User } from "@vencord/discord-types";
import { findStore } from "@webpack";
@ -51,17 +52,17 @@ interface UserPosition {
export default definePlugin({
name: "FriendCloud",
description: "Adds a /friendcloud command to visualize the users you most interact with",
description: t("friendCloud.description"),
authors: [EquicordDevs.Fafa],
commands: [
{
inputType: ApplicationCommandInputType.BUILT_IN,
name: "friendcloud",
description: "Display user you most interact with in a cloud",
description: t("friendCloud.commands.friendcloud"),
options: [
{
name: "count",
description: "Number of users to display",
description: t("friendCloud.commands.count"),
type: ApplicationCommandOptionType.NUMBER,
required: false
}
@ -69,14 +70,14 @@ export default definePlugin({
execute: async (opts, cmdCtx) => {
const count = findOption(opts, "count", 25);
if (!count) return sendBotMessage(cmdCtx.channel.id, { content: "The count must be 1 or higher!" });
if (!count) return sendBotMessage(cmdCtx.channel.id, { content: t("friendCloud.errors.countMustBeHigher") });
try {
const affinities: AffinitiesV2[] = findStore("UserAffinitiesV2Store").getUserAffinities();
if (!affinities?.length) {
return sendBotMessage(cmdCtx.channel.id, {
content: "No affinities found. Check your [privacy settings](<https://support.discord.com/hc/en-us/articles/21864805694999-Data-Used-to-Improve-Discord>)."
content: t("friendCloud.errors.noAffinities")
});
}
@ -91,7 +92,7 @@ export default definePlugin({
if (!users.length) {
return sendBotMessage(cmdCtx.channel.id, {
content: "No valid users found in affinities. Check your [privacy settings](<https://support.discord.com/hc/en-us/articles/21864805694999-Data-Used-to-Improve-Discord>)."
content: t("friendCloud.errors.noValidUsers")
});
}
@ -153,7 +154,7 @@ export default definePlugin({
if (loadedImages === totalImages) {
canvas.toBlob(blob => {
if (!blob) {
sendBotMessage(cmdCtx.channel.id, { content: "Couldn't generate the image :c" });
sendBotMessage(cmdCtx.channel.id, { content: t("friendCloud.errors.couldntGenerate") });
return;
}
const file = new File([blob], "affinities-cloud.png", { type: "image/png" });

View file

@ -13,6 +13,7 @@ import { BaseText } from "@components/BaseText";
import { Divider } from "@components/Divider";
import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/react";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Button, ChannelStore, Menu, RelationshipStore, TextInput, useEffect, UserStore, useState } from "@webpack/common";
@ -99,12 +100,12 @@ function TagConfigCard(props) {
return (
<>
<BaseText size="md" tag="h5">Name</BaseText>
<BaseText size="md" tag="h5">{t("friendTags.ui.name")}</BaseText>
<TextInput value={tagName} onChange={setTagName}></TextInput>
<BaseText size="md" tag="h5">Users (Seperated by comma)</BaseText>
<BaseText size="md" tag="h5">{t("friendTags.ui.usersSeperated")}</BaseText>
<TextInput value={userIds} onChange={setUserIDs}></TextInput>
<div className={"vc-friend-tags-user-header-container"}>
<BaseText>User List (Click A User To Remove)</BaseText>
<BaseText>{t("friendTags.ui.userList")}</BaseText>
<div className={"vc-friend-tags-user-header-btns"}>
{
userIds.split(", ").map(user => {
@ -128,7 +129,7 @@ function TagConfigCard(props) {
}}
color={Button.Colors.RED}
>
Remove
{t("friendTags.ui.remove")}
</Button>
</>
);
@ -156,7 +157,7 @@ function TagConfigurationComponent() {
});
SetData();
update();
}}>Add</Button>
}}>{t("friendTags.ui.add")}</Button>
</>
);
}
@ -187,14 +188,14 @@ const userPatch: NavContextMenuPatchCallback = (children, { user }) => {
const buttonElement =
<Menu.MenuItem
id="vc-tag-group"
label="Tag"
label={t("friendTags.ui.tag")}
>
{SavedData.map(tag => {
const isTagged = SavedData.filter(e => e.tagName === tag.tagName)[0].userIds.includes(user.id);
return (
<Menu.MenuItem
label={`${isTagged ? "Remove from" : "Add to"} ${tag.tagName}`}
label={isTagged ? t("friendTags.ui.removeFrom", { tagName: tag.tagName }) : t("friendTags.ui.addTo", { tagName: tag.tagName })}
key={`vc-tag-${tag.tagName}`}
id={`vc-tag-${tag.tagName}`}
action={() => { UserToTagID(user.id, tag.tagName, isTagged); }}
@ -208,7 +209,7 @@ const userPatch: NavContextMenuPatchCallback = (children, { user }) => {
export default definePlugin({
name: "FriendTags",
description: "Allows you to filter by custom tags in the quick switcher by starting a search with &",
description: t("friendTags.description"),
authors: [Devs.Samwich],
settings,
contextMenus: {

View file

@ -14,6 +14,7 @@ import { Paragraph } from "@components/Paragraph";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Forms, RelationshipStore } from "@webpack/common";
@ -40,44 +41,44 @@ function daysSince(dateString: string): number {
const ranks: rankInfo[] =
[
{
title: "Sprout",
description: "Your friendship is just starting",
title: t("friendshipRanks.ranks.sprout.title"),
description: t("friendshipRanks.ranks.sprout.description"),
requirement: 0,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/sprout.png"
},
{
title: "Blooming",
description: "Your friendship is getting there! (1 Month)",
title: t("friendshipRanks.ranks.blooming.title"),
description: t("friendshipRanks.ranks.blooming.description"),
requirement: 30,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/blooming.png"
},
{
title: "Burning",
description: "Your friendship has reached terminal velocity (3 Months)",
title: t("friendshipRanks.ranks.burning.title"),
description: t("friendshipRanks.ranks.burning.description"),
requirement: 90,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/burning.png"
},
{
title: "Fighter",
description: "Your friendship is strong (6 Months)",
title: t("friendshipRanks.ranks.fighter.title"),
description: t("friendshipRanks.ranks.fighter.description"),
requirement: 182.5,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/fighter.png"
},
{
title: "Star",
description: "Your friendship has been going on for a WHILE (1 Year)",
title: t("friendshipRanks.ranks.star.title"),
description: t("friendshipRanks.ranks.star.description"),
requirement: 365,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/star.png"
},
{
title: "Royal",
description: "Your friendship has gone through thick and thin- a whole 2 years!",
title: t("friendshipRanks.ranks.royal.title"),
description: t("friendshipRanks.ranks.royal.description"),
requirement: 730,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/royal.png"
},
{
title: "Besties",
description: "How do you even manage this??? (5 Years)",
title: t("friendshipRanks.ranks.besties.title"),
description: t("friendshipRanks.ranks.besties.description"),
requirement: 1826.25,
iconSrc: "https://equicord.org/assets/plugins/friendshipRanks/besties.png"
}
@ -142,7 +143,7 @@ function getBadgesToApply() {
export default definePlugin({
name: "FriendshipRanks",
description: "Adds badges showcasing how long you have been friends with a user for",
description: t("friendshipRanks.description"),
authors: [Devs.Samwich],
start() {
getBadgesToApply().forEach(b => Badges.addProfileBadge(b));

View file

@ -6,6 +6,7 @@
import { disableStyle, enableStyle } from "@api/Styles";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { ChannelRTCStore, IconUtils, UserStore, VoiceStateStore } from "@webpack/common";
@ -13,7 +14,7 @@ import style from "./style.css?managed";
export default definePlugin({
name: "FullVCPFP",
description: "Makes avatars take up the entire vc tile",
description: t("fullVcPfp.description"),
authors: [EquicordDevs.mochienya],
patches: [
{

View file

@ -7,6 +7,7 @@
import { definePluginSettings } from "@api/Settings";
import { Paragraph } from "@components/Paragraph";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { ActivityFlags, ActivityType } from "@vencord/discord-types/enums";
@ -31,7 +32,7 @@ function getImageAsset(data: string) {
const settings = definePluginSettings({
refreshInterval: {
type: OptionType.SLIDER,
description: "The interval between activity refreshes (seconds)",
description: t("gensokyoRadioRPC.settings.refreshInterval"),
markers: [1, 2, 2.5, 3, 5, 10, 15],
default: 15,
restartNeeded: true,
@ -42,14 +43,14 @@ let updateInterval: NodeJS.Timeout | undefined;
export default definePlugin({
name: "GensokyoRadioRPC",
description: "Discord rich presence for Gensokyo Radio!",
description: t("gensokyoRadioRPC.description"),
authors: [Devs.RyanCaoDev, EquicordDevs.Prince527],
reporterTestable: ReporterTestable.None,
settingsAboutComponent() {
return (
<Paragraph>
Discord rich presence for Gensokyo Radio!
{t("gensokyoRadioRPC.info")}
</Paragraph>
);
},

View file

@ -13,6 +13,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs, EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { closeModal, openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Channel } from "@vencord/discord-types";
import { Menu, Tooltip, useEffect, useState } from "@webpack/common";
@ -26,30 +27,30 @@ export const cl = classNameFactory("vc-boo-");
export const settings = definePluginSettings({
showIndicator: {
type: OptionType.BOOLEAN,
description: "Show the ghost counter at the top of the server list",
description: t("ghosted.settings.showIndicator"),
default: true,
restartNeeded: false
},
showDmIcons: {
type: OptionType.BOOLEAN,
description: "Show ghost icons next to individual DMs",
description: t("ghosted.settings.showDmIcons"),
default: true,
restartNeeded: false
},
ignoreGroupDms: {
type: OptionType.BOOLEAN,
description: "Exclude all group dms from ghosting",
description: t("ghosted.settings.ignoreGroupDms"),
default: false
},
exemptedChannels: {
type: OptionType.STRING,
description: "Comma-separated list of channel IDs to exempt from ghosting (right-click a DM channel to copy its ID)",
description: t("ghosted.settings.exemptedChannels"),
default: "",
restartNeeded: false
},
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Ignore DMs from bots",
description: t("ghosted.settings.ignoreBots"),
default: true,
restartNeeded: false
}
@ -88,14 +89,14 @@ function BooIndicator() {
const getTooltipText = () => {
const ghostedChannels = getGhostedChannels();
if (ghostedChannels.length === 0) {
return "No Ghosted Users";
return t("ghosted.ui.noGhostedUsers");
}
if (ghostedChannels.length <= 5) {
return ghostedChannels
.map(id => getChannelDisplayName(id))
.join(", ");
}
return `${ghostedChannels.length} Ghosted Users`;
return t("ghosted.ui.ghostedUsers", { count: ghostedChannels.length });
};
return (
@ -125,7 +126,7 @@ function makeContextItem(props) {
return <Menu.MenuItem
id="ec-ghosted-clear"
key="ec-ghosted-clear"
label="unghost"
label={t("ghosted.ui.unghost")}
action={() => {
clearChannelFromGhost(props.channel.id);
}}
@ -134,7 +135,7 @@ function makeContextItem(props) {
export default definePlugin({
name: "Ghosted",
description: "A cute ghost will appear if you don't answer their DMs",
description: t("ghosted.description"),
authors: [EquicordDevs.vei, Devs.sadan, EquicordDevs.justjxke, EquicordDevs.iamme],
settings,
dependencies: ["AudioPlayerAPI", "ServerListAPI"],

View file

@ -16,6 +16,7 @@ import { Paragraph } from "@components/Paragraph";
import { copyToClipboard } from "@utils/clipboard";
import { Devs, EquicordDevs } from "@utils/constants";
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Alerts, Button, ContextMenuApi, FluxDispatcher, Menu, React, showToast, TextInput, Toasts, useCallback, useState } from "@webpack/common";
@ -48,12 +49,12 @@ const addCollectionContextMenuPatch: NavContextMenuPatchCallback = (children, pr
if (settings.store.showCopyImageLink) {
group.push(
<Menu.MenuItem
label="Copy Image Link"
label={t("gifCollections.ui.copyImageLink")}
key="copy-image-link"
id="copy-image-link"
action={() => {
copyToClipboard(gif.url);
showToast("Image link copied to clipboard", Toasts.Type.SUCCESS);
showToast(t("gifCollections.ui.imageLinkCopied"), Toasts.Type.SUCCESS);
}}
/>
);
@ -61,7 +62,7 @@ const addCollectionContextMenuPatch: NavContextMenuPatchCallback = (children, pr
group.push(
<Menu.MenuItem
label="Add To Collection"
label={t("gifCollections.ui.addToCollection")}
key="add-to-collection"
id="add-to-collection"
>
@ -79,7 +80,7 @@ const addCollectionContextMenuPatch: NavContextMenuPatchCallback = (children, pr
<Menu.MenuItem
key="create-collection"
id="create-collection"
label="Create Collection"
label={t("gifCollections.ui.createCollection")}
action={() => {
openModal(modalProps => (
<CreateCollectionModal onClose={modalProps.onClose} gif={gif} modalProps={modalProps} />
@ -93,7 +94,7 @@ const addCollectionContextMenuPatch: NavContextMenuPatchCallback = (children, pr
export const settings = definePluginSettings({
itemPrefix: {
description: "The prefix for gif items",
description: t("gifCollections.settings.itemPrefix"),
type: OptionType.STRING,
default: "gc-item:",
onChange: value => {
@ -106,7 +107,7 @@ export const settings = definePluginSettings({
restartNeeded: true
},
collectionPrefix: {
description: "The prefix for collections",
description: t("gifCollections.settings.collectionPrefix"),
type: OptionType.STRING,
default: "gc:",
onChange: value => {
@ -119,28 +120,28 @@ export const settings = definePluginSettings({
restartNeeded: true
},
onlyShowCollections: {
description: "Only show collections",
description: t("gifCollections.settings.onlyShowCollections"),
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true
},
stopWarnings: {
description: "Stop deletion warnings",
description: t("gifCollections.settings.stopWarnings"),
type: OptionType.BOOLEAN,
default: false,
},
showCopyImageLink: {
description: "Show 'Copy Image Link' option in context menus",
description: t("gifCollections.settings.showCopyImageLink"),
type: OptionType.BOOLEAN,
default: false,
},
preventDuplicates: {
description: "Prevent adding the same GIF to a collection multiple times",
description: t("gifCollections.settings.preventDuplicates"),
type: OptionType.BOOLEAN,
default: false,
},
defaultEmptyCollectionImage: {
description: "The image / gif that will be shown when a collection has no images / gifs",
description: t("gifCollections.settings.defaultEmptyCollectionImage"),
type: OptionType.STRING,
default: "https://c.tenor.com/YEG33HsLEaIAAAAC/parksandrec-oops.gif"
},
@ -175,14 +176,14 @@ export const settings = definePluginSettings({
return (
<div className="collections-sort-container">
<Heading className="collections-sort-title">Sort Collections</Heading>
<Heading className="collections-sort-title">{t("gifCollections.ui.sortCollections")}</Heading>
<Divider className="collections-sort-divider" />
<Paragraph className="collections-sort-description">
Choose a sorting criteria for your collections
{t("gifCollections.ui.sortCollectionsDescription")}
</Paragraph>
<Divider className="collections-sort-divider" />
<div className="collections-sort-section">
<Paragraph className="collections-sort-section-title">Sort By</Paragraph>
<Paragraph className="collections-sort-section-title">{t("gifCollections.ui.sortBy")}</Paragraph>
<div className="collections-sort-option">
<label className="collections-sort-label">
<input
@ -193,7 +194,7 @@ export const settings = definePluginSettings({
onChange={() => handleSortTypeChange(SortingOptions.NAME)}
className="collections-sort-input"
/>
Name
{t("gifCollections.ui.name")}
</label>
</div>
<div className="collections-sort-option">
@ -206,7 +207,7 @@ export const settings = definePluginSettings({
onChange={() => handleSortTypeChange(SortingOptions.CREATION_DATE)}
className="collections-sort-input"
/>
Creation Date
{t("gifCollections.ui.creationDate")}
</label>
</div>
<div className="collections-sort-option">
@ -219,13 +220,13 @@ export const settings = definePluginSettings({
onChange={() => handleSortTypeChange(SortingOptions.MODIFIED_DATE)}
className="collections-sort-input"
/>
Modified Date
{t("gifCollections.ui.modifiedDate")}
</label>
</div>
</div>
<Divider className="collections-sort-divider" />
<div className="collections-sort-section">
<Paragraph className="collections-sort-section-title">Order</Paragraph>
<Paragraph className="collections-sort-section-title">{t("gifCollections.ui.order")}</Paragraph>
<div className="collections-sort-option">
<label className="collections-sort-label">
<input
@ -236,7 +237,7 @@ export const settings = definePluginSettings({
onChange={() => handleSortOrderChange("asc")}
className="collections-sort-input"
/>
Ascending
{t("gifCollections.ui.ascending")}
</label>
</div>
<div className="collections-sort-option">
@ -249,7 +250,7 @@ export const settings = definePluginSettings({
onChange={() => handleSortOrderChange("desc")}
className="collections-sort-input"
/>
Descending
{t("gifCollections.ui.descending")}
</label>
</div>
</div>
@ -259,52 +260,52 @@ export const settings = definePluginSettings({
},
importGifs: {
type: OptionType.COMPONENT,
description: "Import Collections",
description: t("gifCollections.ui.importCollections"),
component: () =>
<Button onClick={async () =>
(await getCollections()).length ? Alerts.show({
title: "Are you sure?",
body: "Importing collections will overwrite your current collections.",
confirmText: "Import",
title: t("gifCollections.ui.areYouSure"),
body: t("gifCollections.ui.importOverwrite"),
confirmText: t("gifCollections.ui.import"),
confirmColor: Button.Colors.RED,
cancelText: "Nevermind",
cancelText: t("gifCollections.ui.nevermind"),
onConfirm: async () => uploadGifCollections()
}) : uploadGifCollections()}>
Import Collections
{t("gifCollections.ui.importCollections")}
</Button>,
},
exportGifs: {
type: OptionType.COMPONENT,
description: "Export Collections",
description: t("gifCollections.ui.exportCollections"),
component: () =>
<Button onClick={downloadCollections}>
Export Collections
{t("gifCollections.ui.exportCollections")}
</Button>
},
resetCollections: {
type: OptionType.COMPONENT,
description: "Reset Collections",
description: t("gifCollections.ui.resetCollections"),
component: () =>
<Button onClick={() =>
Alerts.show({
title: "Are you sure?",
body: "Resetting collections will remove all your collections.",
confirmText: "Reset",
title: t("gifCollections.ui.areYouSure"),
body: t("gifCollections.ui.resetRemove"),
confirmText: t("gifCollections.ui.reset"),
confirmColor: Button.Colors.RED,
cancelText: "Nevermind",
cancelText: t("gifCollections.ui.nevermind"),
onConfirm: async () => {
await DataStore.set(DATA_COLLECTION_NAME, []);
refreshCacheCollection();
}
})}>
Reset Collections
{t("gifCollections.ui.resetCollections")}
</Button>
}
});
export default definePlugin({
name: "GifCollections",
description: "Allows you to create collections of gifs",
description: t("gifCollections.description"),
authors: [Devs.Aria, EquicordDevs.creations],
patches: [
{
@ -448,7 +449,7 @@ const RemoveItemContextMenu = ({ type, nameOrId, instance }) => (
<Menu.MenuItem
key="collection-information"
id="collection-information"
label="Collection Information"
label={t("gifCollections.ui.collectionInformation")}
action={() => {
const collection = cache_collections.find(c => c.name === nameOrId);
if (!collection) return;
@ -460,30 +461,30 @@ const RemoveItemContextMenu = ({ type, nameOrId, instance }) => (
className="custom-modal"
>
<ModalHeader separator={false} className="custom-modal-header">
<Paragraph className="custom-modal-title">Collection Information</Paragraph>
<Paragraph className="custom-modal-title">{t("gifCollections.ui.collectionInformation")}</Paragraph>
</ModalHeader>
<ModalContent className="custom-modal-content">
<section>
<Flex className="collection-info">
<Heading className="collection-info-title">Name</Heading>
<Heading className="collection-info-title">{t("gifCollections.ui.name")}</Heading>
<Paragraph className="collection-info-text">{collection.name.replace(/.+?:/, "")}</Paragraph>
</Flex>
<Flex className="collection-info">
<Heading className="collection-info-title">Gifs</Heading>
<Heading className="collection-info-title">{t("gifCollections.ui.gifs")}</Heading>
<Paragraph className="collection-info-text">{collection.gifs.length}</Paragraph>
</Flex>
<Flex className="collection-info">
<Heading className="collection-info-title">Created At</Heading>
<Paragraph className="collection-info-text">{collection.createdAt ? new Date(collection.createdAt).toLocaleString() : "Unknown"}</Paragraph>
<Heading className="collection-info-title">{t("gifCollections.ui.createdAt")}</Heading>
<Paragraph className="collection-info-text">{collection.createdAt ? new Date(collection.createdAt).toLocaleString() : t("gifCollections.ui.unknown")}</Paragraph>
</Flex>
<Flex className="collection-info">
<Heading className="collection-info-title">Last Updated</Heading>
<Paragraph className="collection-info-text">{collection.lastUpdated ? new Date(collection.lastUpdated).toLocaleString() : "Unknown"}</Paragraph>
<Heading className="collection-info-title">{t("gifCollections.ui.lastUpdated")}</Heading>
<Paragraph className="collection-info-text">{collection.lastUpdated ? new Date(collection.lastUpdated).toLocaleString() : t("gifCollections.ui.unknown")}</Paragraph>
</Flex>
</section>
</ModalContent>
<ModalFooter className="custom-modal-footer">
<Button onClick={modalProps.onClose} className="custom-modal-button">Close</Button>
<Button onClick={modalProps.onClose} className="custom-modal-button">{t("gifCollections.ui.close")}</Button>
</ModalFooter>
</ModalRoot>
));
@ -493,7 +494,7 @@ const RemoveItemContextMenu = ({ type, nameOrId, instance }) => (
<Menu.MenuItem
key="rename-collection"
id="rename-collection"
label="Rename"
label={t("gifCollections.ui.rename")}
action={() => openModal(modalProps => (
<RenameCollectionModal
onClose={modalProps.onClose}

View file

@ -6,6 +6,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { UserSettingsActionCreators } from "@webpack/common";
@ -30,20 +31,20 @@ function getMessage(opts, other) {
const settings = definePluginSettings({
pingOwnerChance: {
type: OptionType.BOOLEAN,
description: "If there should be a 1 in 10 change to ping the owner of the guild (oh no)",
description: t("gifRoulette.settings.pingOwnerChance"),
default: true
}
});
export default definePlugin({
name: "GifRoulette",
description: "Adds a command to send a random gif from your favourites, and a one in ten chance to ping the owner of the server",
description: t("gifRoulette.description"),
authors: [Devs.Samwich],
settings,
commands: [
{
name: "gifroulette",
description: "Tempt fate and send a gif",
description: t("gifRoulette.commands.gifroulette"),
execute: (opts, other) => ({
content: getMessage(opts, other)
}),

View file

@ -11,6 +11,7 @@ import { BaseText } from "@components/BaseText";
import ErrorBoundary from "@components/ErrorBoundary";
import { EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { User } from "@vencord/discord-types";
import { findByCodeLazy } from "@webpack";
@ -23,22 +24,22 @@ export const cl = classNameFactory("vc-github-repos-");
export const settings = definePluginSettings({
showStars: {
type: OptionType.BOOLEAN,
description: "Show repository stars",
description: t("githubRepos.settings.showStars"),
default: true
},
showLanguage: {
type: OptionType.BOOLEAN,
description: "Show repository language",
description: t("githubRepos.settings.showLanguage"),
default: true
},
showInMiniProfile: {
type: OptionType.BOOLEAN,
description: "Show full ui in the mini profile instead of just a button",
description: t("githubRepos.settings.showInMiniProfile"),
default: true
},
showRepositoryTab: {
type: OptionType.BOOLEAN,
description: "Show repositories tab in profile modal (hides button in connections when enabled)",
description: t("githubRepos.settings.showRepositoryTab"),
default: true
},
});
@ -58,7 +59,7 @@ const ProfilePopoutComponent = ErrorBoundary.wrap(
{
noop: true,
fallback: () => <BaseText size="xs" weight="semibold" className="vc-github-repos-error" style={{ color: "var(--text-feedback-critical)" }}>
Error, Failed to render GithubRepos
{t("githubRepos.ui.error")}
</BaseText>
}
);
@ -79,7 +80,7 @@ const ProfileRepositoriesTab = ErrorBoundary.wrap(
export default definePlugin({
name: "GitHubRepos",
description: "Displays a user's public GitHub repositories in their profile",
description: t("githubRepos.description"),
authors: [EquicordDevs.talhakf, EquicordDevs.Panniku, EquicordDevs.benjii],
settings,

View file

@ -12,6 +12,7 @@ import { Button } from "@components/Button";
import { BadgeContextMenu } from "@plugins/_api/badges";
import { Devs, EquicordDevs } from "@utils/constants";
import { openInviteModal } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { ContextMenuApi, React, Toasts, UserStore } from "@webpack/common";
@ -24,7 +25,7 @@ let intervalId: any;
migratePluginSetting("GlobalBadges", "showRaincord", "showRa1ncord");
export default definePlugin({
name: "GlobalBadges",
description: "Adds global badges from other client mods",
description: t("globalBadges.description"),
authors: [Devs.HypedDomi, EquicordDevs.Wolfie, Devs.thororen],
settings,
settingsAboutComponent: () => (
@ -34,7 +35,7 @@ export default definePlugin({
className={cl("settings-button")}
onClick={() => openInviteModal(INVITE_LINK)}
>
Join GlobalBadges Server
{t("globalBadges.ui.joinServer")}
</Button>
</>
),
@ -51,7 +52,7 @@ export default definePlugin({
await loadBadges();
Toasts.show({
id: Toasts.genId(),
message: "Successfully refetched global badges!",
message: t("globalBadges.ui.refetchSuccess"),
type: Toasts.Type.SUCCESS
});
}

View file

@ -7,6 +7,7 @@
import { ApplicationCommandOptionType, findOption } from "@api/Commands";
import { definePluginSettings } from "@api/Settings";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
function getMessage(opts) {
@ -48,17 +49,17 @@ const searchEngines = {
const settings = definePluginSettings({
hyperlink: {
type: OptionType.BOOLEAN,
description: "If the sent link should hyperlink with the query as the label",
description: t("googleThat.settings.hyperlink"),
default: false
},
embed: {
type: OptionType.BOOLEAN,
description: "If the sent link should render an embed",
description: t("googleThat.settings.embed"),
default: true
},
defaultEngine: {
type: OptionType.SELECT,
description: "The search engine to use",
description: t("googleThat.settings.defaultEngine"),
options: Object.keys(searchEngines).map((key, index) => ({
label: key,
value: key,
@ -66,7 +67,7 @@ const settings = definePluginSettings({
}))
},
customEngineURL: {
description: "The URL of the Engine you wish to use",
description: t("googleThat.settings.customEngineURL"),
type: OptionType.STRING,
placeholder: ""
}
@ -74,18 +75,18 @@ const settings = definePluginSettings({
export default definePlugin({
name: "GoogleThat",
description: "Adds a command to send a internet search link",
description: t("googleThat.description"),
authors: [Devs.Samwich, EquicordDevs.KrystalSkull],
tags: ["search", "google", "query", "duckduckgo", "command"],
settings,
commands: [
{
name: "googlethat",
description: "send a search engine link",
description: t("googleThat.commands.googlethat"),
options: [
{
name: "input",
description: "The search query",
description: t("googleThat.commands.input"),
type: ApplicationCommandOptionType.STRING,
required: true,
}

View file

@ -6,6 +6,7 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import type { Guild } from "@vencord/discord-types";
import { EmojiStore, Menu, StickersStore } from "@webpack/common";
@ -14,14 +15,13 @@ import { zipSync } from "fflate";
const StickerExt = [, "png", "apng", "json", "gif"] as const;
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => {
// Assuming "privacy" is the correct ID for the group you want to modify.
const group = findGroupChildrenByChildId("privacy", children);
if (group) {
group.push(
<>
<Menu.MenuItem id="emoji.download" label="Download Emojis" action={() => zipGuildAssets(guild, "emojis")}></Menu.MenuItem>
<Menu.MenuItem id="sticker.download" label="Download Stickers" action={() => zipGuildAssets(guild, "stickers")}></Menu.MenuItem>
<Menu.MenuItem id="emoji.download" label={t("guildPickerDumper.ui.downloadEmojis")} action={() => zipGuildAssets(guild, "emojis")}></Menu.MenuItem>
<Menu.MenuItem id="sticker.download" label={t("guildPickerDumper.ui.downloadStickers")} action={() => zipGuildAssets(guild, "stickers")}></Menu.MenuItem>
</>
);
}
@ -34,7 +34,7 @@ async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") {
: StickersStore.getStickersByGuildId(guild.id);
if (!items) {
return console.log("Server not found!");
return console.log(t("guildPickerDumper.ui.serverNotFound"));
}
const getProxyEndpoint = () => {
@ -94,7 +94,7 @@ async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") {
export default definePlugin({
name: "GuildPickerDumper",
description: "Context menu to dump and download a server's emojis and stickers.",
description: t("guildPickerDumper.description"),
authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Synth, Devs.thororen],
contextMenus: {
"guild-context": Patch,

View file

@ -19,6 +19,7 @@
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import clanBadges from "./styles.css?managed";
@ -26,7 +27,7 @@ import clanBadges from "./styles.css?managed";
const settings = definePluginSettings({
hideTags: {
type: OptionType.BOOLEAN,
description: "Hide tags",
description: t("guildTagSettings.settings.hideTags"),
default: false,
onChange: value => {
if (value) enableStyle(clanBadges);
@ -35,7 +36,7 @@ const settings = definePluginSettings({
},
disableAdoptTagPrompt: {
type: OptionType.BOOLEAN,
description: "Disable the prompt to adopt tags",
description: t("guildTagSettings.settings.disableAdoptTagPrompt"),
default: true,
restartNeeded: true
}
@ -43,7 +44,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "GuildTagSettings",
description: "Adds some settings for guild tags, such as hiding them or disabling the prompt to adopt them.",
description: t("guildTagSettings.description"),
authors: [Devs.thororen],
settings,
patches: [

View file

@ -7,6 +7,7 @@
import { ChatBarButton } from "@api/ChatButtons";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType, StartAt } from "@utils/types";
import { React, useEffect, useState } from "@webpack/common";
import type { MouseEventHandler, ReactNode } from "react";
@ -16,12 +17,12 @@ let hidechatbuttonsopen: boolean | undefined;
const settings = definePluginSettings({
Color: {
type: OptionType.BOOLEAN,
description: "Color it red on open", // something extra
description: t("hideChatButtons.settings.color"),
default: false,
},
Open: {
type: OptionType.BOOLEAN,
description: "opened by default",
description: t("hideChatButtons.settings.open"),
default: false,
onChange: (store: { open: boolean; }) => {
hidechatbuttonsopen = store.open;
@ -32,7 +33,7 @@ const settings = definePluginSettings({
function HideToggleButton(props: { open: boolean | undefined, onClick: MouseEventHandler<HTMLButtonElement>; }) {
return (<ChatBarButton
onClick={props.onClick}
tooltip={props.open ? "Close" : "Open"}
tooltip={props.open ? t("hideChatButtons.ui.close") : t("hideChatButtons.ui.open")}
>
<svg
fill={settings.store.Color && props.open ? "#c32a32" : "currentColor"}
@ -74,7 +75,7 @@ function ButtonsInnerComponent({ buttons }: { buttons: ReactNode[]; }) {
export default definePlugin({
name: "HideChatButtons",
description: "able to hide the chat buttons",
description: t("hideChatButtons.description"),
settings: settings,
authors: [EquicordDevs.iamme],
patches: [

View file

@ -16,6 +16,7 @@ import {
ServerListRenderPosition,
} from "@api/ServerList";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Guild } from "@vencord/discord-types";
import { findStoreLazy } from "@webpack";
@ -53,7 +54,7 @@ const Patch: NavContextMenuPatchCallback = (
group.push(
<Menu.MenuItem
id="vc-hide-server"
label={isHidden ? "Unhide Server" : "Hide Server"}
label={isHidden ? t("hideServers.ui.unhideServer") : t("hideServers.ui.hideServer")}
action={() => {
if (isHidden) {
HiddenServersStore.removeHiddenGuild(guild.id);
@ -75,7 +76,7 @@ export function removeIndicator() {
export default definePlugin({
name: "HideServers",
description: "Allows you to hide servers from the guild list and quick switcher by right clicking them",
description: t("hideServers.description"),
authors: [EquicordDevs.bep],
tags: ["guild", "server", "hide", "folder"],
@ -96,7 +97,7 @@ export default definePlugin({
menuItems.push(
<Menu.MenuItem
id="vc-hide-folder"
label={isHidden ? "Unhide Folder" : "Hide Folder"}
label={isHidden ? t("hideServers.ui.unhideFolder") : t("hideServers.ui.hideFolder")}
action={() => {
if (isHidden) {
HiddenServersStore.removeHiddenFolder(folderId, guildIds);

View file

@ -24,6 +24,7 @@ import { DataStore } from "@api/index";
import { EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { openModal } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { IconComponent } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { findByCodeLazy, findComponentByCodeLazy, findCssClassesLazy, findExportedComponentLazy } from "@webpack";
@ -45,7 +46,7 @@ export const ChannelMessage = findComponentByCodeLazy("Message must not be a thr
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => {
children.push(
<Menu.MenuItem label="Note Message" id="note-message">
<Menu.MenuItem label={t("holyNotes.ui.noteMessage")} id="note-message">
{Object.keys(noteHandler.getAllNotes()).map(notebook => (
<Menu.MenuItem
key={notebook}
@ -61,7 +62,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag
function ToolBarHeader() {
return (
<HeaderBarButton
tooltip="Holy Notes"
tooltip={t("holyNotes.ui.holyNotes")}
position="bottom"
icon={BookmarkIcon}
onClick={() => openModal(props => <NoteModal {...props} />)}
@ -71,11 +72,11 @@ function ToolBarHeader() {
export default definePlugin({
name: "HolyNotes",
description: "Save messages as notes to revisit later",
description: t("holyNotes.description"),
authors: [EquicordDevs.Wolfie],
toolboxActions: {
"Open Notes"() {
[t("holyNotes.ui.openNotes")]() {
openModal(props => <NoteModal {...props} />);
}
},
@ -93,7 +94,7 @@ export default definePlugin({
icon: BookmarkIcon,
render(message) {
return {
label: "Save Note",
label: t("holyNotes.ui.saveNote"),
icon: BookmarkIcon,
message,
channel: ChannelStore.getChannel(message.channel_id),

View file

@ -5,6 +5,7 @@
*/
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { TypingStore, UserStore, useStateFromStores } from "@webpack/common";
@ -15,7 +16,7 @@ const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { ge
export default definePlugin({
name: "HomeTyping",
description: "Changes the home button to a typing indicator if someone in your dms is typing",
description: t("homeTyping.description"),
authors: [Devs.Samwich],
TypingIcon() {
return <ThreeDots dotRadius={3} themed={true} />;

View file

@ -6,6 +6,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { RelationshipStore, SelectedChannelStore } from "@webpack/common";
@ -20,18 +21,18 @@ interface IMessageCreate {
const settings = definePluginSettings({
regex: {
type: OptionType.STRING,
description: "Regex to trigger on",
description: t("hopOn.settings.regex"),
default: "hop on (?:fortnite|fn)"
},
url: {
type: OptionType.STRING,
description: "URL to open",
description: t("hopOn.settings.url"),
default: "com.epicgames.launcher://apps/fn%3A4fe75bbc5a674f4f9b356b5c90567da5%3AFortnite?action=launch&silent=true"
}
});
export default definePlugin({
name: "HopOn",
description: "Hop on Fortnite or Hop on bloons :3",
description: t("hopOn.description"),
authors: [Devs.ImLvna],
settings,
flux: {

View file

@ -21,6 +21,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { classes } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, EmojiStore, RestAPI } from "@webpack/common";
import type { SVGProps } from "react";
@ -69,17 +70,17 @@ export function Husk(props: IconProps) {
const settings = definePluginSettings({
findInServer: {
description: "Attempt to find emoji of same name in server before using ID in settings (useful if no nitro)",
description: t("husk.settings.findInServer"),
type: OptionType.BOOLEAN,
default: true
},
emojiName: {
description: "Emoji name (default (from Vencord Server): husk)",
description: t("husk.settings.emojiName"),
type: OptionType.STRING,
default: "husk"
},
emojiID: {
description: "Emoji ID (default (from Vencord Server): 1026532993923293184)",
description: t("husk.settings.emojiID"),
type: OptionType.BIGINT,
default: 1026532993923293184n
}
@ -98,14 +99,14 @@ function getEmojiIdThatShouldBeUsed(guildId: string) {
export default definePlugin({
name: "Husk",
description: "Adds Husk button (check settings to change used emoji)",
description: t("husk.description"),
authors: [Devs.nin0dev],
settings,
messagePopoverButton: {
icon: Husk,
render(msg) {
return {
label: "Husk",
label: t("husk.ui.husk"),
icon: Husk,
message: msg,
channel: ChannelStore.getChannel(msg.channel_id),

View file

@ -11,6 +11,7 @@ import { EyeIcon } from "@components/Icons";
import SettingsPlugin from "@plugins/_core/settings";
import { EquicordDevs } from "@utils/constants";
import { removeFromArray } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Data } from "./components/data";
@ -18,7 +19,7 @@ import DataUI from "./components/ui";
export default definePlugin({
name: "IRememberYou",
description: "Locally saves everyone you've been communicating with (including servers), in case of lose",
description: t("iRememberYou.description"),
authors: [EquicordDevs.zoodogood, EquicordDevs.keircn],
dependencies: ["MessageEventsAPI"],
@ -27,7 +28,7 @@ export default definePlugin({
async start() {
SettingsPlugin.customEntries.push({
key: "equicord_i_remember_you",
title: "I Remember You",
title: t("iRememberYou.ui.iRememberYou"),
Component: () => <DataUI usersCollection={data.usersCollection} />,
Icon: EyeIcon
});

View file

@ -10,6 +10,7 @@ import { MagnifyingGlassIcon } from "@components/Icons";
import SettingsPlugin from "@plugins/_core/settings";
import { EquicordDevs } from "@utils/constants";
import { removeFromArray } from "@utils/misc";
import { t } from "@utils/translation";
import definePlugin, { StartAt } from "@utils/types";
import { SettingsRouter } from "@webpack/common";
@ -18,12 +19,12 @@ import { SettingsAbout } from "./components/Modals";
export default definePlugin({
name: "IconViewer",
description: "Adds a new tab to settings to preview all icons.",
description: t("iconViewer.description"),
authors: [EquicordDevs.iamme],
dependencies: ["Settings"],
startAt: StartAt.WebpackReady,
toolboxActions: {
"Open Icons Tab"() {
[t("iconViewer.ui.openIconsTab")]() {
SettingsRouter.openUserSettings("equicord_icon_viewer_panel");
},
},
@ -31,7 +32,7 @@ export default definePlugin({
start() {
SettingsPlugin.customEntries.push({
key: "equicord_icon_viewer",
title: "Icon Finder",
title: t("iconViewer.ui.iconFinder"),
Component: IconsTab,
Icon: MagnifyingGlassIcon
});

View file

@ -7,6 +7,7 @@
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Menu, VoiceStateStore } from "@webpack/common";
@ -16,12 +17,12 @@ let intervalId: ReturnType<typeof setInterval> | null = null;
const settings = definePluginSettings({
isEnabled: {
description: "Enable automatic restart after idle",
description: t("idleAutoRestart.settings.isEnabled"),
type: OptionType.BOOLEAN,
default: true,
},
idleMinutes: {
description: "Minutes of inactivity before restarting (when not in VC)",
description: t("idleAutoRestart.settings.idleMinutes"),
type: OptionType.SLIDER,
markers: [5, 10, 15, 30, 60, 120],
default: 30,
@ -35,7 +36,7 @@ function onActivity() {
export default definePlugin({
name: "IdleAutoRestart",
description: "Automatically restarts the client after being idle for a configurable amount of time, but avoids restarting while you are in VC.",
description: t("idleAutoRestart.description"),
authors: [EquicordDevs.SteelTech],
settings,
@ -43,7 +44,7 @@ export default definePlugin({
return (
<Menu.MenuItem
id="auto-idle-restart-toggle-toolbox"
label={settings.store.isEnabled ? "Disable Auto Idle Restart" : "Enable Auto Idle Restart"}
label={settings.store.isEnabled ? t("idleAutoRestart.ui.disableAutoIdle") : t("idleAutoRestart.ui.enableAutoIdle")}
action={() => {
settings.store.isEnabled = !settings.store.isEnabled;
}}

View file

@ -12,6 +12,7 @@ import { Button } from "@components/Button";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs, EquicordDevs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Channel } from "@vencord/discord-types";
import { findComponentByCodeLazy } from "@webpack";
@ -34,7 +35,7 @@ const ContextMenuPatch: NavContextMenuPatchCallback = (children, { channel }: {
<Menu.MenuSeparator />
<Menu.MenuCheckboxItem
id="vc-ignore-calls-temp"
label="Temporarily Ignore Calls"
label={t("ignoreCalls.ui.temporarilyIgnore")}
checked={tempChecked}
action={() => {
if (tempChecked)
@ -47,7 +48,7 @@ const ContextMenuPatch: NavContextMenuPatchCallback = (children, { channel }: {
/>
<Menu.MenuCheckboxItem
id="vc-ignore-calls-perm"
label="Permanently Ignore Calls"
label={t("ignoreCalls.ui.permanentlyIgnore")}
checked={permChecked}
action={() => {
let updated = permanentlyIgnoredUsers.slice();
@ -68,7 +69,7 @@ const ContextMenuPatch: NavContextMenuPatchCallback = (children, { channel }: {
const settings = definePluginSettings({
permanentlyIgnoredUsers: {
type: OptionType.STRING,
description: "User IDs (comma + space) who should be permanetly ignored",
description: t("ignoreCalls.settings.permanentlyIgnoredUsers"),
restartNeeded: true,
default: "",
},
@ -82,7 +83,7 @@ const args = {
export default definePlugin({
name: "IgnoreCalls",
description: "Allows you to ignore calls from specific users or dm groups.",
description: t("ignoreCalls.description"),
authors: [EquicordDevs.TheArmagan, Devs.thororen],
settings,
patches: [
@ -121,7 +122,7 @@ export default definePlugin({
return (
<ErrorBoundary>
<Tooltip text="Ignore">
<Tooltip text={t("ignoreCalls.ui.ignore")}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
className={cl("button")}

View file

@ -18,6 +18,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, sendBotMessage } from "@api/Commands";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { CommandArgument, CommandContext } from "@vencord/discord-types";
import { DraftType, UploadAttachmentStore, UploadHandler, UploadManager } from "@webpack/common";
@ -50,10 +51,10 @@ async function resolveImage(options: CommandArgument[], ctx: CommandContext): Pr
switch (opt.name) {
case "image":
const upload = UploadAttachmentStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand);
if (upload) {
if (upload) {
if (!upload.isImage) {
UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand);
throw "Upload is not an image";
throw t("imgToGif.errors.notAnImage");
}
image = upload.item.file;
}
@ -73,34 +74,34 @@ async function resolveImage(options: CommandArgument[], ctx: CommandContext): Pr
export default definePlugin({
name: "ImgToGif",
description: "Adds a /imgtogif slash command to create a gif from any image",
description: t("imgToGif.description"),
authors: [EquicordDevs.zyqunix],
commands: [
{
inputType: ApplicationCommandInputType.BUILT_IN,
name: "imgtogif",
description: "Allows you to turn an image to a gif",
description: t("imgToGif.commands.imgtogif"),
options: [
{
name: "image",
description: "Image attachment to use",
description: t("imgToGif.commands.image"),
type: ApplicationCommandOptionType.ATTACHMENT
},
{
name: "width",
description: "Width of the gif",
description: t("imgToGif.commands.width"),
type: ApplicationCommandOptionType.INTEGER
},
{
name: "height",
description: "Height of the gif",
description: t("imgToGif.commands.height"),
type: ApplicationCommandOptionType.INTEGER
}
],
execute: async (opts, cmdCtx) => {
try {
const { image, width, height } = await resolveImage(opts, cmdCtx);
if (!image) throw "No Image specified!";
if (!image) throw t("imgToGif.errors.noImage");
const avatar = await loadImage(image);

View file

@ -12,6 +12,7 @@ import { InfoIcon } from "@components/Icons";
import { Paragraph } from "@components/Paragraph";
import { Devs } from "@utils/constants";
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { GuildMember } from "@vencord/discord-types";
import { GuildMemberStore, GuildRoleStore, Menu, Parser } from "@webpack/common";
@ -33,21 +34,20 @@ function getMembersInRole(roleId: string, guildId: string) {
export default definePlugin({
name: "InRole",
description: "Know who is in a role with the role context menu or /inrole command (read plugin info!)",
description: t("inRole.description"),
authors: [Devs.nin0dev],
dependencies: ["UserSettingsAPI"],
start() {
// DeveloperMode needs to be enabled for the context menu to be shown
DeveloperMode.updateSetting(true);
},
settingsAboutComponent: () => {
return (
<>
<Paragraph style={{ fontSize: "1.2rem", marginTop: "15px", fontWeight: "bold" }}>{Parser.parse(":warning:")} Limitations</Paragraph>
<Paragraph style={{ marginTop: "10px", fontWeight: "500" }} >If you don't have mod permissions on the server, and that server is large (over 100 members), the plugin may be limited in the following ways:</Paragraph>
<Paragraph>• Offline members won't be listed</Paragraph>
<Paragraph>• Up to 100 members will be listed by default. To get more, scroll down in the member list to load more members.</Paragraph>
<Paragraph>• However, friends will always be shown regardless of their status.</Paragraph>
<Paragraph style={{ fontSize: "1.2rem", marginTop: "15px", fontWeight: "bold" }}>{Parser.parse(t("inRole.limitations.title"))}</Paragraph>
<Paragraph style={{ marginTop: "10px", fontWeight: "500" }} >{t("inRole.limitations.description")}</Paragraph>
<Paragraph>• {t("inRole.limitations.offlineNotListed")}</Paragraph>
<Paragraph>• {t("inRole.limitations.upTo100")}</Paragraph>
<Paragraph>• {t("inRole.limitations.friendsAlwaysShown")}</Paragraph>
</>
);
},
@ -55,20 +55,19 @@ export default definePlugin({
commands: [
{
name: "inrole",
description: "Know who is in a role",
description: t("inRole.commands.inrole"),
inputType: ApplicationCommandInputType.BUILT_IN,
options: [
{
name: "role",
description: "The role",
description: t("inRole.commands.role"),
type: ApplicationCommandOptionType.ROLE,
required: true
},
],
execute: (args, ctx) => {
// Guild check
if (!ctx.guild) {
return sendBotMessage(ctx.channel.id, { content: "Make sure that you are in a server." });
return sendBotMessage(ctx.channel.id, { content: t("inRole.errors.makeSureServer") });
}
const role = args[0].value;
showInRoleModal(getMembersInRole(role, ctx.guild.id), role, ctx.channel.id);
@ -89,7 +88,7 @@ export default definePlugin({
children.push(
<Menu.MenuItem
id="vc-view-inrole"
label="View Members in Role"
label={t("inRole.ui.viewMembers")}
action={() => {
showInRoleModal(getMembersInRole(role.id, guild.id), role.id, channel.id);
}}
@ -115,7 +114,7 @@ export default definePlugin({
children.push(
<Menu.MenuItem
id="vc-view-inrole"
label="View Members in Role"
label={t("inRole.ui.viewMembers")}
action={() => {
showInRoleModal(getMembersInRole(role.id, guild.id), role.id, channel.id);
}}

View file

@ -8,6 +8,7 @@ import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { React } from "@webpack/common";
@ -20,13 +21,13 @@ const settings = definePluginSettings({
showIcon: {
type: OptionType.BOOLEAN,
default: true,
description: "Show a button to toggle the Ingtoninator plugin",
description: t("ingtoninator.settings.showIcon"),
restartNeeded: true
},
isEnabled: {
type: OptionType.BOOLEAN,
default: true,
description: "Enable or disable the Ingtoninator"
description: t("ingtoninator.settings.isEnabled")
}
});
@ -115,7 +116,7 @@ const IngtoninatorButton: ChatBarButtonFactory = ({ isMainChat }) => {
return (
<ChatBarButton
tooltip={isEnabled ? "Ingtoninator Enabled" : "Ingtoninator Disabled"}
tooltip={isEnabled ? t("ingtoninator.ui.enabled") : t("ingtoninator.ui.disabled")}
onClick={toggle}
>
{isEnabled ? (
@ -145,7 +146,7 @@ function disabledIcon() {
export default definePlugin({
name: "Ingtoninator",
description: "Suffixes 'ington' to a random word in your message",
description: t("ingtoninator.description"),
authors: [EquicordDevs.zyqunix, EquicordDevs.BioTomateDE],
settings,
chatBarButton: {

View file

@ -8,6 +8,7 @@ import { getUserSettingLazy } from "@api/UserSettings";
import { HeadingSecondary } from "@components/Heading";
import { Paragraph } from "@components/Paragraph";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { VoiceState } from "@vencord/discord-types";
import { findByCodeLazy, findStoreLazy } from "@webpack";
@ -56,7 +57,7 @@ async function autoStartStream(instant = true) {
export default definePlugin({
name: "InstantScreenshare",
description: "Instantly screenshare when joining a voice channel with support for desktop sources, windows, and video input devices (cameras, capture cards)",
description: t("instantScreenshare.description"),
authors: [Devs.HAHALOSAH, Devs.thororen, EquicordDevs.mart],
dependencies: ["EquicordToolbox"],
tags: ["ScreenshareKeybind"],
@ -65,21 +66,21 @@ export default definePlugin({
settingsAboutComponent: () => (
<>
<HeadingSecondary>For Linux</HeadingSecondary>
<HeadingSecondary>{t("instantScreenshare.info.linux")}</HeadingSecondary>
<Paragraph>
For Wayland it only pops up the screenshare select
{t("instantScreenshare.info.wayland")}
<br />
For X11 it may or may not work :shrug:
{t("instantScreenshare.info.x11")}
</Paragraph>
<br />
<HeadingSecondary>Video Devices</HeadingSecondary>
<HeadingSecondary>{t("instantScreenshare.info.videoDevices")}</HeadingSecondary>
<Paragraph>
Supports cameras and capture cards (like Elgato HD60X) when enabled in settings
{t("instantScreenshare.info.videoDevicesInfo")}
</Paragraph>
<br />
<HeadingSecondary>Regarding Sound & Preview Settings</HeadingSecondary>
<HeadingSecondary>{t("instantScreenshare.info.soundPreview")}</HeadingSecondary>
<Paragraph>
We use the settings set and used by discord to decide if stream preview and sound should be enabled or not
{t("instantScreenshare.info.soundPreviewInfo")}
</Paragraph>
</>
),
@ -126,9 +127,9 @@ export default definePlugin({
},
toolboxActions: {
"Instant Screenshare"() {
[t("instantScreenshare.ui.instantScreenshare")]() {
settings.store.toolboxManagement = !settings.store.toolboxManagement;
showToast(`Instant Screenshare ${settings.store.toolboxManagement ? "Enabled" : "Disabled"}`, Toasts.Type.SUCCESS);
showToast(settings.store.toolboxManagement ? t("instantScreenshare.ui.instantScreenshareEnabled") : t("instantScreenshare.ui.instantScreenshareDisabled"), Toasts.Type.SUCCESS);
}
}
});

View file

@ -22,6 +22,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getStegCloak } from "@utils/dependencies";
import { t } from "@utils/translation";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common";
@ -46,7 +47,7 @@ function PopOverIcon() {
function Indicator() {
return (
<Tooltip text="This message has a hidden message! (InvisibleChat)">
<Tooltip text={t("invisibleChat.ui.hiddenMessageIndicator")}>
{({ onMouseEnter, onMouseLeave }) => (
<img
aria-label="Hidden Message Indicator (InvisibleChat)"
@ -69,7 +70,7 @@ const ChatBarRender: ChatBarButtonFactory = ({ isMainChat }) => {
return (
<ChatBarButton
tooltip="Encrypt Message"
tooltip={t("invisibleChat.ui.encryptMessage")}
onClick={() => buildEncModal()}
buttonProps={{
"aria-haspopup": "dialog",
@ -99,7 +100,7 @@ const settings = definePluginSettings({
savedPasswords: {
type: OptionType.STRING,
default: "password, Password",
description: "Saved Passwords (Seperated with a , )"
description: t("invisibleChat.settings.savedPasswords")
}
});
@ -148,7 +149,7 @@ export async function buildEmbed(message: any, revealed: string): Promise<void>
export default definePlugin({
name: "InvisibleChat",
description: "Encrypt your Messages in a non-suspicious way!",
description: t("invisibleChat.description"),
authors: [Devs.SammCheese],
dependencies: ["MessageUpdaterAPI"],
reporterTestable: ReporterTestable.Patches,
@ -180,7 +181,7 @@ export default definePlugin({
render(message) {
return INV_REGEX.test(message?.content)
? {
label: "Decrypt Message",
label: t("invisibleChat.ui.decryptMessage"),
icon: PopOverIcon,
message: message,
channel: ChannelStore.getChannel(message.channel_id),

View file

@ -6,12 +6,13 @@
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
const settings = definePluginSettings({
inviteDuration: {
type: OptionType.SELECT,
description: "Invite Duration",
description: t("inviteDefaults.settings.inviteDuration"),
options: [
{ label: "30 minutes", value: 1800 },
{ label: "1 hour", value: 3600 },
@ -19,14 +20,14 @@ const settings = definePluginSettings({
{ label: "12 hours", value: 43200 },
{ label: "1 day", value: 86400 },
{ label: "7 days", value: 604800 },
{ label: "Forever", value: 0, default: true },
{ label: t("inviteDefaults.options.forever"), value: 0, default: true },
],
},
maxUses: {
type: OptionType.SELECT,
description: "Invite Use Count",
description: t("inviteDefaults.settings.maxUses"),
options: [
{ label: "Infinite", value: 0, default: true },
{ label: t("inviteDefaults.options.infinite"), value: 0, default: true },
{ label: "1", value: 1 },
{ label: "5", value: 5 },
{ label: "10", value: 10 },
@ -38,13 +39,13 @@ const settings = definePluginSettings({
temporaryMembership: {
type: OptionType.BOOLEAN,
default: false,
description: "Temporary Membership",
description: t("inviteDefaults.settings.temporaryMembership"),
},
});
export default definePlugin({
name: "InviteDefaults",
description: "Allows you to edit the default values when creating server invites.",
description: t("inviteDefaults.description"),
authors: [EquicordDevs.VillainsRule],
settings,
patches: [

View file

@ -10,6 +10,7 @@ import { HeadingSecondary } from "@components/Heading";
import { Paragraph } from "@components/Paragraph";
import { Devs, EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ApplicationAssetUtils, FluxDispatcher, showToast } from "@webpack/common";
@ -60,86 +61,86 @@ interface MediaData {
const settings = definePluginSettings({
serverUrl: {
description: "Jellyfin server URL (e.g., https://jellyfin.example.com)",
description: t("jellyfinRichPresence.settings.serverUrl"),
type: OptionType.STRING,
},
apiKey: {
description: "Jellyfin API key obtained from your Jellyfin administration dashboard",
description: t("jellyfinRichPresence.settings.apiKey"),
type: OptionType.STRING,
},
userId: {
description: "Jellyfin user ID obtained from your user profile URL",
description: t("jellyfinRichPresence.settings.userId"),
type: OptionType.STRING,
},
nameDisplay: {
description: "Choose how the application name should appear in Rich Presence",
description: t("jellyfinRichPresence.settings.nameDisplay"),
type: OptionType.SELECT,
options: [
{ label: "Series/Movie Name", value: "default", default: true },
{ label: "Series - Episode/Track/Movie Name", value: "full" },
{ label: "Custom", value: "custom" },
{ label: t("jellyfinRichPresence.nameFormatOptions.seriesMovieName"), value: "default", default: true },
{ label: t("jellyfinRichPresence.nameFormatOptions.seriesEpisodeName"), value: "full" },
{ label: t("jellyfinRichPresence.nameFormatOptions.custom"), value: "custom" },
],
},
customName: {
description: "Custom Rich Presence name (only used if 'Custom' is selected).\nOptions: {name}, {series}, {season}, {episode}, {artist}, {album}, {year}",
description: t("jellyfinRichPresence.settings.customName"),
type: OptionType.STRING,
},
coverType: {
description: "Choose which cover to display when watching a TV show",
description: t("jellyfinRichPresence.settings.coverType"),
type: OptionType.SELECT,
options: [
{ label: "Series Cover", value: "series", default: true },
{ label: "Episode Cover", value: "episode" },
{ label: t("jellyfinRichPresence.coverTypeOptions.seriesCover"), value: "series", default: true },
{ label: t("jellyfinRichPresence.coverTypeOptions.episodeCover"), value: "episode" },
],
},
episodeFormat: {
description: "Episode number format",
description: t("jellyfinRichPresence.settings.episodeFormat"),
type: OptionType.SELECT,
options: [
{ label: "S01E01", value: "long", default: true },
{ label: "1x01", value: "short" },
{ label: "Season 1 Episode 1", value: "fulltext" },
{ label: t("jellyfinRichPresence.episodeFormatOptions.s01e01"), value: "long", default: true },
{ label: t("jellyfinRichPresence.episodeFormatOptions.short"), value: "short" },
{ label: t("jellyfinRichPresence.episodeFormatOptions.fulltext"), value: "fulltext" },
],
},
showEpisodeName: {
description: "Show episode name after season/episode info",
description: t("jellyfinRichPresence.settings.showEpisodeName"),
type: OptionType.BOOLEAN,
default: false,
},
overrideRichPresenceType: {
description: "Override the rich presence type",
description: t("jellyfinRichPresence.settings.overrideRichPresenceType"),
type: OptionType.SELECT,
options: [
{
label: "Off",
label: t("jellyfinRichPresence.overrideTypeOptions.off"),
value: false,
default: true,
},
{
label: "Listening",
label: t("jellyfinRichPresence.overrideTypeOptions.listening"),
value: 2,
},
{
label: "Playing",
label: t("jellyfinRichPresence.overrideTypeOptions.playing"),
value: 0,
},
{
label: "Streaming",
label: t("jellyfinRichPresence.overrideTypeOptions.streaming"),
value: 1,
},
{
label: "Watching",
label: t("jellyfinRichPresence.overrideTypeOptions.watching"),
value: 3
},
],
},
showPausedState: {
description: "Show Rich Presence when media is paused",
description: t("jellyfinRichPresence.settings.showPausedState"),
type: OptionType.BOOLEAN,
default: true,
},
privacyMode: {
description: "Privacy Mode (Hide media details like Episode/Song Name)",
description: t("jellyfinRichPresence.settings.privacyMode"),
type: OptionType.BOOLEAN,
default: false,
},
@ -165,14 +166,14 @@ function setActivity(activity: Activity | null) {
export default definePlugin({
name: "JellyfinRichPresence",
description: "Rich presence for Jellyfin media server",
description: t("jellyfinRichPresence.description"),
authors: [EquicordDevs.vmohammad, Devs.SerStars, EquicordDevs.ZcraftElite],
settingsAboutComponent: () => (
<>
<HeadingSecondary>How to get an API key</HeadingSecondary>
<HeadingSecondary>{t("jellyfinRichPresence.howToGetApiKey.title")}</HeadingSecondary>
<Paragraph>
Auth token can be found by following these steps:
{t("jellyfinRichPresence.howToGetApiKey.description")}
<ol style={{ marginTop: 8, marginBottom: 8, paddingLeft: 20 }}>
<li>1. Log into your Jellyfin instance</li>
<li>2. Open your browser's Developer Tools (usually F12 or right-click then Inspect)</li>
@ -187,7 +188,7 @@ export default definePlugin({
</li>
</ol>
<br />
You'll also need your User ID, which can be found in the url of your user profile page.
{t("jellyfinRichPresence.howToGetApiKey.userIdInfo")}
</Paragraph>
</>
),
@ -207,7 +208,7 @@ export default definePlugin({
async fetchMediaData(): Promise<MediaData | null> {
if (!settings.store.serverUrl || !settings.store.apiKey || !settings.store.userId) {
logger.warn("Jellyfin server URL, API key, or user ID is not set in settings.");
showToast("JellyfinRPC is not configured.", "failure", {
showToast(t("jellyfinRichPresence.toasts.notConfigured"), "failure", {
duration: 15000,
});
return null;
@ -302,15 +303,15 @@ export default definePlugin({
case "full":
if (mediaData.type === "Episode" && mediaData.seriesName) {
appName = settings.store.privacyMode
? `${mediaData.seriesName} - [Episode Hidden]`
? `${mediaData.seriesName} - ${t("jellyfinRichPresence.privacyMode.episodeHidden")}`
: `${mediaData.seriesName} - ${mediaData.name}`;
} else if (mediaData.type === "Audio") {
appName = settings.store.privacyMode
? "[Track Hidden]"
? t("jellyfinRichPresence.privacyMode.trackHidden")
: `${mediaData.artist || "Unknown Artist"} - ${mediaData.name}`;
} else {
appName = settings.store.privacyMode
? "[Movie Hidden]"
? t("jellyfinRichPresence.privacyMode.movieHidden")
: mediaData.name || "Jellyfin";
}
break;
@ -318,10 +319,10 @@ export default definePlugin({
appName = templateReplace(settings.store.customName || "{name} on Jellyfin");
if (settings.store.privacyMode) {
appName = appName
.replace(mediaData.name || "", "[Title Hidden]")
.replace(mediaData.seriesName || "", "[Series Hidden]")
.replace(mediaData.artist || "", "[Artist Hidden]")
.replace(mediaData.album || "", "[Album Hidden]");
.replace(mediaData.name || "", t("jellyfinRichPresence.privacyMode.titleHidden"))
.replace(mediaData.seriesName || "", t("jellyfinRichPresence.privacyMode.seriesHidden"))
.replace(mediaData.artist || "", t("jellyfinRichPresence.privacyMode.artistHidden"))
.replace(mediaData.album || "", t("jellyfinRichPresence.privacyMode.albumHidden"));
}
break;
case "default":
@ -330,7 +331,7 @@ export default definePlugin({
appName = mediaData.seriesName;
} else {
appName = settings.store.privacyMode
? "[Media Hidden]"
? t("jellyfinRichPresence.privacyMode.mediaHidden")
: mediaData.name || "Jellyfin";
}
break;
@ -351,14 +352,14 @@ export default definePlugin({
const getDetails = () => {
if (mediaData.type === "Episode" && mediaData.seriesName) {
return settings.store.privacyMode ? "Watching a TV Show" : mediaData.seriesName;
return settings.store.privacyMode ? t("jellyfinRichPresence.privacyMode.watchingTvShow") : mediaData.seriesName;
}
return settings.store.privacyMode ? "Watching Something" : mediaData.name;
return settings.store.privacyMode ? t("jellyfinRichPresence.privacyMode.watchingSomething") : mediaData.name;
};
const getState = () => {
if (mediaData.isPaused) {
return "Paused";
return t("jellyfinRichPresence.state.paused");
}
if (mediaData.type === "Episode" && mediaData.seriesName) {
let episodeFormat = "";
@ -375,13 +376,13 @@ export default definePlugin({
episodeFormat = `${season}x${episode.toString().padStart(2, "0")}`;
break;
case "fulltext":
episodeFormat = `Season ${season} Episode ${episode}`;
episodeFormat = t("jellyfinRichPresence.state.season", { season }) + " " + t("jellyfinRichPresence.state.episode", { episode });
break;
}
} else if (season != null) {
episodeFormat = format === "fulltext" ? `Season ${season}` : `S${season.toString().padStart(2, "0")}`;
episodeFormat = format === "fulltext" ? t("jellyfinRichPresence.state.season", { season }) : `S${season.toString().padStart(2, "0")}`;
} else if (episode != null) {
episodeFormat = format === "fulltext" ? `Episode ${episode}` : `E${episode.toString().padStart(2, "0")}`;
episodeFormat = format === "fulltext" ? t("jellyfinRichPresence.state.episode", { episode }) : `E${episode.toString().padStart(2, "0")}`;
}
if (settings.store.showEpisodeName && mediaData.name && !settings.store.privacyMode) {
@ -390,7 +391,7 @@ export default definePlugin({
return episodeFormat;
}
if (settings.store.privacyMode) {
return mediaData.type === "Audio" ? "Listening to music" : (mediaData.year ? "(????)" : undefined);
return mediaData.type === "Audio" ? t("jellyfinRichPresence.privacyMode.listeningToMusic") : (mediaData.year ? "(????)" : undefined);
}
return mediaData.artist || (mediaData.year ? `(${mediaData.year})` : undefined);
};

View file

@ -6,6 +6,7 @@
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin from "@utils/types";
import { Channel, Message, User } from "@vencord/discord-types";
import { Constants, Menu, NavigationRouter, RestAPI, SelectedChannelStore, SelectedGuildStore, Toasts } from "@webpack/common";
@ -44,7 +45,7 @@ async function jumpToUserMessage(channelId: string, guildId: string, userId: str
if (!messageId) {
Toasts.show({
type: Toasts.Type.FAILURE,
message: "No messages found from this user in this channel.",
message: t("jumpTo.errors.noMessagesFound"),
id: Toasts.genId()
});
return;
@ -55,7 +56,7 @@ async function jumpToUserMessage(channelId: string, guildId: string, userId: str
} catch (e) {
Toasts.show({
type: Toasts.Type.FAILURE,
message: "Failed to search for messages.",
message: t("jumpTo.errors.failedSearch"),
id: Toasts.genId()
});
}
@ -67,12 +68,12 @@ const ChannelMenuPatch: NavContextMenuPatchCallback = (children, { channel }: {
children.push(
<Menu.MenuItem
id="vc-jump-to-first"
label="Jump To First Message"
label={t("jumpTo.ui.jumpToFirst")}
action={() => jumpToFirstMessage(channel.id, channel.guild_id)}
/>,
<Menu.MenuItem
id="vc-jump-to-last"
label="Jump To Last Message"
label={t("jumpTo.ui.jumpToLast")}
action={() => jumpToLastMessage(channel.id, channel.guild_id)}
/>
);
@ -86,12 +87,12 @@ const UserMenuPatch: NavContextMenuPatchCallback = (children, { user, channel }:
children.push(
<Menu.MenuItem
id="vc-jump-to-first"
label="Jump To First Message"
label={t("jumpTo.ui.jumpToFirst")}
action={() => jumpToFirstMessage(channel.id, null)}
/>,
<Menu.MenuItem
id="vc-jump-to-last"
label="Jump To Last Message"
label={t("jumpTo.ui.jumpToLast")}
action={() => jumpToLastMessage(channel.id, null)}
/>
);
@ -107,12 +108,12 @@ const MessageMenuPatch: NavContextMenuPatchCallback = (children, { message }: {
children.push(
<Menu.MenuItem
id="vc-jump-to-first-user"
label="Jump To First Message"
label={t("jumpTo.ui.jumpToFirst")}
action={() => jumpToUserMessage(channelId, guildId, message.author.id, true)}
/>,
<Menu.MenuItem
id="vc-jump-to-last-user"
label="Jump To Last Message"
label={t("jumpTo.ui.jumpToLast")}
action={() => jumpToUserMessage(channelId, guildId, message.author.id, false)}
/>
);
@ -120,7 +121,7 @@ const MessageMenuPatch: NavContextMenuPatchCallback = (children, { message }: {
export default definePlugin({
name: "JumpTo",
description: "Adds context menu options to jump to the start or bottom of a channel/DM.",
description: t("jumpTo.description"),
authors: [Devs.Samwich, Devs.thororen],
contextMenus: {
"channel-context": ChannelMenuPatch,

View file

@ -9,6 +9,7 @@ import "./styles.css";
import { createAudioPlayer } from "@api/AudioPlayer";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { createRoot, FluxDispatcher, useCallback, useEffect, useState } from "@webpack/common";
import { Root } from "react-dom/client";
@ -18,17 +19,17 @@ let jumpscareRoot: Root | undefined;
const settings = definePluginSettings({
imageSource: {
type: OptionType.STRING,
description: "Sets the image url of the jumpscare",
description: t("jumpscare.settings.imageSource"),
default: "https://github.com/Equicord/Equibored/blob/main/icons/jumpscare/troll.gif?raw=true"
},
audioSource: {
type: OptionType.STRING,
description: "Sets the audio url of the jumpscare",
description: t("jumpscare.settings.audioSource"),
default: "https://github.com/Equicord/Equibored/raw/main/sounds/jumpscare/trollolol.mp3?raw=true"
},
chance: {
type: OptionType.NUMBER,
description: "The chance of a jumpscare happening (1 in X so: 100 = 1/100 or 1%, 50 = 1/50 or 2%, etc.)",
description: t("jumpscare.settings.chance"),
default: 1000
}
});
@ -47,7 +48,7 @@ function getJumpscareRoot(): Root {
export default definePlugin({
name: "Jumpscare",
description: "Adds a configurable chance of jumpscaring you whenever you open a channel. Inspired by Geometry Dash Mega Hack",
description: t("jumpscare.description"),
authors: [Devs.surgedevs],
dependencies: ["AudioPlayerAPI"],
settings,

View file

@ -8,6 +8,7 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { closeAllModals } from "@utils/modal";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { SettingsRouter, useState } from "@webpack/common";
@ -19,7 +20,7 @@ let isRecordingGlobal: boolean = false;
export const settings = definePluginSettings({
hotkey: {
description: "The hotkey to open the command palette.",
description: t("keyboardNavigation.settings.hotkey"),
type: OptionType.COMPONENT,
default: ["Control", "Shift", "P"],
component: () => {
@ -69,7 +70,7 @@ export const settings = definePluginSettings({
<div className={`${cl("key-recorder")} ${isRecording ? cl("recording") : ""}`}>
{settings.store.hotkey.map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" + ")}
<button className={`${cl("key-recorder-button")} ${isRecording ? cl("recording-button") : ""}`} disabled={isRecording}>
{isRecording ? "Recording..." : "Record keybind"}
{isRecording ? t("keyboardNavigation.ui.recording") : t("keyboardNavigation.ui.recordKeybind")}
</button>
</div>
</div>
@ -78,7 +79,7 @@ export const settings = definePluginSettings({
}
},
allowMouseControl: {
description: "Allow the mouse to control the command palette.",
description: t("keyboardNavigation.settings.allowMouseControl"),
type: OptionType.BOOLEAN,
default: true
}
@ -86,7 +87,7 @@ export const settings = definePluginSettings({
export default definePlugin({
name: "KeyboardNavigation",
description: "Allows you to navigate the UI with a keyboard.",
description: t("keyboardNavigation.description"),
authors: [Devs.Ethan],
settings,
@ -96,7 +97,7 @@ export default definePlugin({
if (IS_DEV) {
registerAction({
id: "openDevSettings",
label: "Open Dev tab",
label: t("keyboardNavigation.commands.openDevSettings"),
callback: () => SettingsRouter.openUserSettings("equicord_patch_helper_panel"),
registrar: "Equicord"
});

View file

@ -7,6 +7,7 @@
import { AudioPlayerInterface, createAudioPlayer } from "@api/AudioPlayer";
import { definePluginSettings } from "@api/Settings";
import { Devs, EquicordDevs } from "@utils/constants";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { ignoredKeys, packs } from "./packs";
@ -105,7 +106,7 @@ function assignSounds(volume: number, pack: "operagx" | "osu") {
const settings = definePluginSettings({
volume: {
description: "Volume of the keyboard sounds.",
description: t("keyboardSounds.settings.volume"),
type: OptionType.SLIDER,
markers: [0, 25, 50, 75, 100],
stickToMarkers: false,
@ -113,11 +114,11 @@ const settings = definePluginSettings({
onChange: value => { assignSounds(value, settings.store.soundPack); }
},
soundPack: {
description: "Sound pack to use.",
description: t("keyboardSounds.settings.soundPack"),
type: OptionType.SELECT,
options: [
{ label: "OperaGX", value: "operagx" as "operagx", default: true },
{ label: "osu!", value: "osu" as "osu" }
{ label: t("keyboardSounds.options.operaGx"), value: "operagx" as "operagx", default: true },
{ label: t("keyboardSounds.options.osu"), value: "osu" as "osu" }
],
onChange: value => { assignSounds(settings.store.volume, value); }
}
@ -125,7 +126,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "KeyboardSounds",
description: "Adds OperaGX or osu! sound effects when typing on your keyboard.",
description: t("keyboardSounds.description"),
authors: [Devs.HypedDomi, EquicordDevs.Etorix],
dependencies: ["AudioPlayerAPI"],
settings,

View file

@ -18,6 +18,7 @@ import { classNameFactory } from "@utils/css";
import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { useForceUpdater } from "@utils/react";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { findByCodeLazy, findCssClassesLazy } from "@webpack";
@ -162,8 +163,8 @@ function ListTypeSelector({ listType, setListType }: { listType: ListType, setLi
return (
<Select
options={[
{ label: "Whitelist", value: ListType.Whitelist },
{ label: "Blacklist", value: ListType.BlackList }
{ label: t("keywordNotify.options.whitelist"), value: ListType.Whitelist },
{ label: t("keywordNotify.options.blacklist"), value: ListType.BlackList }
]}
placeholder={"Select a list type"}
isSelected={v => v === listType}
@ -205,7 +206,7 @@ function KeywordEntries() {
const elements = keywordEntries.map((entry, i) => {
return (
<>
<Collapsible title={`Keyword Entry ${i + 1}`}>
<Collapsible title={t("keywordNotify.ui.keywordEntry", { index: i + 1 })}>
<Flex flexDirection="row">
<div style={{ flexGrow: 1 }}>
<TextInput
@ -224,14 +225,14 @@ function KeywordEntries() {
</Button>
</Flex>
<FormSwitch
title="Ignore Case"
title={t("keywordNotify.ui.ignoreCase")}
className={cl("ignoreCaseSwitch")}
value={values[i].ignoreCase}
onChange={() => {
setIgnoreCase(i, !values[i].ignoreCase);
}}
/>
<Heading tag="h5">Whitelist/Blacklist</Heading>
<Heading tag="h5">{t("keywordNotify.ui.whitelistBlacklist")}</Heading>
<Flex flexDirection="row">
<div style={{ flexGrow: 1 }}>
<ListedIds listIds={values[i].listIds} setListIds={e => setListIds(i, e)} />
@ -242,7 +243,7 @@ function KeywordEntries() {
<Button onClick={() => {
values[i].listIds.push("");
update();
}}>Add ID</Button>
}}>{t("keywordNotify.ui.addId")}</Button>
<div style={{ flexGrow: 1 }}>
<ListTypeSelector listType={values[i].listType} setListType={e => setListType(i, e)} />
</div>
@ -255,7 +256,7 @@ function KeywordEntries() {
return (
<>
{elements}
<div><Button onClick={() => addKeywordEntry(update)}>Add Keyword Entry</Button></div>
<div><Button onClick={() => addKeywordEntry(update)}>{t("keywordNotify.ui.addKeywordEntry")}</Button></div>
</>
);
}
@ -299,17 +300,17 @@ function DoubleCheckmarkIcon(props: IconProps) {
const settings = definePluginSettings({
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Ignore messages from bots",
description: t("keywordNotify.settings.ignoreBots"),
default: true
},
amountToKeep: {
type: OptionType.NUMBER,
description: "Amount of messages to keep in the log",
description: t("keywordNotify.settings.amountToKeep"),
default: 50
},
keywords: {
type: OptionType.COMPONENT,
description: "Manage keywords",
description: t("keywordNotify.settings.keywords"),
component: () => <KeywordEntries />
}
});
@ -317,7 +318,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "KeywordNotify",
authors: [EquicordDevs.camila314, EquicordDevs.x3rt],
description: "Sends a notification if a given message matches certain keywords or regexes",
description: t("keywordNotify.description"),
settings,
patches: [
{
@ -497,14 +498,14 @@ export default definePlugin({
keywordTabBar() {
return (
<TabBar.Item className={classes(tabClass.tab)} id={8}>
Keywords
{t("keywordNotify.ui.keywords")}
</TabBar.Item>
);
},
keywordClearButton() {
return (
<Tooltip text="Clear All">
<Tooltip text={t("keywordNotify.ui.clearAll")}>
{({ onMouseLeave, onMouseEnter }) => (
<Button
variant="secondary"

Some files were not shown because too many files have changed in this diff Show more