diff --git a/.vscode/i18n-ally-custom-framework.yml b/.vscode/i18n-ally-custom-framework.yml
index 5c68edc42..4cdc6aae0 100644
--- a/.vscode/i18n-ally-custom-framework.yml
+++ b/.vscode/i18n-ally-custom-framework.yml
@@ -6,5 +6,10 @@ languageIds:
usageMatchRegex:
- "[^\\w\\d]\\$t\\(['\"`]({key})['\"`]"
+ - ""
monopoly: true
diff --git a/src/components/PluginSettings/ContributorModal.tsx b/src/components/PluginSettings/ContributorModal.tsx
index 99a8da168..022160c2e 100644
--- a/src/components/PluginSettings/ContributorModal.tsx
+++ b/src/components/PluginSettings/ContributorModal.tsx
@@ -12,8 +12,8 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Link } from "@components/Link";
import { DevsById } from "@utils/constants";
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
-import { pluralise } from "@utils/misc";
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
+import { Translate } from "@utils/translation";
import { Forms, MaskedLink, showToast, Tooltip, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
import { User } from "discord-types/general";
@@ -108,15 +108,9 @@ function ContributorModal({ user }: { user: User; }) {
- {plugins.length ? (
-
- This person has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}!
-
- ) : (
-
- This person has not made any plugins. They likely {ContributedHyperLink} to Vencord in other ways!
-
- )}
+
+
+
{!!plugins.length && (
diff --git a/src/utils/translation.ts b/src/utils/translation.tsx
similarity index 74%
rename from src/utils/translation.ts
rename to src/utils/translation.tsx
index 807ac1d8d..a1cba453d 100644
--- a/src/utils/translation.ts
+++ b/src/utils/translation.tsx
@@ -5,7 +5,7 @@
*/
import { negotiateLanguages } from "@fluent/langneg";
-import { FluxDispatcher, i18n } from "@webpack/common";
+import { FluxDispatcher, i18n, React } from "@webpack/common";
import translations from "~translations";
@@ -124,3 +124,37 @@ export function $t(key: string, variables?: Record): string {
return format(translation as string, variables);
}
+
+interface TranslateProps {
+ /** The key to translate. */
+ i18nKey: string;
+ /** The variables to interpolate into the resultant string. If dealing with plurals, `count` must be set. */
+ variables?: Record;
+ /** The component(s) to interpolate into the resultant string. */
+ children: JSX.Element | JSX.Element[];
+}
+
+/**
+ * A translation component. Follows the same rules as {@link $t}, but lets you add components to strings.
+ * @param param0 Component props.
+ */
+export function Translate({ i18nKey, variables, children: trueChildren }: TranslateProps): JSX.Element {
+ const children = [trueChildren].flat();
+
+ const translation = $t(i18nKey, variables);
+
+ const parts = translation.split(/(<\d+>.*?<\/\d+>)/g);
+
+ const finalChildren = parts.map((part, index) => {
+ const match = part.match(/<(\d+)>(.*?)<\/\d+>/);
+
+ if (match) {
+ const childIndex = parseInt(match[1], 10);
+ return React.cloneElement(children[childIndex], { key: index }, match[2]);
+ }
+
+ return part;
+ });
+
+ return <>{finalChildren}>;
+}
diff --git a/translations/en/vencord.json b/translations/en/vencord.json
index 3c24ed09b..ca08ff5ed 100644
--- a/translations/en/vencord.json
+++ b/translations/en/vencord.json
@@ -5,6 +5,15 @@
"error": "An error occurred while rendering this Component. More info can be found below and in your console.",
"ohNo": "Oh no!"
},
+ "pluginSettings": {
+ "contributorModal": {
+ "contributed": {
+ "zero": "This person has not made any plugins. They likely <0>contributed0> to Vencord in other ways!",
+ "one": "This person has <0>contributed0> to one plugin!",
+ "other": "This person has <0>contributed0> to {count} plugins!"
+ }
+ }
+ },
"vencordSettings": {
"addonCard": {
"new": "NEW"