From b151b30856ce5ff7e6e2d7252c62161117200583 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:54:32 +0200 Subject: [PATCH] Add user seasons page i18n --- .../routes/u.$identifier.seasons.tsx | 86 ++++++++++++------- .../user-page/routes/u.$identifier.tsx | 20 +++-- public/locales/en/user.json | 21 ++++- 3 files changed, 85 insertions(+), 42 deletions(-) diff --git a/app/features/user-page/routes/u.$identifier.seasons.tsx b/app/features/user-page/routes/u.$identifier.seasons.tsx index dad65bc88..8f4484867 100644 --- a/app/features/user-page/routes/u.$identifier.seasons.tsx +++ b/app/features/user-page/routes/u.$identifier.seasons.tsx @@ -56,11 +56,15 @@ import { modesShort } from "~/modules/in-game-lists/modes"; import { atOrError } from "~/utils/arrays"; import { databaseTimestampToDate } from "~/utils/dates"; import { cutToNDecimalPlaces } from "~/utils/number"; -import { notFoundIfFalsy } from "~/utils/remix"; +import { type SendouRouteHandle, notFoundIfFalsy } from "~/utils/remix"; import { sendouQMatchPage, userSeasonsPage } from "~/utils/urls"; import { userParamsSchema, type UserPageLoaderData } from "./u.$identifier"; import * as UserRepository from "~/features/user-page/UserRepository.server"; +export const handle: SendouRouteHandle = { + i18n: ["user"], +}; + export const seasonsSearchParamsSchema = z.object({ page: z.coerce.number().default(1), info: z.enum(["weapons", "stages", "mates", "enemies"]).default("weapons"), @@ -129,6 +133,7 @@ export const loader = async ({ params, request }: LoaderArgs) => { const DAYS_WITH_SKILL_NEEDED_TO_SHOW_POWER_CHART = 2; export default function UserSeasonsPage() { + const { t } = useTranslation(["user"]); const data = useLoaderData(); const tabLink = (tab: string) => @@ -139,7 +144,7 @@ export default function UserSeasonsPage() {
- This user has not played SendouQ or ranked tournaments this season + {t("user:seasons.noQ")}
); @@ -167,7 +172,7 @@ export default function UserSeasonsPage() { controlled active={data.info.currentTab === "weapons"} > - Weapons + {t("user:seasons.tabs.weapons")} - Stages + {t("user:seasons.tabs.stages")} - Teammates + {t("user:seasons.tabs.teammates")} - Opponents + {t("user:seasons.tabs.opponents")}
@@ -206,6 +211,7 @@ export default function UserSeasonsPage() { } function SeasonHeader() { + const { t, i18n } = useTranslation(["user"]); const data = useLoaderData(); const isMounted = useIsMounted(); const { starts, ends } = seasonObject(data.season); @@ -228,7 +234,9 @@ function SeasonHeader() { "text-main-forced": isActive, })} > - {isActive ? "Season " : "S"} + {isActive + ? `${t("user:seasons.season")} ` + : t("user:seasons.season.short")} {s} ); @@ -237,13 +245,13 @@ function SeasonHeader() {
{isMounted ? ( <> - {new Date(starts).toLocaleString("en", { + {new Date(starts).toLocaleString(i18n.language, { day: "numeric", month: "long", year: isDifferentYears ? "numeric" : undefined, })}{" "} -{" "} - {new Date(ends).toLocaleString("en", { + {new Date(ends).toLocaleString(i18n.language, { day: "numeric", month: "long", year: "numeric", @@ -258,6 +266,7 @@ function SeasonHeader() { } function Winrates() { + const { t } = useTranslation(["user"]); const data = useLoaderData(); const winrate = (wins: number, losses: number) => @@ -267,12 +276,16 @@ function Winrates() {
Sets{" "} - {data.winrates.sets.wins}W {data.winrates.sets.losses}L ( + {data.winrates.sets.wins} + {t("user:seasons.win.short")} {data.winrates.sets.losses} + {t("user:seasons.loss.short")} ( {winrate(data.winrates.sets.wins, data.winrates.sets.losses)}%)
Maps{" "} - {data.winrates.maps.wins}W {data.winrates.maps.losses}L ( + {data.winrates.maps.wins} + {t("user:seasons.win.short")} {data.winrates.maps.losses} + {t("user:seasons.loss.short")} ( {winrate(data.winrates.maps.wins, data.winrates.maps.losses)}%)
@@ -280,6 +293,7 @@ function Winrates() { } function Rank({ currentOrdinal }: { currentOrdinal: number }) { + const { t } = useTranslation(["user"]); const data = useLoaderData(); const [, parentRoute] = useMatches(); invariant(parentRoute); @@ -304,22 +318,19 @@ function Rank({ currentOrdinal }: { currentOrdinal: number }) {
{!data.isAccurateTiers ? (
- Tentative + {t("user:seasons.tentative")} ?} contentClassName="u__season__tentative__explanation" > - Leaderboard has low amount of entries. Once enough players have - finished their calculations the ranking tiers will recalculate. - For most players it will mean that their tier goes down. SP always - stays the same. + {t("user:seasons.tentative.explanation")}
) : null}
{ordinalToSp(currentOrdinal)}SP
{!peakAndCurrentSame ? (
- Peak {ordinalToSp(maxOrdinal)}SP + {t("user:seasons.peak")} {ordinalToSp(maxOrdinal)}SP
) : null} {topTenPlacement ? ( @@ -365,7 +376,7 @@ function Weapons({ }: { weapons: NonNullable["info"]["weapons"]>; }) { - const { t } = useTranslation(["weapons"]); + const { t } = useTranslation(["user", "weapons"]); const slicedWeapons = weapons.slice(0, WEAPONS_TO_SHOW); @@ -383,7 +394,7 @@ function Weapons({
{weapons.length === 0 ? (
- No reported weapons yet + {t("user:seasons.noReportedWeapons")}
) : null} {slicedWeapons.map(({ count, weaponSplId }) => ( @@ -417,7 +428,7 @@ function Stages({ stages: NonNullable["info"]["stages"]>; }) { const data = useLoaderData(); - const { t } = useTranslation(["game-misc"]); + const { t } = useTranslation(["user", "game-misc"]); const parentPageData = atOrError(useMatches(), -2).data as UserPageLoaderData; return ( @@ -445,7 +456,9 @@ function Stages({ {stats ? (
- {stats.wins}W {stats.losses}L + {stats.wins} + {t("user:seasons.win.short")} {stats.losses} + {t("user:seasons.loss.short")}
) : null}
@@ -464,7 +477,7 @@ function Stages({ ); })}
- Click a row to show weapon usage stats + {t("user:seasons.clickARow")}
); @@ -476,14 +489,14 @@ function StageWeaponUsageStats(props: { modeShort: ModeShort; stageId: StageId; }) { - const { t } = useTranslation(["game-misc"]); + const { t } = useTranslation(["user", "game-misc"]); const [tab, setTab] = React.useState<"SELF" | "MATE" | "ENEMY">("SELF"); const { weaponUsage, isLoading } = useWeaponUsage(props); if (isLoading) { return (
- Loading... + {t("user:seasons.loading")}
); } @@ -493,7 +506,7 @@ function StageWeaponUsageStats(props: { if (usages.length === 0) { return (
- No reported weapons yet + {t("user:seasons.noReportedWeapons")}
); } @@ -506,13 +519,13 @@ function StageWeaponUsageStats(props: { setTab("SELF")}> - Self + {t("user:seasons.tabs.self")} setTab("MATE")}> - Teammates + {t("user:seasons.tabs.teammates")} setTab("ENEMY")}> - Opponents + {t("user:seasons.tabs.opponents")}
@@ -537,8 +550,12 @@ function StageWeaponUsageStats(props: { > {winrate}%
-
{u.wins} W
-
{u.losses} L
+
+ {u.wins} {t("user:seasons.win.short")} +
+
+ {u.losses} {t("user:seasons.loss.short")} +
); })} @@ -552,6 +569,7 @@ function Players({ }: { players: NonNullable["info"]["players"]>; }) { + const { t } = useTranslation(["user"]); const data = useLoaderData(); return ( @@ -581,10 +599,11 @@ function Players({ {setWinRate}% ({mapWinRate}%)
- {player.setWins} ({player.mapWins}) W + {player.setWins} ({player.mapWins}) {t("user:seasons.win.short")}
- {player.setLosses} ({player.mapLosses}) L + {player.setLosses} ({player.mapLosses}){" "} + {t("user:seasons.loss.short")}
); @@ -689,6 +708,7 @@ function Match({ }: { match: SerializeFrom["matches"]["value"][0]; }) { + const { t } = useTranslation(["user"]); const [, parentRoute] = useMatches(); invariant(parentRoute); const userPageData = parentRoute.data as UserPageLoaderData; @@ -750,7 +770,7 @@ function Match({ {!match.isLocked ? (
- This match has not been processed yet + {t("user:seasons.matchBeingProcessed")}
) : null} diff --git a/app/features/user-page/routes/u.$identifier.tsx b/app/features/user-page/routes/u.$identifier.tsx index 5cfa5298a..abf4fc473 100644 --- a/app/features/user-page/routes/u.$identifier.tsx +++ b/app/features/user-page/routes/u.$identifier.tsx @@ -108,7 +108,7 @@ export const loader = async ({ params, request }: LoaderArgs) => { export default function UserPageLayout() { const data = useLoaderData(); const user = useUser(); - const { t } = useTranslation(); + const { t } = useTranslation(["common", "user"]); const isOwnPage = data.id === user?.id; @@ -117,16 +117,20 @@ export default function UserPageLayout() { return (
- {t("header.profile")} - Seasons + + {t("common:header.profile")} + + + {t("user:seasons")} + {isOwnPage && ( - {t("actions.edit")} + {t("common:actions.edit")} )} {data.results.length > 0 && ( - {t("results")} ({data.results.length}) + {t("common:results")} ({data.results.length}) )} {(isOwnPage || data.buildsCount > 0) && ( @@ -135,17 +139,17 @@ export default function UserPageLayout() { prefetch="intent" data-testid="builds-tab" > - {t("pages.builds")} ({data.buildsCount}) + {t("common:pages.builds")} ({data.buildsCount}) )} {data.vods.length > 0 && ( - {t("pages.vods")} ({data.vods.length}) + {t("common:pages.vods")} ({data.vods.length}) )} {(isOwnPage || data.artCount > 0) && ( - {t("pages.art")} ({data.artCount}) + {t("common:pages.art")} ({data.artCount}) )} diff --git a/public/locales/en/user.json b/public/locales/en/user.json index 4f8a40972..714e8f46a 100644 --- a/public/locales/en/user.json +++ b/public/locales/en/user.json @@ -39,5 +39,24 @@ "forms.info.favoriteBadge": "Your favorite badge is shown as big by default on your profile.", "search.info": "Search for users by Discord, Splatoon 3 or Twitter name", - "search.noResults": "No users found matching '{{query}}'" + "search.noResults": "No users found matching '{{query}}'", + + "seasons": "Seasons", + "seasons.noQ": "This user has not played SendouQ or ranked tournaments this season", + "seasons.tabs.weapons": "Weapons", + "seasons.tabs.stages": "Stages", + "seasons.tabs.self": "Self", + "seasons.tabs.teammates": "Teammates", + "seasons.tabs.opponents": "Opponents", + "seasons.season": "Season", + "seasons.season.short": "S", + "seasons.win.short": "W", + "seasons.loss.short": "L", + "seasons.tentative": "Tentative", + "seasons.tentative.explanation": "Leaderboard has a low amount of entries. Once enough players have finished their calculations the ranking tiers will recalculate. For most players it will mean that their tier goes down. SP always stays the same.", + "seasons.peak": "Peak", + "seasons.noReportedWeapons": "No reported weapons yet", + "seasons.clickARow": "Click a row to see weapon usage stats", + "seasons.loading": "Loading...", + "seasons.matchBeingProcessed": "This match has not been processed yet" }