New footer (#3374)

This commit is contained in:
Kalle
2026-08-29 15:00:03 +03:00
committed by GitHub
parent 980d5c43c0
commit 7ecf963a3b
23 changed files with 777 additions and 162 deletions

View File

@@ -1,133 +1,235 @@
.footer {
container-type: inline-size;
display: flex;
flex-direction: column;
padding: var(--s-3);
align-items: center;
padding: var(--s-8) 0 var(--s-32);
background-color: var(--color-bg-high);
gap: var(--s-6);
margin-block-start: auto;
padding-block-end: var(--s-32);
}
.linkList {
.thanks {
display: flex;
justify-content: space-evenly;
font-size: var(--font-2xs);
& > * {
flex: 1;
text-align: center;
}
}
.socials {
display: flex;
justify-content: center;
gap: var(--s-2);
}
.socialIcon {
height: 2.25rem;
transition: transform 0.25s ease-in-out;
}
.socialLink {
display: flex;
max-width: 10rem;
height: 12rem;
flex: 1 1 0;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: var(--s-4);
border-radius: var(--radius-box);
background-color: var(--color-bg-higher);
cursor: pointer;
font-size: var(--font-lg);
&:hover > .socialIcon {
transform: translateY(-0.3rem);
}
}
.socialHeader {
text-align: center;
& > p {
font-size: var(--font-2xs);
}
}
.patronTitle {
display: flex;
align-items: flex-end;
justify-content: center;
font-size: var(--font-sm);
font-weight: var(--weight-semi);
gap: var(--s-2);
}
.patronList {
display: flex;
max-width: 75vw;
flex-wrap: wrap;
justify-content: center;
padding: 0;
margin: 0 auto;
font-size: var(--font-xs);
gap: var(--s-1);
list-style: none;
margin-block-start: var(--s-2);
text-align: center;
margin-block-start: var(--s-6);
}
.patron {
.thanksTitle {
font-size: var(--font-md);
font-weight: var(--weight-bold);
}
.thanksSubtitle {
font-size: var(--font-xs);
color: var(--color-text-high);
}
.marquee {
display: flex;
width: 100%;
flex-direction: column;
overflow: hidden;
gap: var(--s-2);
mask-image: linear-gradient(
to right,
transparent,
black 8%,
black 92%,
transparent
);
@media (prefers-reduced-motion: reduce) {
padding-inline: var(--s-4);
mask-image: none;
}
}
.marqueeRow {
display: flex;
gap: var(--s-2);
@media (prefers-reduced-motion: reduce) {
flex-wrap: wrap;
justify-content: center;
}
}
.marqueeGroup {
display: flex;
flex-shrink: 0;
align-items: center;
padding: 0;
margin: 0;
list-style: none;
gap: var(--s-2);
animation: scroll 600s linear infinite;
@media (prefers-reduced-motion: reduce) {
width: 100%;
flex-wrap: wrap;
justify-content: center;
animation: none;
&[aria-hidden="true"] {
display: none;
}
}
.marqueeRow:nth-child(2) > & {
animation-duration: 740s;
animation-direction: reverse;
}
.marqueeRow:nth-child(3) > & {
animation-duration: 670s;
}
.marquee:hover & {
animation-play-state: paused;
}
}
@keyframes scroll {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-100% - var(--s-2)));
}
}
.chip {
display: block;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
font-size: var(--font-xs);
padding: var(--s-1) var(--s-2-5);
border-radius: var(--radius-full);
background-color: var(--color-bg-higher);
color: var(--color-text-accent);
}
.copyrightNote {
.cta {
margin-block: calc(var(--s-2) * -1) var(--s-6);
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 48rem;
gap: var(--s-2);
padding-inline: var(--s-4);
@container (max-width: 640px) {
display: grid;
align-self: stretch;
max-width: none;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@container (max-width: 420px) {
grid-template-columns: 1fr;
}
}
.card {
display: flex;
align-items: center;
min-width: 10.5rem;
gap: var(--s-2);
padding: var(--s-2) var(--s-4);
border-radius: var(--radius-box);
background-color: var(--color-bg-higher);
font-size: var(--font-sm);
font-weight: var(--weight-semi);
@container (max-width: 640px) {
min-width: 0;
}
}
.cardIcon {
height: 1.5rem;
width: 1.5rem;
flex-shrink: 0;
}
.cardHeader {
display: flex;
flex-direction: column;
& > span {
font-size: var(--font-2xs);
font-weight: var(--weight-body);
color: var(--color-text-high);
}
}
.privacy {
font-size: var(--font-2xs);
}
.legal {
display: flex;
flex-direction: column;
color: var(--color-text-high);
font-size: var(--font-2xs);
text-align: center;
padding-inline: var(--s-4);
}
.sourceLink {
margin-inline: auto;
.versionLink {
font-size: var(--font-2xs);
color: var(--color-text-high);
font-style: italic;
color: var(--color-text-high);
}
@media screen and (max-width: 640px) {
.socials {
flex-direction: column;
.sitemap {
display: grid;
width: 100%;
max-width: 44rem;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--s-6);
padding-inline: var(--s-4);
@container (max-width: 640px) {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.socialLink {
max-width: initial;
flex-direction: row;
&:hover > .socialIcon {
transform: translateX(-0.3rem);
}
}
.socialHeader {
display: flex;
align-items: center;
gap: var(--s-2);
text-align: initial;
& > p {
margin-block-start: var(--s-1);
}
}
.socialIcon {
height: 1.75rem;
@container (max-width: 420px) {
grid-template-columns: 1fr;
}
}
.sitemapColumn {
display: flex;
flex-direction: column;
gap: var(--s-2);
}
.sitemapTitle {
font-size: var(--font-2xs);
font-weight: var(--weight-bold);
color: var(--color-text-high);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sitemapLink {
display: flex;
align-items: center;
gap: var(--s-2);
font-size: var(--font-xs);
}
.sitemapIcon {
flex-shrink: 0;
}

View File

@@ -1,78 +1,241 @@
import { Braces, CircleHelp, Hand, Heart, HeartHandshake } from "lucide-react";
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import * as R from "remeda";
import { Config } from "~/config";
import { useUser } from "~/features/auth/core/user";
import { leaderboardsPage } from "~/features/leaderboards/leaderboards-urls";
import { usePatrons } from "~/hooks/swr";
import { useHasRole } from "~/modules/permissions/hooks";
import { GIT_COMMIT } from "~/utils/git-commit";
import {
ANALYZER_URL,
API_PAGE,
ART_PAGE,
ARTICLES_MAIN_PAGE,
ASSOCIATIONS_PAGE,
BUILDS_PAGE,
CALENDAR_PAGE,
COMP_ANALYZER_URL,
CONTRIBUTIONS_PAGE,
EVENTS_PAGE,
FAQ_PAGE,
LFG_PAGE,
LINKS_PAGE,
LUTI_PAGE,
MAPS_URL,
NINTENDO_COMMUNITY_TOURNAMENTS_GUIDELINES_URL,
navIconUrl,
OBJECT_DAMAGE_CALCULATOR_URL,
PLANNER_URL,
PLUS_SUGGESTIONS_PAGE,
PLUS_VOTING_RESULTS_PAGE,
SENDOU_INK_DISCORD_URL,
SENDOU_INK_GITHUB_URL,
SENDOU_LOVE_EMOJI_PATH,
SENDOUQ_PAGE,
SENDOUQ_RULES_PAGE,
SETTINGS_PAGE,
SUPPORT_PAGE,
scrimsPage,
TIER_LIST_MAKER_URL,
TIERS_PAGE,
TROPHIES_PAGE,
userPage,
VODS_PAGE,
WELCOME_PAGE,
weaponParamsPage,
XSEARCH_PAGE,
} from "~/utils/urls";
import { LinkButton } from "../elements/Button";
import { Image } from "../Image";
import { DiscordIcon } from "../icons/Discord";
import { GitHubIcon } from "../icons/GitHub";
import { PatreonIcon } from "../icons/Patreon";
import styles from "./Footer.module.css";
export function Footer() {
const { t } = useTranslation();
const user = useUser();
const MARQUEE_ROWS_COUNT = 3;
const showPrivacySettings =
Config.fuseEnabled && !user?.roles.includes("MINOR_SUPPORT");
const SITEMAP_COLUMNS = [
{
title: "nav.play",
items: [
{ name: "sendouq", url: SENDOUQ_PAGE },
{ name: "sendouq", url: SENDOUQ_RULES_PAGE, label: "footer.link.qRules" },
{ name: "sendouq", url: TIERS_PAGE, label: "footer.link.tiers" },
{ name: "scrims", url: scrimsPage() },
{ name: "lfg", url: LFG_PAGE },
{ name: "calendar", url: CALENDAR_PAGE },
{ name: "calendar", url: EVENTS_PAGE, label: "footer.link.events" },
{
name: "leaderboards",
url: leaderboardsPage({ type: "USER" }),
label: "footer.link.userLeaderboard",
},
{
name: "leaderboards",
url: leaderboardsPage({ type: "TEAM" }),
label: "footer.link.teamLeaderboard",
},
{
name: "leaderboards",
url: leaderboardsPage({ type: "XP-ALL" }),
label: "footer.link.xpLeaderboard",
},
...(Config.showLutiNavItem
? [{ name: "luti", url: LUTI_PAGE } as const]
: []),
],
},
{
title: "nav.tools",
items: [
{ name: "analyzer", url: ANALYZER_URL },
{
name: "analyzer",
url: weaponParamsPage("splattershot"),
label: "footer.link.params",
},
{ name: "comp-analyzer", url: COMP_ANALYZER_URL },
{ name: "object-damage-calculator", url: OBJECT_DAMAGE_CALCULATOR_URL },
{ name: "plans", url: PLANNER_URL },
{ name: "maps", url: MAPS_URL },
{ name: "tier-list-maker", url: TIER_LIST_MAKER_URL },
{ name: "xsearch", url: XSEARCH_PAGE },
{ name: "settings", url: SETTINGS_PAGE },
],
},
{
title: "nav.community",
items: [
{ name: "builds", url: BUILDS_PAGE },
{
name: "associations",
url: ASSOCIATIONS_PAGE,
label: "footer.link.associations",
},
{ name: "art", url: ART_PAGE },
{ name: "articles", url: ARTICLES_MAIN_PAGE },
{ name: "vods", url: VODS_PAGE },
{ name: "trophies", url: TROPHIES_PAGE },
{ name: "links", url: LINKS_PAGE },
{
name: "plus",
url: PLUS_SUGGESTIONS_PAGE,
label: "footer.link.plusSuggestions",
},
{
name: "plus",
url: PLUS_VOTING_RESULTS_PAGE,
label: "footer.link.plusVotingResults",
},
],
},
] as const;
export function Footer() {
const { t } = useTranslation(["common", "front"]);
const isPatron = useHasRole("MINOR_SUPPORT");
const showPrivacySettings = Config.fuseEnabled && !isPatron;
const currentYear = new Date().getFullYear();
return (
<footer className={styles.footer}>
<div className={styles.linkList}>
<Link to={CONTRIBUTIONS_PAGE}>{t("pages.contributors")}</Link>
<Link to={FAQ_PAGE}>{t("pages.faq")}</Link>
<Link to={WELCOME_PAGE}>{t("pages.welcome")}</Link>
<Link to={API_PAGE}>{t("pages.api")}</Link>
{showPrivacySettings ? <div data-fuse-privacy-tool /> : null}
</div>
<div className={styles.socials}>
<a
className={styles.socialLink}
<div className={styles.cards}>
<FooterCard
icon={<GitHubIcon className={styles.cardIcon} />}
title="GitHub"
subtitle={t("common:footer.github.subtitle")}
href={SENDOU_INK_GITHUB_URL}
target="_blank"
rel="noreferrer"
>
<div className={styles.socialHeader}>
GitHub<p>{t("footer.github.subtitle")}</p>
</div>
<GitHubIcon className={styles.socialIcon} />
</a>
<a
className={styles.socialLink}
/>
<FooterCard
icon={<DiscordIcon className={styles.cardIcon} />}
title="Discord"
subtitle={t("common:footer.discord.subtitle")}
href={SENDOU_INK_DISCORD_URL}
target="_blank"
rel="noreferrer"
>
<div className={styles.socialHeader}>
Discord<p>{t("footer.discord.subtitle")}</p>
</div>{" "}
<DiscordIcon className={styles.socialIcon} />
</a>
<Link className={styles.socialLink} to={SUPPORT_PAGE}>
<div className={styles.socialHeader}>
Patreon<p>{t("footer.patreon.subtitle")}</p>
</div>{" "}
<PatreonIcon className={styles.socialIcon} />
</Link>
/>
<FooterCard
icon={<PatreonIcon className={styles.cardIcon} />}
title="Patreon"
subtitle={t("common:footer.patreon.subtitle")}
to={SUPPORT_PAGE}
/>
<FooterCard
icon={<HeartHandshake className={styles.cardIcon} />}
title={t("common:pages.contributors")}
subtitle={t("common:footer.contributors.subtitle")}
to={CONTRIBUTIONS_PAGE}
/>
<FooterCard
icon={<CircleHelp className={styles.cardIcon} />}
title={t("common:pages.faq")}
subtitle={t("common:footer.faq.subtitle")}
to={FAQ_PAGE}
/>
<FooterCard
icon={<Hand className={styles.cardIcon} />}
title={t("common:pages.welcome")}
subtitle={t("common:footer.welcome.subtitle")}
to={WELCOME_PAGE}
/>
<FooterCard
icon={<Braces className={styles.cardIcon} />}
title={t("common:pages.api")}
subtitle={t("common:footer.api.subtitle")}
to={API_PAGE}
/>
</div>
<PatronsList />
<div className={styles.copyrightNote}>
<div className={styles.thanks}>
<Image alt="" path={SENDOU_LOVE_EMOJI_PATH} width={40} height={40} />
<h4 className={styles.thanksTitle}>{t("common:footer.thanks")}</h4>
<p className={styles.thanksSubtitle}>
{t("common:footer.thanks.subtitle")}
</p>
</div>
<PatronMarquee />
{isPatron ? null : (
<LinkButton
to={SUPPORT_PAGE}
size="small"
variant="primary"
icon={<Heart fill="currentColor" />}
className={styles.cta}
>
{t("common:footer.support")}
</LinkButton>
)}
<div className={styles.sitemap}>
{SITEMAP_COLUMNS.map((column) => (
<nav
key={column.title}
className={styles.sitemapColumn}
aria-label={t(`front:${column.title}`)}
>
<h5 className={styles.sitemapTitle}>
{t(`front:${column.title}`)}
</h5>
{column.items.map((item) => (
<Link key={item.url} to={item.url} className={styles.sitemapLink}>
<Image
path={navIconUrl(item.name)}
alt=""
size={18}
containerClassName={styles.sitemapIcon}
/>
{"label" in item
? t(`common:${item.label}`)
: t(`common:pages.${item.name}`)}
</Link>
))}
</nav>
))}
</div>
{showPrivacySettings ? (
<div className={styles.privacy} data-fuse-privacy-tool />
) : null}
<div className={styles.legal}>
<p>
sendou.ink © Copyright of Sendou and contributors 2019-{currentYear}.
Original content & source code is licensed under the AGPL-3.0 license.
@@ -97,37 +260,111 @@ export function Footer() {
</div>
{GIT_COMMIT ? (
<a
className={styles.sourceLink}
className={styles.versionLink}
href={`${SENDOU_INK_GITHUB_URL}/commits/${GIT_COMMIT}/`}
target="_blank"
rel="noreferrer"
>
{t("footer.version")} {GIT_COMMIT.slice(0, 10)}
{t("common:footer.version")} {GIT_COMMIT.slice(0, 10)}
</a>
) : null}
</footer>
);
}
function PatronsList() {
const { t } = useTranslation();
type Patron = NonNullable<ReturnType<typeof usePatrons>["patrons"]>[number];
function PatronMarquee() {
const { patrons } = usePatrons();
if (!patrons || patrons.length === 0) return null;
const rows = R.chunk(patrons, Math.ceil(patrons.length / MARQUEE_ROWS_COUNT));
return (
<div>
<h4 className={styles.patronTitle}>
{t("footer.thanks")}
<Image alt="" path={SENDOU_LOVE_EMOJI_PATH} width={24} height={24} />
</h4>
<ul className={styles.patronList}>
{patrons?.map((patron) => (
<li key={patron.id}>
<Link to={userPage(patron)} className={styles.patron}>
{patron.username}
</Link>
</li>
))}
</ul>
<div className={styles.marquee}>
{rows.map((row) => (
<div key={row[0].id} className={styles.marqueeRow}>
<PatronChips patrons={row} />
<PatronChips patrons={row} ariaHidden />
</div>
))}
</div>
);
}
function PatronChips({
patrons,
ariaHidden = false,
}: {
patrons: Array<Patron>;
ariaHidden?: boolean;
}) {
return (
<ul className={styles.marqueeGroup} aria-hidden={ariaHidden}>
{patrons.map((patron) => (
<li key={patron.id}>
<Link
to={userPage(patron)}
className={styles.chip}
data-custom-theme={patron.customTheme ? true : undefined}
style={customThemeChipStyle(patron.customTheme)}
tabIndex={ariaHidden ? -1 : undefined}
>
{patron.username}
</Link>
</li>
))}
</ul>
);
}
function FooterCard({
icon,
title,
subtitle,
href,
to,
}: {
icon: ReactNode;
title: string;
subtitle: string;
href?: string;
to?: string;
}) {
const content = (
<>
{icon}
<span className={styles.cardHeader}>
{title}
<span>{subtitle}</span>
</span>
</>
);
if (href) {
return (
<a className={styles.card} href={href} target="_blank" rel="noreferrer">
{content}
</a>
);
}
return (
<Link className={styles.card} to={to ?? "/"}>
{content}
</Link>
);
}
function customThemeChipStyle(
customTheme: Patron["customTheme"],
): React.CSSProperties {
if (!customTheme) return {};
return R.pickBy(
customTheme,
(value, key) =>
value !== null && !key.includes("--_size") && !key.includes("--_border"),
) as React.CSSProperties;
}

View File

@@ -1,9 +1,10 @@
import { LEADERBOARDS_PAGE } from "~/utils/urls";
import type { LEADERBOARD_TYPES } from "./leaderboards-constants";
import { leaderboardsSearchParams } from "./leaderboards-search-params";
export const leaderboardsPage = (args: {
season?: number;
type?: "USER" | "TEAM";
type?: (typeof LEADERBOARD_TYPES)[number];
}) =>
leaderboardsSearchParams.href(LEADERBOARDS_PAGE, {
season: args.season ?? null,

View File

@@ -499,7 +499,15 @@ export function findModInfoById(id: number) {
export function findAllPatrons() {
return db
.selectFrom("User")
.select(["User.id", "User.discordId", "User.username", "User.patronTier"])
.select([
"User.id",
"User.discordId",
"User.username",
"User.patronTier",
asJson(
sql<CustomTheme | null>`IIF(COALESCE("User"."patronTier", 0) >= 2, "User"."customTheme", null)`,
).as("customTheme"),
])
.where("User.patronTier", "is not", null)
.orderBy("User.patronTier", "desc")
.orderBy("User.patronStartedAt", "asc")

View File

@@ -125,6 +125,9 @@ export const COMP_ANALYZER_URL = "/comp-analyzer";
export const OBJECT_DAMAGE_CALCULATOR_URL = "/object-damage-calculator";
export const SCANNER_PAGE = "/scanner";
export const VODS_PAGE = "/vods";
export const ART_PAGE = "/art";
export const XSEARCH_PAGE = "/xsearch";
export const ASSOCIATIONS_PAGE = "/associations";
export const LEADERBOARDS_PAGE = "/leaderboards";
export const LINKS_PAGE = "/links";
export const SENDOUQ_PAGE = "/q";
@@ -144,6 +147,8 @@ export const FRIENDS_PAGE = "/friends";
export const SETTINGS_PAGE = "/settings";
export const LUTI_PAGE = "/luti";
export const PLUS_VOTING_PAGE = "/plus/voting";
export const PLUS_VOTING_RESULTS_PAGE = "/plus/voting/results";
export const PLUS_SUGGESTIONS_PAGE = "/plus/suggestions";
const STATIC_ASSETS_URL = Config.staticAssetsUrl;

View File

@@ -0,0 +1,4 @@
---
type: feature
---
The footer got a redesign

View File

@@ -28,7 +28,9 @@ export class ScrimsPage {
constructor(page: Page) {
this.page = page;
this.locators = {
associationsLink: page.getByRole("link", { name: "Associations" }),
associationsLink: page
.getByRole("main")
.getByRole("link", { name: "Associations" }),
requestButtons: page.getByTestId("request-scrim-button"),
viewRequestButtons: page.getByTestId("view-request-button"),
acceptRequestButtons: page.getByTestId("confirm-modal-trigger-button"),

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Støt hjemmesiden",
"footer.thanks": "Tak til vores patrons for støtten",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Gem",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Support",
"footer.thanks": "Danke an die Patrons für den Support",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Speichern",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Support",
"footer.thanks": "Thanks to the patrons for the support",
"footer.version": "Version",
"footer.contributors.subtitle": "The people behind the site",
"footer.faq.subtitle": "Common questions",
"footer.welcome.subtitle": "New here? Get started",
"footer.api.subtitle": "Programmatic access",
"footer.thanks.subtitle": "sendou.ink is made possible by the following people",
"footer.support": "Support sendou.ink",
"footer.link.qRules": "SendouQ rules",
"footer.link.tiers": "Ranking tiers",
"footer.link.events": "Events",
"footer.link.userLeaderboard": "SQ player rankings",
"footer.link.teamLeaderboard": "SQ team rankings",
"footer.link.xpLeaderboard": "X Battle rankings",
"footer.link.params": "Weapon parameters",
"footer.link.associations": "Associations",
"footer.link.plusSuggestions": "Plus suggestions",
"footer.link.plusVotingResults": "Plus voting results",
"actions.share": "Share",
"actions.save": "Save",
"actions.unsave": "Unsave",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Apoyar",
"footer.thanks": "Gracias a los mecenas por su apoyo",
"footer.version": "Versión",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "Compartir",
"actions.save": "Guardar",
"actions.unsave": "No guardar",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Apoyar",
"footer.thanks": "Gracias a los Patrons por su apoyo",
"footer.version": "Versión",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "Compartir",
"actions.save": "Guardar",
"actions.unsave": "No guardar",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Soutien",
"footer.thanks": "Merci à nos Supporters pour le soutien",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Sauvegarder",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Soutien",
"footer.thanks": "Merci à nos Supporters pour le soutien",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Sauvegarder",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "תמיכה",
"footer.thanks": "תודה לפטרונים על התמיכה",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "שמירה",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Supporto",
"footer.thanks": "Mille grazie ai supporter su Patreon!",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Salva",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "寄付",
"footer.thanks": "寄付のご協力ありがとうございます",
"footer.version": "バージョン",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "共有",
"actions.save": "保存",
"actions.unsave": "保存解除",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "후원",
"footer.thanks": "Patreon 후원자들에게 갑사드립니다",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "저장하기",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Steun sendou.ink",
"footer.thanks": "Met dank aan alle patrons voor de steun",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Opslaan",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Wsparcie",
"footer.thanks": "Dziękujemy patronom za wsparcie",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Zapisz",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Suporte",
"footer.thanks": "Obrigado aos patronos e patronesses pelo suporte",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Salvar",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "Поддержать",
"footer.thanks": "Спасибо меценатам за поддержку",
"footer.version": "",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "",
"actions.save": "Сохранить",
"actions.unsave": "",

View File

@@ -123,6 +123,22 @@
"footer.patreon.subtitle": "赞助",
"footer.thanks": "感谢 Patreon 上的赞助者",
"footer.version": "版本",
"footer.contributors.subtitle": "",
"footer.faq.subtitle": "",
"footer.welcome.subtitle": "",
"footer.api.subtitle": "",
"footer.thanks.subtitle": "",
"footer.support": "",
"footer.link.qRules": "",
"footer.link.tiers": "",
"footer.link.events": "",
"footer.link.userLeaderboard": "",
"footer.link.teamLeaderboard": "",
"footer.link.xpLeaderboard": "",
"footer.link.params": "",
"footer.link.associations": "",
"footer.link.plusSuggestions": "",
"footer.link.plusVotingResults": "",
"actions.share": "分享",
"actions.save": "保存",
"actions.unsave": "不保存",