badges improvements
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
// Dedupe before adding the unique index: keep the best row per
|
||||||
|
// (player, badge) — earned first, then highest progress, then newest.
|
||||||
|
app.db().newQuery(`
|
||||||
|
DELETE FROM badge_progress
|
||||||
|
WHERE id NOT IN (
|
||||||
|
SELECT id FROM (
|
||||||
|
SELECT id, ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY player, badge
|
||||||
|
ORDER BY earned DESC, progress DESC, updated DESC
|
||||||
|
) AS rn
|
||||||
|
FROM badge_progress
|
||||||
|
)
|
||||||
|
WHERE rn = 1
|
||||||
|
)
|
||||||
|
`).execute()
|
||||||
|
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1063824264")
|
||||||
|
|
||||||
|
unmarshal({
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX `idx_badge_progress_player_badge` ON `badge_progress` (`player`, `badge`)"
|
||||||
|
]
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1063824264")
|
||||||
|
|
||||||
|
unmarshal({
|
||||||
|
"indexes": []
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { List } from "@mantine/core";
|
import { List, Text } from "@mantine/core";
|
||||||
import { useLingui } from "@lingui/react/macro";
|
import { Plural, Trans, useLingui } from "@lingui/react/macro";
|
||||||
import ListLink from "@/components/list-link";
|
import ListLink from "@/components/list-link";
|
||||||
import {
|
import {
|
||||||
DatabaseIcon,
|
DatabaseIcon,
|
||||||
@@ -10,19 +10,38 @@ import {
|
|||||||
ListIcon,
|
ListIcon,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
import ListButton from "@/components/list-button";
|
import ListButton from "@/components/list-button";
|
||||||
import { migrateBadgeProgress } from "@/features/badges/server";
|
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
|
||||||
import { useState } from "react";
|
import { useServerQuery } from "@/lib/tanstack-query/hooks";
|
||||||
|
import toast from "@/lib/sonner";
|
||||||
|
|
||||||
const AdminPage = () => {
|
const AdminPage = () => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const [isMigrating, setIsMigrating] = useState(false);
|
|
||||||
|
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
|
||||||
|
queryKey: ["admin", "badge-migration-status"],
|
||||||
|
queryFn: () => getBadgeMigrationStatus(),
|
||||||
|
options: {
|
||||||
|
refetchInterval: (query) =>
|
||||||
|
query.state.data?.state === "running" ? 2000 : false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isMigrating = migrationStatus?.state === "running";
|
||||||
|
|
||||||
const handleMigrateBadges = async () => {
|
const handleMigrateBadges = async () => {
|
||||||
if (isMigrating) return;
|
if (isMigrating) return;
|
||||||
|
|
||||||
setIsMigrating(true);
|
const result = await migrateBadgeProgress();
|
||||||
await migrateBadgeProgress();
|
if (result.success) {
|
||||||
setIsMigrating(false);
|
toast.success(
|
||||||
|
result.data.started
|
||||||
|
? t`Badge recalculation started in the background`
|
||||||
|
: t`Badge recalculation is already running`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast.error(t`Failed to start badge recalculation`);
|
||||||
|
}
|
||||||
|
await refetchMigrationStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -43,6 +62,30 @@ const AdminPage = () => {
|
|||||||
onClick={handleMigrateBadges}
|
onClick={handleMigrateBadges}
|
||||||
loading={isMigrating}
|
loading={isMigrating}
|
||||||
/>
|
/>
|
||||||
|
{migrationStatus?.state === "running" && (
|
||||||
|
<Text size="sm" c="dimmed" px="md" py="xs">
|
||||||
|
<Trans>
|
||||||
|
Recalculating badges in the background ({migrationStatus.playersProcessed}/{migrationStatus.totalPlayers})
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{migrationStatus?.state === "completed" && (
|
||||||
|
<Text size="sm" c="dimmed" px="md" py="xs">
|
||||||
|
<Trans>
|
||||||
|
Last recalculation:{" "}
|
||||||
|
<Plural
|
||||||
|
value={migrationStatus.result.totalBadgesEarned}
|
||||||
|
one="# badge earned"
|
||||||
|
other="# badges earned"
|
||||||
|
/>
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{migrationStatus?.state === "failed" && (
|
||||||
|
<Text size="sm" c="red" px="md" py="xs">
|
||||||
|
<Trans>Badge recalculation failed. Check the server logs.</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
<ListLink
|
<ListLink
|
||||||
label={t`Activities`}
|
label={t`Activities`}
|
||||||
Icon={ListIcon}
|
Icon={ListIcon}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
|
import { emitServerEvent } from "@/lib/events/emitter";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
|
export type BadgeMigrationResult = {
|
||||||
|
playersProcessed: number;
|
||||||
|
totalBadgesEarned: number;
|
||||||
|
created: number;
|
||||||
|
updated: number;
|
||||||
|
deleted: number;
|
||||||
|
averageBadgesPerPlayer: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BadgeMigrationStatus =
|
||||||
|
| { state: "idle" }
|
||||||
|
| { state: "running"; startedAt: string; playersProcessed: number; totalPlayers: number }
|
||||||
|
| { state: "completed"; startedAt: string; finishedAt: string; result: BadgeMigrationResult }
|
||||||
|
| { state: "failed"; startedAt: string; finishedAt: string; error: string };
|
||||||
|
|
||||||
|
// In-process job state — valid only because the app runs a single replica.
|
||||||
|
let status: BadgeMigrationStatus = { state: "idle" };
|
||||||
|
|
||||||
|
export const getBadgeMigrationJobStatus = (): BadgeMigrationStatus => status;
|
||||||
|
|
||||||
|
export const startBadgeMigration = (): { started: boolean; status: BadgeMigrationStatus } => {
|
||||||
|
if (status.state === "running") {
|
||||||
|
return { started: false, status };
|
||||||
|
}
|
||||||
|
|
||||||
|
const startedAt = new Date().toISOString();
|
||||||
|
status = { state: "running", startedAt, playersProcessed: 0, totalPlayers: 0 };
|
||||||
|
|
||||||
|
void pbAdmin
|
||||||
|
.migrateBadgeProgress((playersProcessed, totalPlayers) => {
|
||||||
|
if (status.state === "running") {
|
||||||
|
status = { ...status, playersProcessed, totalPlayers };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
status = { state: "completed", startedAt, finishedAt: new Date().toISOString(), result };
|
||||||
|
logger.info("Badge migration completed", result);
|
||||||
|
emitServerEvent({ type: "badge" });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
status = {
|
||||||
|
state: "failed",
|
||||||
|
startedAt,
|
||||||
|
finishedAt: new Date().toISOString(),
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
};
|
||||||
|
logger.error("Badge migration failed", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { started: true, status };
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import { createServerFn } from "@tanstack/react-start";
|
|||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { emitServerEvent } from "@/lib/events/emitter";
|
import { emitServerEvent } from "@/lib/events/emitter";
|
||||||
|
import { getBadgeMigrationJobStatus, startBadgeMigration } from "./migration.server";
|
||||||
|
|
||||||
export const getPlayerBadges = createServerFn()
|
export const getPlayerBadges = createServerFn()
|
||||||
.validator(z.string())
|
.validator(z.string())
|
||||||
@@ -15,11 +16,13 @@ export const getPlayerBadges = createServerFn()
|
|||||||
export const migrateBadgeProgress = createServerFn()
|
export const migrateBadgeProgress = createServerFn()
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensAdminFunctionMiddleware])
|
||||||
.handler(async () =>
|
.handler(async () =>
|
||||||
toServerResult(async () => {
|
toServerResult(async () => startBadgeMigration())
|
||||||
const result = await pbAdmin.migrateBadgeProgress();
|
);
|
||||||
emitServerEvent({ type: "badge" });
|
|
||||||
return result;
|
export const getBadgeMigrationStatus = createServerFn()
|
||||||
})
|
.middleware([superTokensAdminFunctionMiddleware])
|
||||||
|
.handler(async () =>
|
||||||
|
toServerResult(async () => getBadgeMigrationJobStatus())
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getAllBadges = createServerFn()
|
export const getAllBadges = createServerFn()
|
||||||
|
|||||||
@@ -70,16 +70,6 @@ export function createBadgesService(pb: PocketBase) {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async clearAllBadgeProgress(): Promise<number> {
|
|
||||||
const existingProgress = await pb.collection("badge_progress").getFullList({
|
|
||||||
filter: 'badge.type != "manual"',
|
|
||||||
});
|
|
||||||
for (const progress of existingProgress) {
|
|
||||||
await pb.collection("badge_progress").delete(progress.id);
|
|
||||||
}
|
|
||||||
return existingProgress.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
async calculateBadgeProgress(playerId: string, badge: Badge): Promise<number> {
|
async calculateBadgeProgress(playerId: string, badge: Badge): Promise<number> {
|
||||||
if (badge.type === "manual") {
|
if (badge.type === "manual") {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -651,27 +641,36 @@ export function createBadgesService(pb: PocketBase) {
|
|||||||
return transformBadgeProgress(created);
|
return transformBadgeProgress(created);
|
||||||
},
|
},
|
||||||
|
|
||||||
async migrateBadgeProgress(): Promise<{
|
async migrateBadgeProgress(
|
||||||
success: boolean;
|
onProgress?: (playersProcessed: number, totalPlayers: number) => void
|
||||||
|
): Promise<{
|
||||||
playersProcessed: number;
|
playersProcessed: number;
|
||||||
progressRecordsCreated: number;
|
|
||||||
totalBadgesEarned: number;
|
totalBadgesEarned: number;
|
||||||
|
created: number;
|
||||||
|
updated: number;
|
||||||
|
deleted: number;
|
||||||
averageBadgesPerPlayer: string;
|
averageBadgesPerPlayer: string;
|
||||||
}> {
|
}> {
|
||||||
await this.clearAllBadgeProgress();
|
|
||||||
|
|
||||||
const badges = await this.listBadges();
|
const badges = await this.listBadges();
|
||||||
|
const autoBadges = badges.filter((badge) => badge.type !== "manual");
|
||||||
|
const players = await pb.collection("players").getFullList();
|
||||||
|
|
||||||
const allPlayers = await pb.collection("players").getFullList();
|
// Phase 1: read-only calculation. Existing badge_progress rows stay
|
||||||
const uniquePlayers = new Set(allPlayers.map((p: any) => p.id));
|
// untouched until the full desired state is known.
|
||||||
|
const desired = new Map<string, {
|
||||||
|
badge: string;
|
||||||
|
player: string;
|
||||||
|
progress: number;
|
||||||
|
earned: boolean;
|
||||||
|
}>();
|
||||||
|
const failedKeys = new Set<string>();
|
||||||
|
let playersProcessed = 0;
|
||||||
|
|
||||||
let totalProgressRecords = 0;
|
for (const player of players) {
|
||||||
let totalBadgesEarned = 0;
|
for (const badge of autoBadges) {
|
||||||
|
const key = `${player.id}|${badge.id}`;
|
||||||
for (const playerId of uniquePlayers) {
|
|
||||||
for (const badge of badges) {
|
|
||||||
try {
|
try {
|
||||||
const progress = await this.calculateBadgeProgress(playerId, badge);
|
const progress = await this.calculateBadgeProgress(player.id, badge);
|
||||||
const target = this.getTargetProgress(badge);
|
const target = this.getTargetProgress(badge);
|
||||||
|
|
||||||
const isPlacementBadge = badge.criteria.placement !== undefined;
|
const isPlacementBadge = badge.criteria.placement !== undefined;
|
||||||
@@ -681,31 +680,77 @@ export function createBadgesService(pb: PocketBase) {
|
|||||||
: progress === target;
|
: progress === target;
|
||||||
|
|
||||||
if (progress > 0 || earned) {
|
if (progress > 0 || earned) {
|
||||||
await this.createBadgeProgress({
|
desired.set(key, {
|
||||||
badge: badge.id,
|
badge: badge.id,
|
||||||
player: playerId,
|
player: player.id,
|
||||||
progress: progress,
|
progress,
|
||||||
earned: earned,
|
earned,
|
||||||
});
|
});
|
||||||
|
|
||||||
totalProgressRecords++;
|
|
||||||
|
|
||||||
if (earned) {
|
|
||||||
totalBadgesEarned++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Error processing badge "${badge.name}" for player ${playerId}:`, error.message);
|
// Keep whatever row already exists when a calculation fails.
|
||||||
|
failedKeys.add(key);
|
||||||
|
console.error(`Error processing badge "${badge.name}" for player ${player.id}:`, error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
playersProcessed++;
|
||||||
|
onProgress?.(playersProcessed, players.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: apply as a diff — update changed rows, create missing ones,
|
||||||
|
// delete stale/duplicate ones. No window where all badges are gone.
|
||||||
|
const existing = await pb.collection("badge_progress").getFullList({
|
||||||
|
filter: 'badge.type != "manual"',
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingByKey = new Map<string, { id: string; progress: number; earned: boolean }>();
|
||||||
|
const toDelete: string[] = [];
|
||||||
|
for (const row of existing) {
|
||||||
|
const key = `${row.player}|${row.badge}`;
|
||||||
|
if (existingByKey.has(key)) {
|
||||||
|
toDelete.push(row.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
existingByKey.set(key, { id: row.id, progress: row.progress, earned: row.earned });
|
||||||
|
if (!desired.has(key) && !failedKeys.has(key)) {
|
||||||
|
toDelete.push(row.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let created = 0;
|
||||||
|
let updated = 0;
|
||||||
|
let totalBadgesEarned = 0;
|
||||||
|
|
||||||
|
for (const [key, want] of desired) {
|
||||||
|
if (want.earned) {
|
||||||
|
totalBadgesEarned++;
|
||||||
|
}
|
||||||
|
const row = existingByKey.get(key);
|
||||||
|
if (!row) {
|
||||||
|
await this.createBadgeProgress(want);
|
||||||
|
created++;
|
||||||
|
} else if (row.progress !== want.progress || row.earned !== want.earned) {
|
||||||
|
await this.updateBadgeProgress(row.id, {
|
||||||
|
progress: want.progress,
|
||||||
|
earned: want.earned,
|
||||||
|
});
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of toDelete) {
|
||||||
|
await this.deleteBadgeProgress(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
playersProcessed: players.length,
|
||||||
playersProcessed: uniquePlayers.size,
|
totalBadgesEarned,
|
||||||
progressRecordsCreated: totalProgressRecords,
|
created,
|
||||||
totalBadgesEarned: totalBadgesEarned,
|
updated,
|
||||||
averageBadgesPerPlayer: (totalBadgesEarned / uniquePlayers.size).toFixed(2),
|
deleted: toDelete.length,
|
||||||
|
averageBadgesPerPlayer: players.length
|
||||||
|
? (totalBadgesEarned / players.length).toFixed(2)
|
||||||
|
: "0",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ msgid "Active device: {0}"
|
|||||||
msgstr "Aktives Gerät: {0}"
|
msgstr "Aktives Gerät: {0}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/activities.tsx:21
|
#: src/app/routes/_authed/admin/activities.tsx:21
|
||||||
#: src/features/admin/components/admin-page.tsx:47
|
#: src/features/admin/components/admin-page.tsx:90
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Aktivitäten"
|
msgstr "Aktivitäten"
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ msgstr "Ø Sieg-Marge"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Abzeichen verleihen"
|
msgstr "Abzeichen verleihen"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:36
|
#: src/features/admin/components/admin-page.tsx:55
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Abzeichen verleihen"
|
msgstr "Abzeichen verleihen"
|
||||||
|
|
||||||
@@ -554,6 +554,18 @@ msgstr "Zurück"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Abzeichen an {playerName} verliehen"
|
msgstr "Abzeichen an {playerName} verliehen"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:86
|
||||||
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
|
msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs."
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:39
|
||||||
|
msgid "Badge recalculation is already running"
|
||||||
|
msgstr "Abzeichen-Neuberechnung läuft bereits"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:38
|
||||||
|
msgid "Badge recalculation started in the background"
|
||||||
|
msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet"
|
||||||
|
|
||||||
#: src/features/admin/components/award-badges.tsx:70
|
#: src/features/admin/components/award-badges.tsx:70
|
||||||
msgid "Badge Type"
|
msgid "Badge Type"
|
||||||
msgstr "Abzeichentyp"
|
msgstr "Abzeichentyp"
|
||||||
@@ -581,7 +593,7 @@ msgid "Bracket"
|
|||||||
msgstr "Turnierbaum"
|
msgstr "Turnierbaum"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/admin-page.tsx:59
|
#: src/features/admin/components/admin-page.tsx:102
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Turnierbaum-Vorschau"
|
msgstr "Turnierbaum-Vorschau"
|
||||||
|
|
||||||
@@ -1060,6 +1072,10 @@ msgstr "Aktives Gerät konnte nicht festgelegt werden"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "Lautstärke konnte nicht festgelegt werden"
|
msgstr "Lautstärke konnte nicht festgelegt werden"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:42
|
||||||
|
msgid "Failed to start badge recalculation"
|
||||||
|
msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden"
|
||||||
|
|
||||||
#: src/features/teams/components/team-form/index.tsx:105
|
#: src/features/teams/components/team-form/index.tsx:105
|
||||||
msgid "Failed to update team"
|
msgid "Failed to update team"
|
||||||
msgstr "Team konnte nicht aktualisiert werden"
|
msgstr "Team konnte nicht aktualisiert werden"
|
||||||
@@ -1322,6 +1338,11 @@ msgstr "Der Nachname muss 2-20 Zeichen lang sein und darf nur Buchstaben enthalt
|
|||||||
msgid "Last name must be 3-20 characters long and contain only letters"
|
msgid "Last name must be 3-20 characters long and contain only letters"
|
||||||
msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten"
|
msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
|
#: src/features/admin/components/admin-page.tsx:74
|
||||||
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}"
|
||||||
|
|
||||||
#: src/features/settings/components/color-scheme-picker.tsx:43
|
#: src/features/settings/components/color-scheme-picker.tsx:43
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Hell"
|
msgstr "Hell"
|
||||||
@@ -1410,7 +1431,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Team-Songs/Logos verwalten"
|
msgstr "Team-Songs/Logos verwalten"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/admin-page.tsx:31
|
#: src/features/admin/components/admin-page.tsx:50
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Turniere verwalten"
|
msgstr "Turniere verwalten"
|
||||||
|
|
||||||
@@ -1489,7 +1510,7 @@ msgstr "Matchup-Details"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Ich"
|
msgstr "Ich"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:41
|
#: src/features/admin/components/admin-page.tsx:60
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Abzeichen-Fortschritt migrieren"
|
msgstr "Abzeichen-Fortschritt migrieren"
|
||||||
|
|
||||||
@@ -1729,7 +1750,7 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "Einmalcode"
|
msgstr "Einmalcode"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:52
|
#: src/features/admin/components/admin-page.tsx:95
|
||||||
msgid "Open Pocketbase"
|
msgid "Open Pocketbase"
|
||||||
msgstr "Pocketbase öffnen"
|
msgstr "Pocketbase öffnen"
|
||||||
|
|
||||||
@@ -1927,6 +1948,12 @@ msgstr "Neu würfeln"
|
|||||||
msgid "Reactions"
|
msgid "Reactions"
|
||||||
msgstr "Reaktionen"
|
msgstr "Reaktionen"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
|
#: src/features/admin/components/admin-page.tsx:67
|
||||||
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
|
msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})"
|
||||||
|
|
||||||
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
||||||
msgid "Recently finished"
|
msgid "Recently finished"
|
||||||
msgstr "Kürzlich beendet"
|
msgstr "Kürzlich beendet"
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ msgid "Active device: {0}"
|
|||||||
msgstr "Active device: {0}"
|
msgstr "Active device: {0}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/activities.tsx:21
|
#: src/app/routes/_authed/admin/activities.tsx:21
|
||||||
#: src/features/admin/components/admin-page.tsx:47
|
#: src/features/admin/components/admin-page.tsx:90
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Activities"
|
msgstr "Activities"
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ msgstr "Avg Win Margin"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Award Badge"
|
msgstr "Award Badge"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:36
|
#: src/features/admin/components/admin-page.tsx:55
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Award Badges"
|
msgstr "Award Badges"
|
||||||
|
|
||||||
@@ -554,6 +554,18 @@ msgstr "Back"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Badge awarded to {playerName}"
|
msgstr "Badge awarded to {playerName}"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:86
|
||||||
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
|
msgstr "Badge recalculation failed. Check the server logs."
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:39
|
||||||
|
msgid "Badge recalculation is already running"
|
||||||
|
msgstr "Badge recalculation is already running"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:38
|
||||||
|
msgid "Badge recalculation started in the background"
|
||||||
|
msgstr "Badge recalculation started in the background"
|
||||||
|
|
||||||
#: src/features/admin/components/award-badges.tsx:70
|
#: src/features/admin/components/award-badges.tsx:70
|
||||||
msgid "Badge Type"
|
msgid "Badge Type"
|
||||||
msgstr "Badge Type"
|
msgstr "Badge Type"
|
||||||
@@ -581,7 +593,7 @@ msgid "Bracket"
|
|||||||
msgstr "Bracket"
|
msgstr "Bracket"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/admin-page.tsx:59
|
#: src/features/admin/components/admin-page.tsx:102
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Bracket Preview"
|
msgstr "Bracket Preview"
|
||||||
|
|
||||||
@@ -1060,6 +1072,10 @@ msgstr "Failed to set active device"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "Failed to set volume"
|
msgstr "Failed to set volume"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:42
|
||||||
|
msgid "Failed to start badge recalculation"
|
||||||
|
msgstr "Failed to start badge recalculation"
|
||||||
|
|
||||||
#: src/features/teams/components/team-form/index.tsx:105
|
#: src/features/teams/components/team-form/index.tsx:105
|
||||||
msgid "Failed to update team"
|
msgid "Failed to update team"
|
||||||
msgstr "Failed to update team"
|
msgstr "Failed to update team"
|
||||||
@@ -1322,6 +1338,11 @@ msgstr "Last name must be 2-20 characters long and contain only letters"
|
|||||||
msgid "Last name must be 3-20 characters long and contain only letters"
|
msgid "Last name must be 3-20 characters long and contain only letters"
|
||||||
msgstr "Last name must be 3-20 characters long and contain only letters"
|
msgstr "Last name must be 3-20 characters long and contain only letters"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
|
#: src/features/admin/components/admin-page.tsx:74
|
||||||
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
|
||||||
#: src/features/settings/components/color-scheme-picker.tsx:43
|
#: src/features/settings/components/color-scheme-picker.tsx:43
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Light"
|
msgstr "Light"
|
||||||
@@ -1410,7 +1431,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Manage Team Songs/Logos"
|
msgstr "Manage Team Songs/Logos"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/admin-page.tsx:31
|
#: src/features/admin/components/admin-page.tsx:50
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Manage Tournaments"
|
msgstr "Manage Tournaments"
|
||||||
|
|
||||||
@@ -1489,7 +1510,7 @@ msgstr "Matchup details"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Me"
|
msgstr "Me"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:41
|
#: src/features/admin/components/admin-page.tsx:60
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Migrate Badge Progress"
|
msgstr "Migrate Badge Progress"
|
||||||
|
|
||||||
@@ -1729,7 +1750,7 @@ msgstr "Once enrolled, you can view other free agents and their phone number in
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "One time code"
|
msgstr "One time code"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:52
|
#: src/features/admin/components/admin-page.tsx:95
|
||||||
msgid "Open Pocketbase"
|
msgid "Open Pocketbase"
|
||||||
msgstr "Open Pocketbase"
|
msgstr "Open Pocketbase"
|
||||||
|
|
||||||
@@ -1927,6 +1948,12 @@ msgstr "Re-roll"
|
|||||||
msgid "Reactions"
|
msgid "Reactions"
|
||||||
msgstr "Reactions"
|
msgstr "Reactions"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
|
#: src/features/admin/components/admin-page.tsx:67
|
||||||
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
|
msgstr "Recalculating badges in the background ({0}/{1})"
|
||||||
|
|
||||||
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
||||||
msgid "Recently finished"
|
msgid "Recently finished"
|
||||||
msgstr "Recently finished"
|
msgstr "Recently finished"
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ msgid "Active device: {0}"
|
|||||||
msgstr "Dispositivo activo: {0}"
|
msgstr "Dispositivo activo: {0}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/activities.tsx:21
|
#: src/app/routes/_authed/admin/activities.tsx:21
|
||||||
#: src/features/admin/components/admin-page.tsx:47
|
#: src/features/admin/components/admin-page.tsx:90
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Actividades"
|
msgstr "Actividades"
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ msgstr "Prom. Margen de Victoria"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Otorgar insignia"
|
msgstr "Otorgar insignia"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:36
|
#: src/features/admin/components/admin-page.tsx:55
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Otorgar insignias"
|
msgstr "Otorgar insignias"
|
||||||
|
|
||||||
@@ -554,6 +554,18 @@ msgstr "Atrás"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Insignia otorgada a {playerName}"
|
msgstr "Insignia otorgada a {playerName}"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:86
|
||||||
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
|
msgstr "Falló el recálculo de insignias. Revisa los registros del servidor."
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:39
|
||||||
|
msgid "Badge recalculation is already running"
|
||||||
|
msgstr "El recálculo de insignias ya está en curso"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:38
|
||||||
|
msgid "Badge recalculation started in the background"
|
||||||
|
msgstr "Recálculo de insignias iniciado en segundo plano"
|
||||||
|
|
||||||
#: src/features/admin/components/award-badges.tsx:70
|
#: src/features/admin/components/award-badges.tsx:70
|
||||||
msgid "Badge Type"
|
msgid "Badge Type"
|
||||||
msgstr "Tipo de insignia"
|
msgstr "Tipo de insignia"
|
||||||
@@ -581,7 +593,7 @@ msgid "Bracket"
|
|||||||
msgstr "Bracket"
|
msgstr "Bracket"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/admin-page.tsx:59
|
#: src/features/admin/components/admin-page.tsx:102
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Vista previa del bracket"
|
msgstr "Vista previa del bracket"
|
||||||
|
|
||||||
@@ -1060,6 +1072,10 @@ msgstr "No se pudo establecer el dispositivo activo"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "No se pudo ajustar el volumen"
|
msgstr "No se pudo ajustar el volumen"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:42
|
||||||
|
msgid "Failed to start badge recalculation"
|
||||||
|
msgstr "No se pudo iniciar el recálculo de insignias"
|
||||||
|
|
||||||
#: src/features/teams/components/team-form/index.tsx:105
|
#: src/features/teams/components/team-form/index.tsx:105
|
||||||
msgid "Failed to update team"
|
msgid "Failed to update team"
|
||||||
msgstr "No se pudo actualizar el equipo"
|
msgstr "No se pudo actualizar el equipo"
|
||||||
@@ -1322,6 +1338,11 @@ msgstr "El apellido debe tener entre 2 y 20 caracteres y contener solo letras"
|
|||||||
msgid "Last name must be 3-20 characters long and contain only letters"
|
msgid "Last name must be 3-20 characters long and contain only letters"
|
||||||
msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras"
|
msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
|
#: src/features/admin/components/admin-page.tsx:74
|
||||||
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}"
|
||||||
|
|
||||||
#: src/features/settings/components/color-scheme-picker.tsx:43
|
#: src/features/settings/components/color-scheme-picker.tsx:43
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "Claro"
|
msgstr "Claro"
|
||||||
@@ -1410,7 +1431,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Administrar canciones/logos del equipo"
|
msgstr "Administrar canciones/logos del equipo"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/admin-page.tsx:31
|
#: src/features/admin/components/admin-page.tsx:50
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Administrar torneos"
|
msgstr "Administrar torneos"
|
||||||
|
|
||||||
@@ -1489,7 +1510,7 @@ msgstr "Detalles del enfrentamiento"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Yo"
|
msgstr "Yo"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:41
|
#: src/features/admin/components/admin-page.tsx:60
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Migrar progreso de insignias"
|
msgstr "Migrar progreso de insignias"
|
||||||
|
|
||||||
@@ -1729,7 +1750,7 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "Código de un solo uso"
|
msgstr "Código de un solo uso"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:52
|
#: src/features/admin/components/admin-page.tsx:95
|
||||||
msgid "Open Pocketbase"
|
msgid "Open Pocketbase"
|
||||||
msgstr "Abrir Pocketbase"
|
msgstr "Abrir Pocketbase"
|
||||||
|
|
||||||
@@ -1927,6 +1948,12 @@ msgstr "Volver a sortear"
|
|||||||
msgid "Reactions"
|
msgid "Reactions"
|
||||||
msgstr "Reacciones"
|
msgstr "Reacciones"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
|
#: src/features/admin/components/admin-page.tsx:67
|
||||||
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
|
msgstr "Recalculando insignias en segundo plano ({0}/{1})"
|
||||||
|
|
||||||
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
||||||
msgid "Recently finished"
|
msgid "Recently finished"
|
||||||
msgstr "Recién terminados"
|
msgstr "Recién terminados"
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ msgid "Active device: {0}"
|
|||||||
msgstr "アクティブなデバイス: {0}"
|
msgstr "アクティブなデバイス: {0}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/activities.tsx:21
|
#: src/app/routes/_authed/admin/activities.tsx:21
|
||||||
#: src/features/admin/components/admin-page.tsx:47
|
#: src/features/admin/components/admin-page.tsx:90
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "アクティビティ"
|
msgstr "アクティビティ"
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ msgstr "平均勝差"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "バッジを付与"
|
msgstr "バッジを付与"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:36
|
#: src/features/admin/components/admin-page.tsx:55
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "バッジを付与"
|
msgstr "バッジを付与"
|
||||||
|
|
||||||
@@ -554,6 +554,18 @@ msgstr "戻る"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "{playerName}にバッジを付与しました"
|
msgstr "{playerName}にバッジを付与しました"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:86
|
||||||
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
|
msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:39
|
||||||
|
msgid "Badge recalculation is already running"
|
||||||
|
msgstr "バッジの再計算はすでに実行中です"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:38
|
||||||
|
msgid "Badge recalculation started in the background"
|
||||||
|
msgstr "バックグラウンドでバッジの再計算を開始しました"
|
||||||
|
|
||||||
#: src/features/admin/components/award-badges.tsx:70
|
#: src/features/admin/components/award-badges.tsx:70
|
||||||
msgid "Badge Type"
|
msgid "Badge Type"
|
||||||
msgstr "バッジの種類"
|
msgstr "バッジの種類"
|
||||||
@@ -581,7 +593,7 @@ msgid "Bracket"
|
|||||||
msgstr "ブラケット"
|
msgstr "ブラケット"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/admin-page.tsx:59
|
#: src/features/admin/components/admin-page.tsx:102
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "ブラケットプレビュー"
|
msgstr "ブラケットプレビュー"
|
||||||
|
|
||||||
@@ -1060,6 +1072,10 @@ msgstr "アクティブなデバイスの設定に失敗しました"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "音量の設定に失敗しました"
|
msgstr "音量の設定に失敗しました"
|
||||||
|
|
||||||
|
#: src/features/admin/components/admin-page.tsx:42
|
||||||
|
msgid "Failed to start badge recalculation"
|
||||||
|
msgstr "バッジの再計算を開始できませんでした"
|
||||||
|
|
||||||
#: src/features/teams/components/team-form/index.tsx:105
|
#: src/features/teams/components/team-form/index.tsx:105
|
||||||
msgid "Failed to update team"
|
msgid "Failed to update team"
|
||||||
msgstr "チームの更新に失敗しました"
|
msgstr "チームの更新に失敗しました"
|
||||||
@@ -1322,6 +1338,11 @@ msgstr "姓は2〜20文字の英字のみで入力してください"
|
|||||||
msgid "Last name must be 3-20 characters long and contain only letters"
|
msgid "Last name must be 3-20 characters long and contain only letters"
|
||||||
msgstr "姓は3〜20文字の英字のみで入力してください"
|
msgstr "姓は3〜20文字の英字のみで入力してください"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
|
#: src/features/admin/components/admin-page.tsx:74
|
||||||
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}"
|
||||||
|
|
||||||
#: src/features/settings/components/color-scheme-picker.tsx:43
|
#: src/features/settings/components/color-scheme-picker.tsx:43
|
||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr "ライト"
|
msgstr "ライト"
|
||||||
@@ -1410,7 +1431,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "チームの曲・ロゴを管理"
|
msgstr "チームの曲・ロゴを管理"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/admin-page.tsx:31
|
#: src/features/admin/components/admin-page.tsx:50
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "トーナメント管理"
|
msgstr "トーナメント管理"
|
||||||
|
|
||||||
@@ -1489,7 +1510,7 @@ msgstr "対戦カードの詳細"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "自分"
|
msgstr "自分"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:41
|
#: src/features/admin/components/admin-page.tsx:60
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "バッジ進捗を移行"
|
msgstr "バッジ進捗を移行"
|
||||||
|
|
||||||
@@ -1729,7 +1750,7 @@ msgstr "登録が完了すると、他のフリーエージェントとその電
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "ワンタイムコード"
|
msgstr "ワンタイムコード"
|
||||||
|
|
||||||
#: src/features/admin/components/admin-page.tsx:52
|
#: src/features/admin/components/admin-page.tsx:95
|
||||||
msgid "Open Pocketbase"
|
msgid "Open Pocketbase"
|
||||||
msgstr "Pocketbaseを開く"
|
msgstr "Pocketbaseを開く"
|
||||||
|
|
||||||
@@ -1927,6 +1948,12 @@ msgstr "再抽選"
|
|||||||
msgid "Reactions"
|
msgid "Reactions"
|
||||||
msgstr "リアクション"
|
msgstr "リアクション"
|
||||||
|
|
||||||
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
|
#: src/features/admin/components/admin-page.tsx:67
|
||||||
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
|
msgstr "バックグラウンドでバッジを再計算中({0}/{1})"
|
||||||
|
|
||||||
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
#: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34
|
||||||
msgid "Recently finished"
|
msgid "Recently finished"
|
||||||
msgstr "最近終了した試合"
|
msgstr "最近終了した試合"
|
||||||
|
|||||||
Reference in New Issue
Block a user