feat: initial implementation of translation component
This commit is contained in:
parent
7ed73b49e5
commit
16549695d1
5
.vscode/i18n-ally-custom-framework.yml
vendored
5
.vscode/i18n-ally-custom-framework.yml
vendored
|
@ -6,5 +6,10 @@ languageIds:
|
||||||
|
|
||||||
usageMatchRegex:
|
usageMatchRegex:
|
||||||
- "[^\\w\\d]\\$t\\(['\"`]({key})['\"`]"
|
- "[^\\w\\d]\\$t\\(['\"`]({key})['\"`]"
|
||||||
|
- "<Translate ?.* i18nKey=\\{?['\"`]({key})['\"`]"
|
||||||
|
|
||||||
|
refactorTemplates:
|
||||||
|
- "$t(\"$1\")"
|
||||||
|
- "<Translate i18nKey=\"$1\" />"
|
||||||
|
|
||||||
monopoly: true
|
monopoly: true
|
||||||
|
|
|
@ -12,8 +12,8 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { DevsById } from "@utils/constants";
|
import { DevsById } from "@utils/constants";
|
||||||
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
|
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
|
||||||
import { pluralise } from "@utils/misc";
|
|
||||||
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
|
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 { Forms, MaskedLink, showToast, Tooltip, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -108,15 +108,9 @@ function ContributorModal({ user }: { user: User; }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{plugins.length ? (
|
<Translate i18nKey="vencord.components.pluginSettings.contributorModal.contributed" variables={{ count: plugins.length }}>
|
||||||
<Forms.FormText>
|
<Link href="https://vencord.dev/source" />
|
||||||
This person has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}!
|
</Translate>
|
||||||
</Forms.FormText>
|
|
||||||
) : (
|
|
||||||
<Forms.FormText>
|
|
||||||
This person has not made any plugins. They likely {ContributedHyperLink} to Vencord in other ways!
|
|
||||||
</Forms.FormText>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!!plugins.length && (
|
{!!plugins.length && (
|
||||||
<div className={cl("plugins")}>
|
<div className={cl("plugins")}>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { negotiateLanguages } from "@fluent/langneg";
|
import { negotiateLanguages } from "@fluent/langneg";
|
||||||
import { FluxDispatcher, i18n } from "@webpack/common";
|
import { FluxDispatcher, i18n, React } from "@webpack/common";
|
||||||
|
|
||||||
import translations from "~translations";
|
import translations from "~translations";
|
||||||
|
|
||||||
|
@ -124,3 +124,37 @@ export function $t(key: string, variables?: Record<string, any>): string {
|
||||||
|
|
||||||
return format(translation as string, variables);
|
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<string, any>;
|
||||||
|
/** 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}</>;
|
||||||
|
}
|
|
@ -5,6 +5,15 @@
|
||||||
"error": "An error occurred while rendering this Component. More info can be found below and in your console.",
|
"error": "An error occurred while rendering this Component. More info can be found below and in your console.",
|
||||||
"ohNo": "Oh no!"
|
"ohNo": "Oh no!"
|
||||||
},
|
},
|
||||||
|
"pluginSettings": {
|
||||||
|
"contributorModal": {
|
||||||
|
"contributed": {
|
||||||
|
"zero": "This person has not made any plugins. They likely <0>contributed</0> to Vencord in other ways!",
|
||||||
|
"one": "This person has <0>contributed</0> to one plugin!",
|
||||||
|
"other": "This person has <0>contributed</0> to {count} plugins!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"vencordSettings": {
|
"vencordSettings": {
|
||||||
"addonCard": {
|
"addonCard": {
|
||||||
"new": "NEW"
|
"new": "NEW"
|
||||||
|
|
Loading…
Reference in a new issue