From db3a5869a67f662d0049a7c06f017420d636a261 Mon Sep 17 00:00:00 2001 From: eduardozgz Date: Tue, 23 Jul 2024 22:56:53 +0200 Subject: [PATCH] add translations --- apps/website/src/@types/resources.d.ts | 132 ++++++++++++++++++ apps/website/src/app/account/DeleteButton.tsx | 17 ++- .../src/app/account/DisplayUserBadges.tsx | 31 ++-- apps/website/src/app/account/page.tsx | 10 +- .../src/app/admin/guilds/LoadGuildInput.tsx | 8 +- .../src/app/admin/users/DisplayUser.tsx | 52 ++++--- .../src/app/admin/users/LoadUserInput.tsx | 6 +- .../src/app/admin/users/RecentUsers.tsx | 7 +- .../src/app/admin/users/[id]/DeleteButton.tsx | 16 ++- .../src/app/admin/users/[id]/ManageUser.tsx | 60 +++++--- .../website/src/app/admin/users/[id]/page.tsx | 5 +- .../servers/[guildId]/BlockedBanner.tsx | 37 +++-- .../servers/[guildId]/ChannelMaps.ts | 25 ++-- .../servers/[guildId]/ForbiddenPage.tsx | 10 +- .../servers/[guildId]/InviteBotBanner.tsx | 15 +- .../servers/[guildId]/InviteBotPage.tsx | 25 ++-- .../app/dashboard/servers/[guildId]/page.tsx | 61 +++++--- apps/website/src/app/layout.tsx | 11 +- .../src/i18n/{client.ts => client.tsx} | 8 +- apps/website/src/i18n/locales/en-US/main.json | 132 ++++++++++++++++++ 20 files changed, 515 insertions(+), 153 deletions(-) rename apps/website/src/i18n/{client.ts => client.tsx} (88%) diff --git a/apps/website/src/@types/resources.d.ts b/apps/website/src/@types/resources.d.ts index 4a90f7aa6..8d4d6cc94 100644 --- a/apps/website/src/@types/resources.d.ts +++ b/apps/website/src/@types/resources.d.ts @@ -24,6 +24,138 @@ interface Resources { "supportBtn": "Get support", "backBtn": "Go back" } + }, + "dashboard": { + "servers": { + "suggestedTopics": { + "quickSetup": { + "title": "Quick-setup", + "description": "Quickly create the most common counters in a few clicks. No imagination or brain required!", + "label": "Hassle free!" + }, + "createFromScratch": { + "title": "Create from scratch", + "description": "Learn how to create your first custom counter in a few minutes", + "label": "Be unique!" + }, + "advancedCounters": { + "title": "Advanced counters", + "description": "Get the most out of your counters, tailored to your needs", + "label": "Like a pro" + }, + "queryHistoricalStatistics": { + "title": "Query historical statistics", + "description": "See how your counters and other common stats have evolved over time", + "label": "Coming Soon™" + }, + "title": "Welcome back!", + "subTitle": "Here are some suggestions for you" + }, + "inviteBotPage": { + "title": "Let's set up the bot!", + "subtitle": "Add Member counter to {{serverName}} and enjoy realtime counters.", + "addToServer": "Add to {{serverName}}", + "noPermission": "You don't have enough permissions to add the bot. Please ask an administrator or someone with Manage Server permission to add this bot.", + "useOrShareLink": "Use or share this link to add the bot", + "linkCopied": "The link has been copied to your clipboard", + "copyLink": "Copy invite link" + }, + "inviteBotBanner": { + "message": "It looks like the Member Counter Bot isn't in this server. Would you like to add it?", + "closeBtn": "Close" + }, + "forbiddenPage": { + "message": "You don't have permission to manage this server.\nAsk an admin at {guildName} to give you Administrator or Manage Guild permissions.", + "title": "Permission Denied" + }, + "blockedBanner": { + "text": "This server has been blocked for violating our Terms of Service or Acceptable Use Policy.
Reason given: {{reason}}
If you think this is a mistake, please contact our support team.", + "termsOfService": "Terms of Service", + "acceptableUsePolicy": "Acceptable Use Policy", + "noReasonGiven": "No reason given", + "supportTeam": "support team" + } + } + }, + "account": { + "deleteButton": { + "deleteAccountBtn": "Delete account", + "confirmTitle": "Are you absolutely sure?", + "confirmDescription": "This action cannot be undone. This will permanently delete your account and remove your data from our servers.", + "closeBtn": "Close" + }, + "userBadges": { + "donor": "You donated to support the development and maintenance of Member Counter", + "premium": "You are a premium user", + "betaTester": "You participated in a beta program", + "translator": "You helped to translate the bot", + "contributor": "You implemented a feature or fixed a bug", + "bigBrain": "You suggested an idea and it was implemented", + "bugCatcher": "You found and reported a bug", + "patPat": "You found a secret", + "foldingAtHome": "You contributed a WU in folding@home" + }, + "page": { + "avatarAlt": "{{username}}'s avatar", + "logoutButton": "Logout" + } + }, + "admin": { + "guilds": { + "loadGuildInput": { + "placeholder": "Paste server ID", + "loadButton": "Load server" + } + }, + "users": { + "delete": { + "button": "Delete account", + "dialogTitle": "Are you absolutely sure?", + "dialogDescription": "This action cannot be undone.", + "closeButton": "Close" + }, + "manage": { + "permissions": { + "title": "Permissions", + "seeUsers": "See Users", + "manageUsers": "Manage Users", + "seeGuilds": "See Servers", + "manageGuilds": "Manage Servers" + }, + "badges": { + "title": "Badges", + "donor": "Donor", + "premium": "Premium", + "betaTester": "Beta Tester", + "translator": "Translator", + "contributor": "Contributor", + "bigBrain": "Big Brain", + "bugCatcher": "Bug Catcher", + "patPat": "Pat Pat", + "foldingAtHome": "Folding@Home" + }, + "transferAccount": "Transfer account", + "pasteUserId": "Paste user ID", + "saved": "Saved", + "save": "Save" + }, + "loadUser": "Load user", + "recentUsers": "Recent users", + "userNotRegistered": "This user isn't registered." + } + } + }, + "common": { + "unknownServer": "Unknown server", + "unknownUser": "Unknown user {{id}}", + "channelLabels": { + "textChannel": "Text channel", + "category": "Category", + "voiceChannel": "Voice channel", + "announcementChannel": "Announcement channel", + "stageChannel": "Stage channel", + "forumChannel": "Forum channel", + "mediaChannel": "Media channel" } } } diff --git a/apps/website/src/app/account/DeleteButton.tsx b/apps/website/src/app/account/DeleteButton.tsx index 72157594b..69cbc9f19 100644 --- a/apps/website/src/app/account/DeleteButton.tsx +++ b/apps/website/src/app/account/DeleteButton.tsx @@ -2,6 +2,7 @@ import { useRouter } from "next/navigation"; import { TrashIcon } from "lucide-react"; +import { Trans } from "react-i18next"; import { Button } from "@mc/ui/button"; import { @@ -40,16 +41,16 @@ export function DeleteButton() { variant={"destructive"} icon={TrashIcon} > - Delete account + - {/* // Thank you shad for providing me exactly the text I wanted in your dialog example lol */} - Are you absolutely sure? + + + - This action cannot be undone. This will permanently delete your - account and remove your data from our servers. + - + diff --git a/apps/website/src/app/account/DisplayUserBadges.tsx b/apps/website/src/app/account/DisplayUserBadges.tsx index 682f86ec5..9c6b18ad1 100644 --- a/apps/website/src/app/account/DisplayUserBadges.tsx +++ b/apps/website/src/app/account/DisplayUserBadges.tsx @@ -1,5 +1,8 @@ "use client"; +import type { TFunction } from "i18next"; +import { useTranslation } from "react-i18next"; + import { BitField } from "@mc/common/BitField"; import { UserBadges, @@ -13,23 +16,25 @@ import { TooltipTrigger, } from "@mc/ui/tooltip"; -const UserBadgesDescription: Record<(typeof UserBadges)[number], string> = { - Donor: - "You donated to support the development and maintenance of Member Counter", - Premium: "You are a premium user", - BetaTester: "You participated in a beta program", - Translator: "You helped to translate the bot", - Contributor: "You implemented a feature or fixed a bug", - BigBrain: "You suggested an idea and it was implemented", - BugCatcher: "You found and reported a bug", - PatPat: "You found a secret", - FoldingAtHome: "You contributed a WU in folding@home", -} as const; +const getUserBadgesDescription = (t: TFunction) => ({ + Donor: t("pages.account.userBadges.donor"), + Premium: t("pages.account.userBadges.premium"), + BetaTester: t("pages.account.userBadges.betaTester"), + Translator: t("pages.account.userBadges.translator"), + Contributor: t("pages.account.userBadges.contributor"), + BigBrain: t("pages.account.userBadges.bigBrain"), + BugCatcher: t("pages.account.userBadges.bugCatcher"), + PatPat: t("pages.account.userBadges.patPat"), + FoldingAtHome: t("pages.account.userBadges.foldingAtHome"), +}); export function DisplayUserBadges({ badges: unparsed }: { badges: bigint }) { + const { t } = useTranslation(); + if (unparsed === 0n) return; const badges = new BitField(unparsed); + const userBadgesDescription = getUserBadgesDescription(t); const badgesToDisplay: { badge: (typeof UserBadges)[number]; @@ -39,7 +44,7 @@ export function DisplayUserBadges({ badges: unparsed }: { badges: bigint }) { (badge) => ({ badge, emoji: UserBadgesEmoji[badge], - description: UserBadgesDescription[badge], + description: userBadgesDescription[badge], }), ); diff --git a/apps/website/src/app/account/page.tsx b/apps/website/src/app/account/page.tsx index 78b77634c..23f0f4304 100644 --- a/apps/website/src/app/account/page.tsx +++ b/apps/website/src/app/account/page.tsx @@ -4,6 +4,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { LogOutIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; import { Button } from "@mc/ui/button"; import { Skeleton } from "@mc/ui/skeleton"; @@ -15,6 +16,7 @@ import { DeleteButton } from "./DeleteButton"; import { DisplayUserBadges } from "./DisplayUserBadges"; export default function Page() { + const { t } = useTranslation(); const isAuthenticated = api.session.isAuthenticated.useQuery(); if (isAuthenticated.data === false) redirect(Routes.Login); @@ -35,11 +37,13 @@ export default function Page() { {discordUser.isSuccess ? ( {`${discordUser.data.username}'s ) : ( - + )}
{discordUser.isSuccess ? ( @@ -61,7 +65,7 @@ export default function Page() {
diff --git a/apps/website/src/app/admin/guilds/LoadGuildInput.tsx b/apps/website/src/app/admin/guilds/LoadGuildInput.tsx index 43d9ffbad..10619943b 100644 --- a/apps/website/src/app/admin/guilds/LoadGuildInput.tsx +++ b/apps/website/src/app/admin/guilds/LoadGuildInput.tsx @@ -1,5 +1,8 @@ +"use client"; + import { useState } from "react"; import { useRouter } from "next/navigation"; +import { useTranslation } from "react-i18next"; import { Button } from "@mc/ui/button"; import { Input } from "@mc/ui/input"; @@ -7,6 +10,7 @@ import { Input } from "@mc/ui/input"; import { Routes } from "~/other/routes"; export const LoadGuildInput = () => { + const { t } = useTranslation(); const router = useRouter(); const [guildId, setGuildId] = useState(""); @@ -21,14 +25,14 @@ export const LoadGuildInput = () => { value={guildId} onChange={(e) => setGuildId(e.target.value)} onKeyDown={(e) => e.key === "Enter" && loadGuild(guildId)} - placeholder="Paste server ID" + placeholder={t("pages.admin.guilds.loadGuildInput.placeholder")} /> ); diff --git a/apps/website/src/app/admin/users/DisplayUser.tsx b/apps/website/src/app/admin/users/DisplayUser.tsx index 3d389e072..575f98ddb 100644 --- a/apps/website/src/app/admin/users/DisplayUser.tsx +++ b/apps/website/src/app/admin/users/DisplayUser.tsx @@ -1,3 +1,7 @@ +"use client"; + +import { useTranslation } from "react-i18next"; + /* eslint-disable @next/next/no-img-element */ export const DisplayUser = ({ id, @@ -9,24 +13,30 @@ export const DisplayUser = ({ username?: string; discriminator?: string; avatar?: string; -}) => ( -
- {avatar && username && discriminator ? ( - <> - {`${username}'s -
- {username} - {discriminator !== "0" && ( - #{discriminator} - )} -
- - ) : ( - Uknown user {id} - )} -
-); +}) => { + const { t } = useTranslation(); + + return ( +
+ {avatar && username && discriminator ? ( + <> + {`${username}'s +
+ {username} + {discriminator !== "0" && ( + #{discriminator} + )} +
+ + ) : ( + + {t("common.unknownUser", { id })} + + )} +
+ ); +}; diff --git a/apps/website/src/app/admin/users/LoadUserInput.tsx b/apps/website/src/app/admin/users/LoadUserInput.tsx index 1f3db36af..d7f5583e4 100644 --- a/apps/website/src/app/admin/users/LoadUserInput.tsx +++ b/apps/website/src/app/admin/users/LoadUserInput.tsx @@ -1,5 +1,8 @@ +"use client"; + import { useState } from "react"; import { useRouter } from "next/navigation"; +import { useTranslation } from "react-i18next"; import { Button } from "@mc/ui/button"; import { Input } from "@mc/ui/input"; @@ -7,6 +10,7 @@ import { Input } from "@mc/ui/input"; import { Routes } from "~/other/routes"; export const LoadUserInput = () => { + const { t } = useTranslation(); const router = useRouter(); const [userId, setUserId] = useState(""); @@ -28,7 +32,7 @@ export const LoadUserInput = () => { onClick={() => loadUser(userId)} disabled={!userId} > - Load user + {t("pages.admin.users.loadUser")} ); diff --git a/apps/website/src/app/admin/users/RecentUsers.tsx b/apps/website/src/app/admin/users/RecentUsers.tsx index 90e4f6271..c69f1c809 100644 --- a/apps/website/src/app/admin/users/RecentUsers.tsx +++ b/apps/website/src/app/admin/users/RecentUsers.tsx @@ -1,5 +1,7 @@ "use client"; +import { useTranslation } from "react-i18next"; + import { cn } from "@mc/ui"; import { Card, CardContent, CardHeader } from "@mc/ui/card"; import { TypographyH4 } from "@mc/ui/TypographyH4"; @@ -14,6 +16,7 @@ import { } from "./recentUsers"; export function RecentUsers({ className }: { className?: string }) { + const { t } = useTranslation(); const [recentUsers] = useLocalStorage( recentUsersKey, recentUsersSchema, @@ -26,7 +29,9 @@ export function RecentUsers({ className }: { className?: string }) { {!!recentUsers.length && ( - Recent users + + {t("pages.admin.users.recentUsers")} + {recentUsers.map((userId) => ( diff --git a/apps/website/src/app/admin/users/[id]/DeleteButton.tsx b/apps/website/src/app/admin/users/[id]/DeleteButton.tsx index 1d92a1f20..4fd82a701 100644 --- a/apps/website/src/app/admin/users/[id]/DeleteButton.tsx +++ b/apps/website/src/app/admin/users/[id]/DeleteButton.tsx @@ -2,6 +2,7 @@ import { useRouter } from "next/navigation"; import { TrashIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; import { Button } from "@mc/ui/button"; import { @@ -24,6 +25,7 @@ export function DeleteButton({ userId: string; disabled?: boolean; }) { + const { t } = useTranslation(); const router = useRouter(); const deleteUser = api.user.delete.useMutation(); @@ -36,23 +38,27 @@ export function DeleteButton({ - Are you absolutely sure? - This action cannot be undone. + {t("pages.admin.users.delete.dialogTitle")} + + {t("pages.admin.users.delete.dialogDescription")} + - + diff --git a/apps/website/src/app/admin/users/[id]/ManageUser.tsx b/apps/website/src/app/admin/users/[id]/ManageUser.tsx index 3ef8707f1..3a288b062 100644 --- a/apps/website/src/app/admin/users/[id]/ManageUser.tsx +++ b/apps/website/src/app/admin/users/[id]/ManageUser.tsx @@ -1,6 +1,8 @@ +import type { TFunction } from "i18next"; import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { SaveIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; import { BitField } from "@mc/common/BitField"; import { UserBadgesBitfield } from "@mc/common/UserBadges"; @@ -16,26 +18,27 @@ import { Routes } from "~/other/routes"; import { api } from "~/trpc/react"; import { DeleteButton } from "./DeleteButton"; -const permissionsLabels: Record = { - SeeUsers: "See Users", - ManageUsers: "Manage Users", - SeeGuilds: "See Servers", - ManageGuilds: "Manage Servers", -} as const; - -const badgesLabels: Record = { - Donor: "Donor", - Premium: "Premium", - BetaTester: "Beta Tester", - Translator: "Translator", - Contributor: "Contributor", - BigBrain: "Big Brain", - BugCatcher: "Bug Catcher", - PatPat: "Pat Pat", - FoldingAtHome: "Folding@Home", -} as const; +const getPermissionsLabels = (t: TFunction) => ({ + SeeUsers: t("pages.admin.users.manage.permissions.seeUsers"), + ManageUsers: t("pages.admin.users.manage.permissions.manageUsers"), + SeeGuilds: t("pages.admin.users.manage.permissions.seeGuilds"), + ManageGuilds: t("pages.admin.users.manage.permissions.manageGuilds"), +}); + +const getBadgesLabels = (t: TFunction) => ({ + Donor: t("pages.admin.users.manage.badges.donor"), + Premium: t("pages.admin.users.manage.badges.premium"), + BetaTester: t("pages.admin.users.manage.badges.betaTester"), + Translator: t("pages.admin.users.manage.badges.translator"), + Contributor: t("pages.admin.users.manage.badges.contributor"), + BigBrain: t("pages.admin.users.manage.badges.bigBrain"), + BugCatcher: t("pages.admin.users.manage.badges.bugCatcher"), + PatPat: t("pages.admin.users.manage.badges.patPat"), + FoldingAtHome: t("pages.admin.users.manage.badges.foldingAtHome"), +}); export default function ManageUser({ userId }: { userId: string }) { + const { t } = useTranslation(); const router = useRouter(); const [enableTransfer, setEnableTransfer] = useState(false); const [isDirty, setIsDirty] = useState(false); @@ -72,11 +75,16 @@ export default function ManageUser({ userId }: { userId: string }) { router.replace(Routes.ManageUsers(mutableUser.discordUserId)); }; + const permissionsLabels = getPermissionsLabels(t); + const badgesLabels = getBadgesLabels(t); + return (
- Permissions + + {t("pages.admin.users.manage.permissions.title")} + {Object.entries(permissionsLabels).map((entry) => { const [permission, label] = entry as [ keyof typeof permissionsLabels, @@ -107,7 +115,9 @@ export default function ManageUser({ userId }: { userId: string }) { })}
- Badges + + {t("pages.admin.users.manage.badges.title")} + {Object.entries(badgesLabels).map((entry) => { const [badge, label] = entry as [keyof typeof badgesLabels, string]; @@ -139,11 +149,13 @@ export default function ManageUser({ userId }: { userId: string }) { checked={enableTransfer} onCheckedChange={(v) => setEnableTransfer(!!v)} > - Transfer account + + {t("pages.admin.users.manage.transferAccount")} + {enableTransfer && ( setMutableUser({ @@ -162,7 +174,9 @@ export default function ManageUser({ userId }: { userId: string }) { type="submit" disabled={!canModify || !isDirty || userMutation.isPending} > - {userMutation.isSuccess && !isDirty ? "Saved" : "Save"} + {userMutation.isSuccess && !isDirty + ? t("pages.admin.users.manage.saved") + : t("pages.admin.users.manage.save")} diff --git a/apps/website/src/app/admin/users/[id]/page.tsx b/apps/website/src/app/admin/users/[id]/page.tsx index 8d32eedcd..6a95b5761 100644 --- a/apps/website/src/app/admin/users/[id]/page.tsx +++ b/apps/website/src/app/admin/users/[id]/page.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { LoaderIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; import { Card, CardContent, CardHeader } from "@mc/ui/card"; @@ -31,8 +32,8 @@ export default function Page({ params: { id: userId } }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [userId]); + const { t } = useTranslation(); const discordUser = api.discord.getUser.useQuery({ id: userId }); - const user = api.user.get.useQuery( { discordUserId: userId }, { throwOnError: true }, @@ -48,7 +49,7 @@ export default function Page({ params: { id: userId } }: Props) { {!user.data && !user.isLoading && ( - This user isn't registered. + {t("pages.admin.users.userNotRegistered")} )} {user.data && } diff --git a/apps/website/src/app/dashboard/servers/[guildId]/BlockedBanner.tsx b/apps/website/src/app/dashboard/servers/[guildId]/BlockedBanner.tsx index 6531a99f3..1c6fae99c 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/BlockedBanner.tsx +++ b/apps/website/src/app/dashboard/servers/[guildId]/BlockedBanner.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; import { ShieldBanIcon, XIcon } from "lucide-react"; +import { Trans } from "react-i18next"; import { Button } from "@mc/ui/button"; import { LinkUnderlined } from "@mc/ui/LinkUnderlined"; @@ -20,33 +21,31 @@ export function BlockedBanner() { setClosed(false); }, [blockedState.data]); - if (!blockedState.isSuccess) return; - if (!blockedState.data) return; - if (closed) return; + if (!blockedState.isSuccess) return null; + if (!blockedState.data) return null; + if (closed) return null; const reason = blockedState.data.reason; + return (
-

- This server has been blocked for violating our{" "} - - Terms of conditions - {" "} - or{" "} - - Acceptable Use Policy - - . -
- Reason given: {reason.trim().length ? reason : "No reason given"} -
- If you think this is a mistake, please contact our{" "} - support team. -

+ + ), + LinkPolicy: ( + + ), + SupportLink: , + }} + values={{ reason: reason.trim().length ? reason : undefined }} + />
diff --git a/apps/website/src/app/dashboard/servers/[guildId]/ChannelMaps.ts b/apps/website/src/app/dashboard/servers/[guildId]/ChannelMaps.ts index b0b75a073..5967501b7 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/ChannelMaps.ts +++ b/apps/website/src/app/dashboard/servers/[guildId]/ChannelMaps.ts @@ -1,3 +1,4 @@ +import type { TFunction } from "i18next"; import type { LucideIcon } from "lucide-react"; import { useParams } from "next/navigation"; import { ChannelType, PermissionFlagsBits } from "discord-api-types/v10"; @@ -30,15 +31,21 @@ export const ChannelIconMap: Record = { [ChannelType.GuildMedia]: ImageIcon, }; -export const ChannelLabelMap: Record = { - [ChannelType.GuildText]: "Text channel", - [ChannelType.GuildCategory]: "Category", - [ChannelType.GuildVoice]: "Voice channel", - [ChannelType.GuildAnnouncement]: "Announcement channel", - [ChannelType.GuildStageVoice]: "Stage channel", - [ChannelType.GuildForum]: "Forum channel", - [ChannelType.GuildMedia]: "Media channel", -}; +export function ChannelLabelMap( + t: TFunction, +): Record { + return { + [ChannelType.GuildText]: t("common.channelLabels.textChannel"), + [ChannelType.GuildCategory]: t("common.channelLabels.category"), + [ChannelType.GuildVoice]: t("common.channelLabels.voiceChannel"), + [ChannelType.GuildAnnouncement]: t( + "common.channelLabels.announcementChannel", + ), + [ChannelType.GuildStageVoice]: t("common.channelLabels.stageChannel"), + [ChannelType.GuildForum]: t("common.channelLabels.forumChannel"), + [ChannelType.GuildMedia]: t("common.channelLabels.mediaChannel"), + }; +} export function useChannelIcon( channelId: string, diff --git a/apps/website/src/app/dashboard/servers/[guildId]/ForbiddenPage.tsx b/apps/website/src/app/dashboard/servers/[guildId]/ForbiddenPage.tsx index 59c2473de..3737d3ba4 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/ForbiddenPage.tsx +++ b/apps/website/src/app/dashboard/servers/[guildId]/ForbiddenPage.tsx @@ -2,6 +2,7 @@ import { useParams } from "next/navigation"; import { BanIcon } from "lucide-react"; import type { DashboardGuildParams } from "./layout"; +import { useTranslation } from "~/i18n/client"; import { api } from "~/trpc/react"; import { MenuButton } from "../../Menu"; @@ -12,15 +13,18 @@ export function ForbiddenPage() { }); const guild = userGuildsQuery.data.userGuilds.get(guildId); + const [t] = useTranslation(); return (
- You don't have permission to manage this server.
- Ask an admin at {guild?.name ?? "Unknown server"} to give you - Administrator or Manage Guild permissions. +

+ {t("pages.dashboard.servers.forbiddenPage.message", { + guildName: guild?.name ?? t("common.unknownServer"), + })} +

); diff --git a/apps/website/src/app/dashboard/servers/[guildId]/InviteBotBanner.tsx b/apps/website/src/app/dashboard/servers/[guildId]/InviteBotBanner.tsx index 83ff98dab..7befaca50 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/InviteBotBanner.tsx +++ b/apps/website/src/app/dashboard/servers/[guildId]/InviteBotBanner.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; import { BotIcon, XIcon } from "lucide-react"; +import { Trans } from "react-i18next"; import { Button } from "@mc/ui/button"; import { LinkUnderlined } from "@mc/ui/LinkUnderlined"; @@ -37,12 +38,14 @@ export function InviteBotBanner() {

- Seems like Member Counter Bot isn't in this server. Would you like - to  - - add it - - ? + + ), + }} + />

diff --git a/apps/website/src/app/dashboard/servers/[guildId]/InviteBotPage.tsx b/apps/website/src/app/dashboard/servers/[guildId]/InviteBotPage.tsx index 2ad11e341..d36f471a0 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/InviteBotPage.tsx +++ b/apps/website/src/app/dashboard/servers/[guildId]/InviteBotPage.tsx @@ -9,6 +9,7 @@ import { LinkUnderlined } from "@mc/ui/LinkUnderlined"; import type { DashboardGuildParams } from "./layout"; import { BotIcon } from "~/app/components/BotIcon"; import { DiscordIcon } from "~/app/components/DiscordIcon"; +import { useTranslation } from "~/i18n/client"; import { Routes } from "~/other/routes"; import { api } from "~/trpc/react"; import { MenuButton } from "../../Menu"; @@ -28,6 +29,8 @@ export function InviteBotPage() { return () => clearTimeout(timeoutId); }, [copySuccess]); + const [t] = useTranslation(); + const inviteLink = Routes.Invite(guildId); const copyLink = async () => { try { @@ -52,30 +55,30 @@ export function InviteBotPage() {

- Let's setup the bot! + {t("pages.dashboard.servers.inviteBotPage.title")}

- Add Member counter to {guild?.name ?? "Unknown server"} and enjoy - realtime counters. + {t("pages.dashboard.servers.inviteBotPage.subtitle", { + serverName: guild?.name ?? t("common.unknownServer"), + })}

{userPermissions.canInviteBot ? ( ) : ( <>
- You don't have enough permissions to add the bot. -
- Please ask an administrator or someone with Manage Server - permission to add this bot. + {t("pages.dashboard.servers.inviteBotPage.noPermission")}
{clipboardFailed ? ( - Use or share this link to add the bot + {t("pages.dashboard.servers.inviteBotPage.useOrShareLink")} ) : ( )} diff --git a/apps/website/src/app/dashboard/servers/[guildId]/page.tsx b/apps/website/src/app/dashboard/servers/[guildId]/page.tsx index ef4fa566e..42cc27f6e 100644 --- a/apps/website/src/app/dashboard/servers/[guildId]/page.tsx +++ b/apps/website/src/app/dashboard/servers/[guildId]/page.tsx @@ -1,5 +1,6 @@ "use client"; +import type { TFunction } from "i18next"; import type { LucideIcon } from "lucide-react"; import { BlocksIcon, @@ -11,59 +12,75 @@ import { import { cn } from "@mc/ui"; import { Card, CardContent, CardHeader } from "@mc/ui/card"; +import { useTranslation } from "~/i18n/client"; import { MenuButton } from "../../Menu"; // TODO actually implement the guides -const suggestedTopics: { +const suggestedTopics = ( + t: TFunction, +): { title: string; description: string; icon: LucideIcon; label?: string; disabled?: boolean; -}[] = [ +}[] => [ { icon: ZapIcon, - title: "Quick-setup", - description: - "Quickly create the most common counters in a few clicks. No imagintion or brain required!", - label: "Hasle free!", + title: t("pages.dashboard.servers.suggestedTopics.quickSetup.title"), + description: t( + "pages.dashboard.servers.suggestedTopics.quickSetup.description", + ), + label: t("pages.dashboard.servers.suggestedTopics.quickSetup.label"), }, { icon: LandPlotIcon, - title: "Create from scratch", - description: - "Learn how to create your first custom counter in a few minutes", - label: "Be unique!", + title: t("pages.dashboard.servers.suggestedTopics.createFromScratch.title"), + description: t( + "pages.dashboard.servers.suggestedTopics.createFromScratch.description", + ), + label: t("pages.dashboard.servers.suggestedTopics.createFromScratch.label"), }, { icon: BlocksIcon, - title: "Advanced counters", - description: "Get the most out of your counters, tailored to your needs", - label: "Like a pro", + title: t("pages.dashboard.servers.suggestedTopics.advancedCounters.title"), + description: t( + "pages.dashboard.servers.suggestedTopics.advancedCounters.description", + ), + label: t("pages.dashboard.servers.suggestedTopics.advancedCounters.label"), }, { icon: CandlestickChartIcon, - title: "Query historical statistics", - description: - "See how your counters and other common stats have evolved over time", - label: "Coming Soon™", + title: t( + "pages.dashboard.servers.suggestedTopics.queryHistoricalStatistics.title", + ), + description: t( + "pages.dashboard.servers.suggestedTopics.queryHistoricalStatistics.description", + ), + label: t( + "pages.dashboard.servers.suggestedTopics.queryHistoricalStatistics.label", + ), disabled: true, }, ]; export default function Page() { + const [t] = useTranslation(); + return ( -
+
-

Welcome back!

+

+ {t("pages.dashboard.servers.suggestedTopics.title")} +

- Here are some suggestions for you + {t("pages.dashboard.servers.suggestedTopics.subTitle")}

- {suggestedTopics.map((topic, i) => ( + {suggestedTopics(t).map((topic, i) => (
diff --git a/apps/website/src/app/layout.tsx b/apps/website/src/app/layout.tsx index 4f570f970..292c470b6 100644 --- a/apps/website/src/app/layout.tsx +++ b/apps/website/src/app/layout.tsx @@ -2,6 +2,7 @@ import "~/globals.css"; import { Inter } from "next/font/google"; +import { I18nProvider } from "~/i18n/client"; import { TRPCReactProvider } from "~/trpc/react"; import NavBar from "./components/NavBar"; @@ -25,10 +26,12 @@ export default function RootLayout({ className={`${inter.className} antialiasing dark flex min-h-screen flex-col`} style={{ backgroundColor: "#0c0a09" }} > - - - {children} - + + + + {children} + + ); diff --git a/apps/website/src/i18n/client.ts b/apps/website/src/i18n/client.tsx similarity index 88% rename from apps/website/src/i18n/client.ts rename to apps/website/src/i18n/client.tsx index 129d431ce..4ce30322c 100644 --- a/apps/website/src/i18n/client.ts +++ b/apps/website/src/i18n/client.tsx @@ -13,6 +13,7 @@ import LanguageDetector from "i18next-browser-languagedetector"; import resourcesToBackend from "i18next-resources-to-backend"; import { useCookies } from "react-cookie"; import { + I18nextProvider, initReactI18next, useTranslation as useTranslationOrg, } from "react-i18next"; @@ -49,6 +50,7 @@ void i18next preload: runsOnServerSide ? languages : [], }); +// TODO remove this and move this logic to the provider function useTranslation< Ns extends keyof Resources = "main", KPrefix extends KeyPrefix> = undefined, @@ -82,5 +84,9 @@ function useTranslation(ns = defaultNS, options = {}) { return isClient ? ret : retDef; } +function I18nProvider({ children }: { children: React.ReactNode }) { + const [_, i18n] = useTranslation(); + return {children}; +} -export { useTranslation }; +export { useTranslation, I18nProvider }; diff --git a/apps/website/src/i18n/locales/en-US/main.json b/apps/website/src/i18n/locales/en-US/main.json index b09b58b85..37c1994f5 100644 --- a/apps/website/src/i18n/locales/en-US/main.json +++ b/apps/website/src/i18n/locales/en-US/main.json @@ -23,6 +23,138 @@ "supportBtn": "Get support", "backBtn": "Go back" } + }, + "dashboard": { + "servers": { + "suggestedTopics": { + "quickSetup": { + "title": "Quick-setup", + "description": "Quickly create the most common counters in a few clicks. No imagination or brain required!", + "label": "Hassle free!" + }, + "createFromScratch": { + "title": "Create from scratch", + "description": "Learn how to create your first custom counter in a few minutes", + "label": "Be unique!" + }, + "advancedCounters": { + "title": "Advanced counters", + "description": "Get the most out of your counters, tailored to your needs", + "label": "Like a pro" + }, + "queryHistoricalStatistics": { + "title": "Query historical statistics", + "description": "See how your counters and other common stats have evolved over time", + "label": "Coming Soon™" + }, + "title": "Welcome back!", + "subTitle": "Here are some suggestions for you" + }, + "inviteBotPage": { + "title": "Let's set up the bot!", + "subtitle": "Add Member counter to {{serverName}} and enjoy realtime counters.", + "addToServer": "Add to {{serverName}}", + "noPermission": "You don't have enough permissions to add the bot. Please ask an administrator or someone with Manage Server permission to add this bot.", + "useOrShareLink": "Use or share this link to add the bot", + "linkCopied": "The link has been copied to your clipboard", + "copyLink": "Copy invite link" + }, + "inviteBotBanner": { + "message": "It looks like the Member Counter Bot isn't in this server. Would you like to add it?", + "closeBtn": "Close" + }, + "forbiddenPage": { + "message": "You don't have permission to manage this server.\nAsk an admin at {guildName} to give you Administrator or Manage Guild permissions.", + "title": "Permission Denied" + }, + "blockedBanner": { + "text": "This server has been blocked for violating our Terms of Service or Acceptable Use Policy.
Reason given: {{reason}}
If you think this is a mistake, please contact our support team.", + "termsOfService": "Terms of Service", + "acceptableUsePolicy": "Acceptable Use Policy", + "noReasonGiven": "No reason given", + "supportTeam": "support team" + } + } + }, + "account": { + "deleteButton": { + "deleteAccountBtn": "Delete account", + "confirmTitle": "Are you absolutely sure?", + "confirmDescription": "This action cannot be undone. This will permanently delete your account and remove your data from our servers.", + "closeBtn": "Close" + }, + "userBadges": { + "donor": "You donated to support the development and maintenance of Member Counter", + "premium": "You are a premium user", + "betaTester": "You participated in a beta program", + "translator": "You helped to translate the bot", + "contributor": "You implemented a feature or fixed a bug", + "bigBrain": "You suggested an idea and it was implemented", + "bugCatcher": "You found and reported a bug", + "patPat": "You found a secret", + "foldingAtHome": "You contributed a WU in folding@home" + }, + "page": { + "avatarAlt": "{{username}}'s avatar", + "logoutButton": "Logout" + } + }, + "admin": { + "guilds": { + "loadGuildInput": { + "placeholder": "Paste server ID", + "loadButton": "Load server" + } + }, + "users": { + "delete": { + "button": "Delete account", + "dialogTitle": "Are you absolutely sure?", + "dialogDescription": "This action cannot be undone.", + "closeButton": "Close" + }, + "manage": { + "permissions": { + "title": "Permissions", + "seeUsers": "See Users", + "manageUsers": "Manage Users", + "seeGuilds": "See Servers", + "manageGuilds": "Manage Servers" + }, + "badges": { + "title": "Badges", + "donor": "Donor", + "premium": "Premium", + "betaTester": "Beta Tester", + "translator": "Translator", + "contributor": "Contributor", + "bigBrain": "Big Brain", + "bugCatcher": "Bug Catcher", + "patPat": "Pat Pat", + "foldingAtHome": "Folding@Home" + }, + "transferAccount": "Transfer account", + "pasteUserId": "Paste user ID", + "saved": "Saved", + "save": "Save" + }, + "loadUser": "Load user", + "recentUsers": "Recent users", + "userNotRegistered": "This user isn't registered." + } + } + }, + "common": { + "unknownServer": "Unknown server", + "unknownUser": "Unknown user {{id}}", + "channelLabels": { + "textChannel": "Text channel", + "category": "Category", + "voiceChannel": "Voice channel", + "announcementChannel": "Announcement channel", + "stageChannel": "Stage channel", + "forumChannel": "Forum channel", + "mediaChannel": "Media channel" } } }