Merge branch 'main' into preview

This commit is contained in:
TresAbhi committed 2025-04-04 20:26:22 +00:00
commit 2a4375c718
300 files changed
+15602 -7394

No files matched your search

+1
View File
@@ -5,6 +5,7 @@
"types": "src/types.d.ts",
"dependencies": {
"@blitzkit/core": "workspace:*",
"@blitzkit/i18n": "workspace:*",
"satori": "^0.10.14",
"@resvg/resvg-js": "^2.6.2",
"discord.js": "^14.16.3",
+2 -2
View File
@@ -28,10 +28,10 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
async handler(interaction) {
const subcommand = interaction.options.getSubcommand();
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (subcommand === 'commands') {
return t`bot.commands.about.subcommands.commands.body${(
return `${strings.bot.commands.about.subcommands.commands.body}${(
await Promise.all(COMMANDS_RAW)
)
.sort((a, b) =>
+17 -13
View File
@@ -3,17 +3,18 @@ import {
Region,
regionToRegionSubdomain,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
import { chunkLines } from '../core/discord/chunkLines';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
interface Auction {
type Auction = null | {
count: number;
has_next: boolean;
results: Result[];
}
};
type Result =
| {
@@ -75,7 +76,7 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('auction'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const region: Region = 'com';
const pageSize = 80;
@@ -87,16 +88,16 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
pageSize
}&type[]=vehicle&saleable=${saleable}`,
).then((response) => response.json() as Promise<Auction>);
const saleableTanks = data.results.filter((result) => result.available);
if (saleableTanks.length === 0) {
return t`bot.commands.auction.errors.no_auction`;
if (data === null) {
return strings.bot.commands.auction.errors.no_auction;
}
const saleableTanks = data.results.filter((result) => result.available);
const nextPricesTimestamp = saleableTanks[0].next_price_timestamp;
const title = `# ${t`bot.commands.auction.body.title`}`;
const subtitle = `${translate('bot.commands.auction.body.subtitle', [
const title = `# ${strings.bot.commands.auction.body.title}`;
const subtitle = `${literals(strings.bot.commands.auction.body.subtitle, [
`${saleableTanks.length} / ${data.results.length}`,
`<t:${nextPricesTimestamp}:R>`,
'<https://wotblitz.com/auction/>',
@@ -112,15 +113,18 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
const next =
data.next_price === null
? ''
: `\n-# ${translate('bot.commands.auction.body.next', [
: `\n-# ${literals(strings.bot.commands.auction.body.next, [
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
interaction.locale,
)}`,
])}`;
const available = translate('bot.commands.auction.body.available', [
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
]);
const available = literals(
strings.bot.commands.auction.body.available,
[
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
],
);
const isOut = data.current_count === 0;
const outString = isOut ? '~~' : '';
const isLow = data.current_count <= WARNING_COUNT;
+17 -13
View File
@@ -7,6 +7,7 @@ import {
getWN8Percentile,
TankDefinition,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { chunk } from 'lodash-es';
import * as Breakdown from '../../components/Breakdown';
@@ -32,7 +33,7 @@ export async function renderBreakdown(
filters: StatFilters,
locale: Locale,
) {
const { t, translate } = translator(locale);
const { strings, unwrap } = translator(locale);
const awaitedTankDefinitions = await tankDefinitions;
const awaitedTankAverages = await blitzStarsTankAverages;
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
@@ -48,7 +49,7 @@ export async function renderBreakdown(
const orderedCareerWN8Full: (number | undefined)[] = [];
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
filteredOrder.forEach((id) => {
@@ -136,21 +137,21 @@ export async function renderBreakdown(
<Breakdown.Row
key={id}
displayType="summary"
title={t`bot.commands.breakdown.body.total`}
title={strings.bot.commands.breakdown.body.total}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: currentBattles,
career: careerBattles,
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * currentWinrate).toFixed(2)}%`,
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: currentWinrate - careerWinrate,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -165,7 +166,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(currentDamage).toLocaleString(),
career: Math.round(careerDamage).toLocaleString(),
delta: currentDamage - careerDamage,
@@ -190,17 +191,20 @@ export async function renderBreakdown(
class={tankDefinition?.class}
type={tankDefinition?.type}
title={
tankDefinition?.name ??
translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
tankDefinition
? unwrap(tankDefinition.name)
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
`${id}`,
])
}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: current.battles.toLocaleString(),
career: career.battles.toLocaleString(),
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * (current.wins / current.battles)).toFixed(
2,
)}%`,
@@ -209,7 +213,7 @@ export async function renderBreakdown(
current.wins / current.battles - career.wins / career.battles,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -224,7 +228,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(
current.damage_dealt / current.battles,
).toLocaleString(),
+6 -6
View File
@@ -11,17 +11,17 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('debug'),
async handler(interaction) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const currentTime = new Date().getTime();
const uptime = currentTime - executionStart;
const list = [
[t`bot.commands.debug.body.version`, packageJSON.version],
[strings.bot.commands.debug.body.version, packageJSON.version],
[
t`bot.commands.debug.body.shard`,
client.shard?.ids[0] ?? t`bot.commands.debug.body.shard.default`,
strings.bot.commands.debug.body.shard.label,
client.shard?.ids[0] ?? strings.bot.commands.debug.body.shard.default,
],
[
t`bot.commands.debug.body.uptime`,
strings.bot.commands.debug.body.uptime,
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor(
(uptime / 1000 / 60) % 60,
)}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
@@ -29,7 +29,7 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
)}ms`,
],
];
return t`bot.commands.debug.body.title${list
return `${strings.bot.commands.debug.body.title}${list
.map(([key, value]) => `- ${key}: ${value}`)
.join('\n')}`;
},
+12 -9
View File
@@ -1,4 +1,5 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { GenericStats } from '../components/GenericStats';
@@ -19,30 +20,31 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('inactive')
.addStringOption(addClanChoices)
.addNumberOption((option) => {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('threshold')
.setNameLocalizations(
localizationObject(
'bot.commands.inactive.options.threshold',
(strings) => strings.bot.commands.inactive.options.threshold.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.inactive.options.threshold.description'),
strings.bot.commands.inactive.options.threshold.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.inactive.options.threshold.description',
(strings) =>
strings.bot.commands.inactive.options.threshold.description,
),
)
.setMinValue(0);
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction);
const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
@@ -63,7 +65,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) =>
[
name,
translate('bot.commands.inactive.body.listing', [
literals(strings.bot.commands.inactive.body.listing, [
days.toFixed(0),
]),
] as [string, string],
@@ -75,9 +77,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={clanInfo.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${translate('bot.commands.inactive.body.subtitle', [
`${threshold}`,
])} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${literals(
strings.bot.commands.inactive.body.subtitle,
[`${threshold}`],
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
{!hasInactiveMembers && (
+7 -7
View File
@@ -22,7 +22,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const discordId = BigInt(interaction.user.id);
const accountInfo = await getAccountInfo(region, id);
@@ -120,9 +120,9 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
height: 16,
}}
/>
<span
style={{ fontSize: 16 }}
>{t`bot.commands.link.body.accounts_linked`}</span>
<span style={{ fontSize: 16 }}>
{strings.bot.commands.link.body.accounts_linked}
</span>
</div>
<div
@@ -174,7 +174,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
{clanAccountInfo?.clan
? `[${clanAccountInfo?.clan?.tag}] • `
: ''}
{translate(`common.regions.normal.${region}`)}
{strings.common.regions.normal[region]}
</span>
</div>
</div>
@@ -185,8 +185,8 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
</CommandWrapper>,
embedInfo(
t`bot.commands.link.embed.title`,
t`bot.commands.link.embed.description`,
strings.bot.commands.link.embed.title,
strings.bot.commands.link.embed.description,
),
];
},
+5 -4
View File
@@ -6,6 +6,7 @@ import {
getAccountInfo,
getTankStats,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import {
gameDefinitions,
@@ -27,14 +28,14 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const tier = Number(interaction.options.getString('tier'));
const { id, region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id);
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
const filteredTanks = (
@@ -59,7 +60,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
const awaitedTankDefinitions = await tankDefinitions;
const awaitedGameDefinitions = await gameDefinitions;
const title = translate('bot.commands.owned_tanks.body.title', [
const title = literals(strings.bot.commands.owned_tanks.body.title, [
markdownEscape(accountInfo.nickname),
TIER_ROMAN_NUMERALS[tier],
]);
@@ -77,7 +78,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
)
.map(
(tank) =>
`[${flags[tank.nation]} ${markdownEscape(tank.name)}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
);
const lines = [title, ...tankList];
+7 -6
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -7,26 +8,26 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('permissions'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
// friday the 13th permissions easter egg
const isFridayThe13th =
new Date().getDay() === 5 && new Date().getDate() === 13;
const permissions = [
[
interaction.appPermissions?.has('ViewChannel'),
t`bot.commands.permissions.body.view_channels`,
strings.bot.commands.permissions.body.view_channels,
],
[
interaction.appPermissions?.has('ReadMessageHistory'),
t`bot.commands.permissions.body.read_message_history`,
strings.bot.commands.permissions.body.read_message_history,
],
[
interaction.appPermissions?.has('AttachFiles'),
t`bot.commands.permissions.body.attach_files`,
strings.bot.commands.permissions.body.attach_files,
],
[
interaction.appPermissions?.has('UseExternalEmojis'),
t`bot.commands.permissions.body.external_emojis`,
strings.bot.commands.permissions.body.external_emojis,
],
];
const body = permissions
@@ -39,7 +40,7 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
})
.join('\n');
return translate('bot.commands.permissions.body.title', [body]);
return literals(strings.bot.commands.permissions.body.title, [body]);
},
});
});
+4 -3
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -12,19 +13,19 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
]),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand();
const executionStart = Date.now();
if (subcommand === 'blitzkit') {
await interaction.editReply(t`bot.commands.ping.body.pong`);
await interaction.editReply(strings.bot.commands.ping.body.pong);
} else {
await fetch('https://api.wotblitz.com/');
}
const executionTime = Date.now() - executionStart;
interaction.editReply(
translate('bot.commands.ping.body.pong_with_time', [
literals(strings.bot.commands.ping.body.pong_with_time, [
`${Math.round(executionTime)}`,
]),
);
+9 -8
View File
@@ -1,4 +1,5 @@
import { getAccountInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
@@ -15,27 +16,27 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const account = await resolvePlayerFromCommand(interaction);
const { id, region: region } = account;
const accountInfo = await getAccountInfo(region, id);
return embedInfo(
translate('bot.commands.player_info.body.title', [
literals(strings.bot.commands.player_info.body.title, [
markdownEscape(accountInfo.nickname),
]),
markdownTable([
[
t`bot.commands.player_info.body.nickname`,
strings.bot.commands.player_info.body.nickname,
`${accountInfo.nickname}`,
],
[
t`bot.commands.player_info.body.battles`,
strings.bot.commands.player_info.body.battles,
`${accountInfo.statistics.all.battles}`,
],
[
t`bot.commands.player_info.body.winrate`,
strings.bot.commands.player_info.body.winrate,
`${(
100 *
(accountInfo.statistics.all.wins /
@@ -44,17 +45,17 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
],
[],
[
t`bot.commands.player_info.body.account_id`,
strings.bot.commands.player_info.body.account_id,
`${accountInfo.account_id}`,
],
[
t`bot.commands.player_info.body.created`,
strings.bot.commands.player_info.body.created,
new Date(accountInfo.created_at * 1000).toLocaleDateString(
interaction.locale,
),
],
[
t`bot.commands.player_info.body.last_battle`,
strings.bot.commands.player_info.body.last_battle,
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
interaction.locale,
),
+8 -7
View File
@@ -9,6 +9,7 @@ import {
getRatingNeighbors,
imgur,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { SlashCommandSubcommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
@@ -53,7 +54,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const subcommand = interaction.options.getSubcommand(true) as
| 'season'
| 'today';
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
@@ -89,7 +90,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
if (statsB2Array === undefined) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -98,10 +99,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const league = getLeagueFromScore(statsB2.score);
if (!statsA) {
return t`bot.commands.rating.errors.unrecorded_stats`;
return strings.bot.commands.rating.errors.unrecorded_stats;
}
if (!statsB1) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -139,9 +140,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={accountInfo.nickname}
image={clanImage}
description={`${translate(
`bot.commands.rating.body.subtitle.${subcommand}`,
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${
strings.bot.commands.rating.body.subtitle[subcommand]
} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
<div
+67 -38
View File
@@ -14,6 +14,7 @@ import {
RatingLeaderboard,
Region,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -54,28 +55,32 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
);
function addOptions(option: SlashCommandSubcommandBuilder) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.addIntegerOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.limit`)
.setName(strings.bot.commands.rating_leaderboard.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.limit.description',
literals(
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -85,22 +90,26 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
)
.addStringOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.season`)
.setName(
strings.bot.commands.rating_leaderboard.options.season.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.season.description',
),
strings.bot.commands.rating_leaderboard.options.season
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season
.description,
),
)
.setRequired(false)
@@ -112,15 +121,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
return {
name: current
? translate(
'bot.commands.rating_leaderboard.options.season.choices.current',
? literals(
strings.bot.commands.rating_leaderboard.options.season
.choices.current,
[`${number}`],
)
: `${number}`,
value: `${number}`,
name_localizations: current
? localizationObject(
'bot.commands.rating_leaderboard.options.season.choices.current',
(strings) =>
strings.bot.commands.rating_leaderboard.options
.season.choices.current,
[`${number}`],
)
: undefined,
@@ -145,35 +157,45 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
modify(option: SlashCommandSubcommandBuilder) {
option
.addStringOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.rating_leaderboard.options.league`)
.setName(
strings.bot.commands.rating_leaderboard.options.league.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.league.description',
),
strings.bot.commands.rating_leaderboard.options.league
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.description,
),
)
.addChoices(
...LEAGUES.map(
({ name }, value) =>
({
name: translate(`common.leagues.${name}`),
name: (
strings.common.leagues as Record<string, string>
)[name],
value: `${value}`,
name_localizations: localizationObject(
`common.leagues.${name}`,
(strings) =>
(
strings.common.leagues as Record<string, string>
)[name],
),
}) satisfies APIApplicationCommandOptionChoice<string>,
),
@@ -189,7 +211,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
// BIG TODO: title seems to be in english
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
| 'neighbors';
@@ -223,7 +245,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const result = isCurrentSeason
@@ -268,8 +291,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
clan: clanData[player.id]?.clan?.tag,
nickname:
clanData[index]?.account_name ??
translate(
'bot.commands.rating_leaderboard.body.deleted_player',
literals(
strings.bot.commands.rating_leaderboard.body
.deleted_player,
[`${player.id}`],
),
}) satisfies SimplifiedPlayer,
@@ -289,12 +313,14 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
: regionRatingInfo.count) - result[result.length - 1].position;
titleName = `${
leagueInfo
? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`)
? (strings.common.leagues as Record<string, string>)[
LEAGUES[leagueInfo.index].name
]
: ''
} - ${translate(`common.regions.short.${region}`)}`;
} - ${strings.common.regions.short[region]}`;
titleImage = imgur(leagueInfo.icon);
titleDescription = translate(
'bot.commands.rating_leaderboard.body.top_players',
titleDescription = literals(
strings.bot.commands.rating_leaderboard.body.top_players,
[`${limit}`],
);
} else {
@@ -307,7 +333,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const accountInfo = await getAccountInfo(region, id);
@@ -334,8 +361,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
if (playerIndex === -1) {
throw new UserError(
translate(
'bot.commands.rating_leaderboard.body.no_participation',
literals(
strings.bot.commands.rating_leaderboard.body
.no_participation,
[markdownEscape(accountInfo.nickname), `${season}`],
),
);
@@ -373,7 +401,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined;
titleName = neighbors ? accountInfo.nickname : '';
titleDescription = t`bot.commands.rating_leaderboard.body.neighbors`;
titleDescription =
strings.bot.commands.rating_leaderboard.body.neighbors;
playersAfter =
(regionRatingInfo === null
? archivedLeaderboard.version!.value.entries.length
@@ -422,8 +451,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
{items}
{playersAfter > 0 && (
<Leaderboard.Gap
message={translate(
'bot.commands.rating_leaderboard.body.more',
message={literals(
strings.bot.commands.rating_leaderboard.body.more,
[`${playersAfter.toLocaleString(interaction.locale)}`],
)}
/>
+13 -3
View File
@@ -6,6 +6,7 @@ import {
tankIcon,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
import { clamp } from 'lodash-es';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -18,6 +19,7 @@ import { RenderConfiguration } from '../core/blitzkit/renderConfiguration';
import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
import { buttonLink } from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
@@ -218,11 +220,13 @@ function stat(value: string | number, flex = 2) {
}
async function playerListing(
locale: Locale,
player: WotInspectorReplayPlayerData,
protagonistTeam: boolean,
winningTeam: number,
recordingPlayer = false,
) {
const { unwrap } = translator(locale);
const awaitedTankAverages = await blitzStarsTankAverages;
const awaitedTankDefinitions = await tankDefinitions;
const tank = awaitedTankDefinitions.tanks[player.vehicle_descr];
@@ -275,7 +279,7 @@ async function playerListing(
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={await iconPng(tankIcon(tank.id))}
style={{
width: 32,
@@ -312,7 +316,7 @@ async function playerListing(
fontSize: 16,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
@@ -530,6 +534,7 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
)
.map((player) =>
playerListing(
interaction.locale,
player,
true,
replayData.winner_team,
@@ -553,7 +558,12 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
players
.filter((player) => player.team === antagonistTeamId)
.map((player) =>
playerListing(player, false, replayData.winner_team),
playerListing(
interaction.locale,
player,
false,
replayData.winner_team,
),
),
)}
</div>
+72 -52
View File
@@ -9,6 +9,7 @@ import {
TankType,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { escapeMarkdown, Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -28,26 +29,28 @@ import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('research')
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.target_tank`)
.setName(strings.bot.commands.research.options.target_tank.name)
.setNameLocalizations(
localizationObject(
'bot.commands.research.options.target_tank',
(strings) =>
strings.bot.commands.research.options.target_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.target_tank.description`,
strings.bot.commands.research.options.target_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.target_tank.description',
(strings) =>
strings.bot.commands.research.options.target_tank.description,
),
)
.setAutocomplete(true)
@@ -55,20 +58,22 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
)
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.starting_tank`)
.setName(strings.bot.commands.research.options.starting_tank.name)
.setNameLocalizations(
localizationObject(
'bot.commands.research.options.starting_tank',
(strings) =>
strings.bot.commands.research.options.starting_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.starting_tank.description`,
strings.bot.commands.research.options.starting_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.starting_tank.description',
(strings) =>
strings.bot.commands.research.options.starting_tank.description,
),
)
.setAutocomplete(true)
@@ -77,7 +82,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const targetTankId = await resolveTankId(
interaction.options.getString('target-tank', true),
@@ -98,50 +103,55 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
);
if (targetTankId === startingTankId) {
return translate('bot.commands.research.errors.start_end_equal', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.start_end_equal,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
if (startingTank.tier > targetTank.tier) {
return translate('bot.commands.research.errors.unordered', [
escapeMarkdown(startingTank.name),
escapeMarkdown(targetTank.name),
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.unordered, [
escapeMarkdown(unwrap(startingTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (!targetTankAncestry.includes(startingTankId)) {
return translate('bot.commands.research.errors.tanks_not_on_line', [
escapeMarkdown(targetTank.name),
escapeMarkdown(startingTank.name),
]);
return literals(
strings.bot.commands.research.errors.tanks_not_on_line,
[
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(startingTank.name)),
],
);
}
} else {
if (targetTank.type !== TankType.RESEARCHABLE) {
return translate('bot.commands.research.errors.non_tech_tree', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.non_tech_tree, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (targetTank.tier === 1) {
return translate('bot.commands.research.errors.tier_1', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.tier_1, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return translate('bot.commands.research.errors.already_researched', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.already_researched,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const foundAncestor = targetTankAncestry.find(
@@ -255,7 +265,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={nickname}
image={clanImage}
description={`${t`bot.commands.research.body.subtitle`} • ${
description={`${strings.bot.commands.research.body.subtitle} • ${
targetTank.name
}`}
/>
@@ -292,7 +302,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{t`bot.commands.research.body.total`}
{strings.bot.commands.research.body.total}
</span>
</div>
@@ -452,7 +462,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={TANK_ICONS[tank.class]}
width={16}
height={16}
@@ -463,7 +473,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
</div>
@@ -497,11 +507,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.research', [
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
])}
{literals(
strings.bot.commands.research.body.research,
[
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
],
)}
</span>
</div>
@@ -527,9 +540,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.upgrades', [
costs[index].upgrades.toLocaleString(),
])}
{literals(
strings.bot.commands.research.body.upgrades,
[costs[index].upgrades.toLocaleString()],
)}
</span>
</div>
)}
@@ -555,11 +569,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.purchase', [
costs[index].purchase.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.purchase,
[
costs[index].purchase.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
@@ -584,11 +601,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.equipment', [
costs[index].equipment.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.equipment,
[
costs[index].equipment.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
</div>
@@ -602,7 +622,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
return startingTankRaw
? image
: [t`bot.commands.research.body.estimation`, image];
: [strings.bot.commands.research.body.estimation, image];
},
autocomplete(interaction) {
+14 -11
View File
@@ -1,4 +1,5 @@
import { ClanList, fetchBlitz, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addClanChoices } from '../core/discord/addClanChoices';
@@ -19,25 +20,27 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
addClanChoices(option).setAutocomplete(false),
)
.addIntegerOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.search_clans.options.limit`)
.setName(strings.bot.commands.search_clans.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.search_clans.options.limit',
(strings) => strings.bot.commands.search_clans.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_clans.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_clans.options.limit.description',
(strings) =>
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -46,7 +49,7 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const clan = interaction.options.getString('clan')!;
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
@@ -56,12 +59,12 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
});
return embedInfo(
translate('bot.commands.search_clans.body.title', [
literals(strings.bot.commands.search_clans.body.title, [
markdownEscape(clan),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
clanList.length === 0
? translate('bot.commands.search_clans.body.no_results')
? strings.bot.commands.search_clans.body.no_results
: `\`\`\`\n${clanList
.map((clan) => `${clan.name} [${clan.tag}]`)
.join('\n')}\n\`\`\``,
+15 -11
View File
@@ -4,6 +4,7 @@ import {
Region,
usernamePattern,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addRegionChoices } from '../core/discord/addRegionChoices';
@@ -17,7 +18,7 @@ import { CommandRegistry } from '../events/interactionCreate';
const DEFAULT_LIMIT = 25;
export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('search-players')
@@ -27,22 +28,25 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
)
.addIntegerOption((option) =>
option
.setName(t`bot.commands.search_players.options.limit`)
.setName(strings.bot.commands.search_players.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.search_players.options.limit',
(strings) =>
strings.bot.commands.search_players.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_players.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_players.options.limit.description',
(strings) =>
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -51,7 +55,7 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const name = interaction.options.getString('username')!;
const limit = interaction.options.getInteger('limit') ?? 25;
@@ -64,13 +68,13 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
: [];
return embedInfo(
translate('bot.commands.search_players.body.title', [
literals(strings.bot.commands.search_players.body.title, [
markdownEscape(trimmedSearch),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
`\`\`\`${
players.length === 0
? translate('bot.commands.search_players.body.no_results')
? strings.bot.commands.search_players.body.no_results
: players.map((player) => player.nickname).join('\n')
}\`\`\``,
);
@@ -32,7 +32,7 @@ export function AllStatsOverview({
locale,
supplementaryStats,
}: AllStatsOverviewProps) {
const { t, translate } = translator(locale);
const { strings } = translator(locale);
const percentile =
typeof supplementaryStats.WN8 === 'number'
? getWN8Percentile(supplementaryStats.WN8)
@@ -63,18 +63,18 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.battles`}
label={strings.bot.common.hero_stats.battles}
value={stats.battles.toLocaleString()}
align={WN8SurroundingStatAlign.Right}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.winrate`}
label={strings.bot.common.hero_stats.winrate}
value={`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`}
align={WN8SurroundingStatAlign.Right}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage`}
label={strings.bot.common.hero_stats.damage}
value={Math.round(
stats.damage_dealt / stats.battles,
).toLocaleString()}
@@ -82,7 +82,7 @@ export function AllStatsOverview({
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage_ratio`}
label={strings.bot.common.hero_stats.damage_ratio}
value={(stats.damage_dealt / stats.damage_received).toFixed(2)}
align={WN8SurroundingStatAlign.Right}
/>
@@ -90,7 +90,7 @@ export function AllStatsOverview({
<HeroStat
value={heroStat}
subtitle={translate(`common.wn8_percentile.${percentile}`)}
subtitle={strings.common.wn8_percentile[percentile]}
color={color}
/>
@@ -103,25 +103,25 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.tier`}
label={strings.bot.common.hero_stats.tier}
value={
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.survival`}
label={strings.bot.common.hero_stats.survival}
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
2,
)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.accuracy`}
label={strings.bot.common.hero_stats.accuracy}
value={`${(100 * (stats.hits / stats.shots)).toFixed(2)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.kills`}
label={strings.bot.common.hero_stats.kills}
value={(stats.frags / stats.battles).toFixed(2)}
/>
</div>
@@ -16,8 +16,8 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
gap: 16,
width: '100%',
color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1,
position: 'relative',
background: theme.colors.appBackground1,
}}
>
{children}
@@ -21,7 +21,7 @@ export function GenericAllStats({
supplementaryStats,
locale,
}: GenericAllStatsProps) {
const { t } = translator(locale);
const { strings } = translator(locale);
const damageRatio = stats.damage_dealt / stats.damage_received;
const killsToDeathRatio =
stats.frags / (stats.battles - stats.survived_battles);
@@ -30,27 +30,28 @@ export function GenericAllStats({
<Root>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.games`}
title={strings.bot.commands.full_stats.body.cards.games.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.games.winrate`,
label: strings.bot.commands.full_stats.body.cards.games.winrate,
value: `${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.games.battles`,
label: strings.bot.commands.full_stats.body.cards.games.battles,
value: stats.battles,
},
{
label: t`bot.commands.full_stats.body.cards.games.wins_losses`,
label:
strings.bot.commands.full_stats.body.cards.games.wins_losses,
value: `${stats.wins} / ${stats.losses}`,
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.efficiency`}
title={strings.bot.commands.full_stats.body.cards.efficiency.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.efficiency.wn8`,
label: strings.bot.commands.full_stats.body.cards.efficiency.wn8,
value:
supplementaryStats && isNumber(supplementaryStats.WN8)
? supplementaryStats.WN8!.toFixed(0)
@@ -60,14 +61,16 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.survival`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.survival,
value: `${(
100 *
(stats.survived_battles / stats.battles)
).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.accuracy`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.accuracy,
value: `${((stats.hits / stats.shots) * 100).toFixed(2)}%`,
},
]}
@@ -75,14 +78,18 @@ export function GenericAllStats({
</Row>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.lethality`}
title={strings.bot.commands.full_stats.body.cards.lethality.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.lethality.average_damage`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_damage,
value: (stats.damage_dealt / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_per_tier`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_per_tier,
value: supplementaryStats?.tier
? (
stats.damage_dealt /
@@ -92,32 +99,44 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_ratio`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_ratio,
value: isFinite(damageRatio) ? damageRatio.toFixed(2) : undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.average_kills`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_kills,
value: (stats.frags / stats.battles).toFixed(0),
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.miscellaneous`}
title={strings.bot.commands.full_stats.body.cards.miscellaneous.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_tier`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_tier,
value: supplementaryStats?.tier?.toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_spots`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_spots,
value: (stats.spotted / stats.battles).toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_xp`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_xp,
value: (stats.xp / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.kills_to_death_ratio`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.kills_to_death_ratio,
value: isFinite(killsToDeathRatio)
? killsToDeathRatio.toFixed(2)
: undefined,
+2 -2
View File
@@ -12,7 +12,7 @@ interface NoDataProps {
}
export function NoData({ type, locale }: NoDataProps) {
const { translate } = translator(locale);
const { strings } = translator(locale);
return (
<div
@@ -23,7 +23,7 @@ export function NoData({ type, locale }: NoDataProps) {
}}
>
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
{translate(`bot.common.no_data.${type}`)}
{strings.bot.common.no_data[type]}
</span>
</div>
);
+6 -7
View File
@@ -1,3 +1,5 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
import markdownEscape from 'markdown-escape';
@@ -11,22 +13,19 @@ export async function resolveTankId(
locale: Locale,
techTreeOnly = false,
) {
const { translate } = translator(locale);
const { strings } = translator(locale);
const number = typeof tank === 'string' ? Number(tank) : tank;
if (Number.isNaN(number)) {
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 1,
},
{ keys: SEARCH_KEYS, limit: 1 },
);
if (searchResult.length === 0) {
throw new UserError(
translate('bot.common.errors.tank_name_not_found', [
literals(strings.bot.common.errors.tank_name_not_found, [
markdownEscape(`${tank}`),
]),
);
@@ -40,7 +39,7 @@ export async function resolveTankId(
return number;
} else {
throw new UserError(
translate('bot.common.errors.tank_id_not_found', [`${number}`]),
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
);
}
}
@@ -8,15 +8,15 @@ export async function searchClansAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchClansAcrossRegions(trimmed, limit);
@@ -8,15 +8,15 @@ export async function searchPlayersAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchPlayersAcrossRegions(trimmed, limit);
@@ -1,4 +1,5 @@
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { StatFilters } from '../blitzstars/filterStats';
import { translator } from '../localization/translator';
@@ -7,22 +8,21 @@ export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
) {
const { translate } = translator(locale);
const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions();
const info: string[] = [];
// TODO: somehow localize tank names?
if (tank) return awaitedTankDefinitions.tanks[tank].name;
if (nation) info.push(translate(`common.nations.${nation}`));
if (tank) return awaitedTankDefinitions.tanks[tank].name.locales[bkLocale];
if (nation)
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
info.push(
translate('bot.common.filters.tier', [TIER_ROMAN_NUMERALS[tier]]),
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
);
}
if (tankClass) info.push(translate(`common.tank_class_short.${tankClass}`));
if (type) info.push(translate(`common.tree_type.${type}`));
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
if (type) info.push(strings.common.tree_type[type]);
return info.length === 0
? translate('bot.common.filters.none')
: info.join(', ');
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
}
@@ -8,6 +8,8 @@ export async function getBlitzFromDiscord(discordId: bigint) {
if (unique === null) return null;
await flagUserActivity(unique.blitz_id);
return { id: unique.blitz_id, region: idToRegion(unique.blitz_id) };
const id = Number(unique.blitz_id);
await flagUserActivity(id);
return { id, region: idToRegion(id) };
}
@@ -38,7 +38,7 @@ export async function getTankHistories(
locale: Locale,
options?: Partial<GetTankHistoriesOptions>,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
@@ -82,7 +82,7 @@ export async function getTankHistories(
const tankStats = await getTankStats(server, id);
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
tankStats.forEach((tankHistory) => {
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addClanChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('clan')
.setNameLocalizations(
localizationObject('bot.common.options.clan', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.clan.name,
undefined,
true,
),
)
.setDescription(translate('bot.common.options.clan.description'))
.setDescription(strings.bot.common.options.clan.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.clan.description'),
localizationObject(
(strings) => strings.bot.common.options.clan.description,
),
)
.setAutocomplete(true)
.setMinLength(2)
@@ -17,34 +17,40 @@ import { localizationObject } from './localizationObject';
export function addFilterOptions<
Option extends SlashCommandBuilder | SlashCommandSubcommandBuilder,
>(option: Option, nations: string[]) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return (
option
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.nation'))
.setName(strings.bot.common.options.filters.nation.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.nation',
(strings) => strings.bot.common.options.filters.nation.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.nation.description'),
)
.setDescription(strings.bot.common.options.filters.nation.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.nation.description'),
localizationObject(
(strings) =>
strings.bot.common.options.filters.nation.description,
),
)
.addChoices(
...nations.map(
(nation) =>
({
value: nation,
name: translate(`common.nations.${nation}`),
name: (strings.common.nations as Record<string, string>)[
nation
],
name_localizations: localizationObject(
`common.nations.${nation}`,
(strings) =>
(strings.common.nations as Record<string, string>)[
nation
],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -54,28 +60,28 @@ export function addFilterOptions<
.addStringOption((option) => addTierChoices(option).setRequired(false))
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.class'))
.setName(strings.bot.common.options.filters.class.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.class',
(strings) => strings.bot.common.options.filters.class.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.class.description'),
)
.setDescription(strings.bot.common.options.filters.class.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.class.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.class.description,
),
)
.addChoices(
...TANK_CLASSES.map(
(tankType) =>
({
value: `${tankType}`,
name: translate(`common.tank_class_short.${tankType}`),
name: strings.common.tank_class_short[tankType],
name_localizations: localizationObject(
`common.tank_class_short.${tankType}`,
(strings) => strings.common.tank_class_short[tankType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -84,28 +90,28 @@ export function addFilterOptions<
)
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.type'))
.setName(strings.bot.common.options.filters.type.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.type',
(strings) => strings.bot.common.options.filters.type.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.type.description'),
)
.setDescription(strings.bot.common.options.filters.type.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.type.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.type.description,
),
)
.addChoices(
...TANK_TYPES.map(
(treeType) =>
({
value: TANK_TYPE_COMMAND_NAMES[treeType],
name: translate(`common.tree_type.${treeType}`),
name: strings.common.tree_type[treeType],
name_localizations: localizationObject(
`common.tree_type.${treeType}`,
(strings) => strings.common.tree_type[treeType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -16,39 +16,67 @@ export function addPeriodSubCommands(
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
const subcommandNameLocalizations = {
today: localizationObject('bot.common.subcommands.today', undefined, true),
30: localizationObject('bot.common.subcommands.30', undefined, true),
60: localizationObject('bot.common.subcommands.60', undefined, true),
90: localizationObject('bot.common.subcommands.90', undefined, true),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.name,
undefined,
true,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].name,
undefined,
true,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].name,
undefined,
true,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].name,
undefined,
true,
),
career: localizationObject(
'bot.common.subcommands.career',
(strings) => strings.bot.common.subcommands.career.name,
undefined,
true,
),
custom: localizationObject(
'bot.common.subcommands.custom',
(strings) => strings.bot.common.subcommands.custom.name,
undefined,
true,
),
};
const subcommandDescriptionLocalizations = {
today: localizationObject('bot.common.subcommands.today.description'),
30: localizationObject('bot.common.subcommands.30.description'),
60: localizationObject('bot.common.subcommands.60.description'),
90: localizationObject('bot.common.subcommands.90.description'),
career: localizationObject('bot.common.subcommands.career.description'),
custom: localizationObject('bot.common.subcommands.custom.description'),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.description,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].description,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].description,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].description,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.description,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.description,
),
};
option
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.today'))
.setName(strings.bot.common.subcommands.today.name)
.setNameLocalizations(subcommandNameLocalizations.today)
.setDescription(translate('bot.common.subcommands.today.description'))
.setDescription(strings.bot.common.subcommands.today.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.today,
),
@@ -57,38 +85,36 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.30'))
.setName(strings.bot.common.subcommands[30].name)
.setNameLocalizations(subcommandNameLocalizations[30])
.setDescription(translate('bot.common.subcommands.30.description'))
.setDescription(strings.bot.common.subcommands[30].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[30]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.60'))
.setName(strings.bot.common.subcommands[60].name)
.setNameLocalizations(subcommandNameLocalizations[60])
.setDescription(translate('bot.common.subcommands.60.description'))
.setDescription(strings.bot.common.subcommands[60].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[60]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.90'))
.setName(strings.bot.common.subcommands[90].name)
.setNameLocalizations(subcommandNameLocalizations[90])
.setDescription(translate('bot.common.subcommands.90.description'))
.setDescription(strings.bot.common.subcommands[90].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[90]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.career'))
.setName(strings.bot.common.subcommands.career.name)
.setNameLocalizations(subcommandNameLocalizations.career)
.setDescription(
translate('bot.common.subcommands.career.description'),
)
.setDescription(strings.bot.common.subcommands.career.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.career,
),
@@ -97,31 +123,25 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.custom'))
.setName(strings.bot.common.subcommands.custom.name)
.setNameLocalizations(subcommandNameLocalizations.custom)
.setDescription(
translate('bot.common.subcommands.custom.description'),
)
.setDescription(strings.bot.common.subcommands.custom.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.custom,
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.start'))
.setName(strings.bot.common.subcommands.custom.options.start.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.start.description',
),
strings.bot.common.subcommands.custom.options.start.description,
)
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.end'))
.setName(strings.bot.common.subcommands.custom.options.end.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.end.description',
),
strings.bot.common.subcommands.custom.options.end.description,
)
.setRequired(true),
),
@@ -8,25 +8,31 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addRegionChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.region`)
.setName(strings.bot.common.options.region.name)
.setNameLocalizations(
localizationObject('bot.common.options.region', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.region.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.region.description`)
.setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.region.description'),
localizationObject(
(strings) => strings.bot.common.options.region.description,
),
)
.addChoices(
...REGIONS.map(
(region) =>
({
value: region,
name: translate(`common.regions.normal.${region}`),
name: strings.common.regions.normal[region],
name_localizations: localizationObject(
`common.regions.normal.${region}`,
(strings) => strings.common.regions.normal[region],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTankChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(translate('bot.common.options.tank'))
.setName(strings.bot.common.options.tank.name)
.setNameLocalizations(
localizationObject('bot.common.options.tank', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tank.name,
undefined,
true,
),
)
.setDescription(translate('bot.common.options.tank.description'))
.setDescription(strings.bot.common.options.tank.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tank.description'),
localizationObject(
(strings) => strings.bot.common.options.tank.description,
),
)
.setAutocomplete(true)
.setRequired(true);
@@ -1,4 +1,5 @@
import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -9,28 +10,34 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTierChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.tier`)
.setName(strings.bot.common.options.tier.name)
.setNameLocalizations(
localizationObject('bot.common.options.tier', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tier.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.tier.description`)
.setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tier.description'),
localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
)
.setChoices(
...range(10, 0).map(
(tier) =>
({
name: translate('bot.common.options.tier.choices.tier', [
name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`,
numberToRomanNumeral(tier),
]),
value: `${tier}`,
name_localizations: localizationObject(
'bot.common.options.tier.choices.tier',
(strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)],
),
}) satisfies APIApplicationCommandOptionChoice,
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addUsernameChoices(option: SlashCommandStringOption) {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.username`)
.setName(strings.bot.common.options.username.name)
.setNameLocalizations(
localizationObject('bot.common.options.username', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.username.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.username.description`)
.setDescription(strings.bot.common.options.username.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.username.description'),
localizationObject(
(strings) => strings.bot.common.options.username.description,
),
)
.setAutocomplete(true);
}
@@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants';
export async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]);
@@ -19,9 +19,7 @@ export async function autocompleteClan(
await interaction.respond(
clans
? clans.map((clan) => {
let name = `${clan.name} (${translate(
`common.regions.short.${clan.region}`,
)})`;
let name = `${clan.name} (${strings.common.regions.short[clan.region]})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -29,9 +27,7 @@ export async function autocompleteClan(
name = `${clan.name.slice(
0,
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${clan.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${strings.common.regions.short[clan.region]})`;
}
return {
@@ -1,15 +1,16 @@
import { fetchTankNames, TankType } from '@blitzkit/core';
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { go } from 'fuzzysort';
import { tankNames } from '../blitzkit/nonBlockingPromises';
import { translator } from '../localization/translator';
import {
DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX,
} from './autocompleteClan/constants';
import { tankNames } from '../blitzkit/nonBlockingPromises';
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
@@ -20,6 +21,7 @@ export async function autocompleteTanks(
techTreeOnly = false,
fields = ['tank'],
) {
const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (!fields.includes(focusedOption.name)) return;
@@ -29,19 +31,16 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 10,
},
{ keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => {
let name = item.obj.searchableName;
let name = unwrap(item.obj.searchableName);
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
name = `${item.obj.searchableName.slice(
name = `${unwrap(item.obj.searchableName).slice(
0,
item.obj.searchableName.length -
unwrap(item.obj.searchableName).length -
overSize -
OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX}`;
@@ -13,7 +13,7 @@ import {
export async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value);
@@ -22,9 +22,9 @@ export async function autocompleteUsername(
await interaction.respond(
players
? players.map((player) => {
let name = `${player.nickname} (${translate(
`common.regions.short.${player.region}`,
)})`;
let name = `${player.nickname} (${
strings.common.regions.short[player.region]
})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -32,9 +32,9 @@ export async function autocompleteUsername(
name = `${player.nickname.slice(
0,
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${player.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${
strings.common.regions.short[player.region]
})`;
}
return {
@@ -6,13 +6,13 @@ export function buttonRefresh(
interaction: ChatInputCommandInteraction,
path: string,
) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (
interaction.appPermissions?.has('ReadMessageHistory') &&
interaction.appPermissions?.has('ViewChannel')
) {
return buttonPrimary(path, t`bot.common.buttons.refresh`);
return buttonPrimary(path, strings.bot.common.buttons.refresh);
}
return null;
@@ -1,3 +1,4 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import {
Locale,
SlashCommandBuilder,
@@ -23,28 +24,47 @@ export function createLocalizedCommand(
| CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[],
) {
const commandPathItem = command.replaceAll('-', '_');
const { translate } = translator(Locale.EnglishUS);
const commandPathItem = command.replaceAll(
'-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder()
.setName(command)
.setNameLocalizations(
localizationObject(`bot.commands.${commandPathItem}`, undefined, true),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].name,
undefined,
true,
),
)
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
.setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations(
localizationObject(`bot.commands.${commandPathItem}.description`),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
);
if (sub) {
sub.map((subItem) => {
if ('subcommand' in subItem) {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { name: string }>;
}
).subcommands[subItem.subcommand].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
);
slashCommand.addSubcommand((option) => {
@@ -54,20 +74,32 @@ export function createLocalizedCommand(
.setName(subItem.subcommand)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
} else {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { name: string }>;
}
).groups[subItem.group].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
);
slashCommand.addSubcommandGroup((option) => {
@@ -77,9 +109,11 @@ export function createLocalizedCommand(
.setName(subItem.group)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -1,10 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands';
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { translate } = translator(locale);
if (period === 'career') return translate('bot.common.periods.career');
if (period === 'today') return translate('bot.common.periods.today');
return translate('bot.common.periods.days', [period]);
const { strings } = translator(locale);
if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, [period]);
}
@@ -1,19 +1,22 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
const validNameRegex =
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
export function localizationObject(
path: string,
literals?: string[],
path: (strings: BlitzKitStrings) => string,
literals: string[] = [],
strict = false,
) {
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => {
const { translate } = translator(locale);
const translation = translate(path, literals);
const isValid = !strict || validNameRegex.test(translation);
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid =
(!strict || validNameRegex.test(translation)) && translation.length > 0;
if (!isValid) {
console.warn(
@@ -1,4 +1,5 @@
import { Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
@@ -10,7 +11,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export async function resolveClanFromCommand(
interaction: ChatInputCommandInteraction,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const clan = interaction.options.getString('clan', true);
if (serverAndIdPattern.test(clan)) {
@@ -26,7 +27,9 @@ export async function resolveClanFromCommand(
return { region: accounts[0].region, id: accounts[0].clan_id };
} else {
throw new UserError(
translate('bot.common.errors.clan_not_found', [markdownEscape(clan)]),
literals(strings.bot.common.errors.clan_not_found, [
markdownEscape(clan),
]),
);
}
}
@@ -1,4 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { getPeriodNow } from '../blitzkit/getPeriodNow';
import { getPeriodStart } from '../blitzkit/getPeriodStart';
@@ -17,7 +18,7 @@ export function resolvePeriodFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
forcedPeriod?: PeriodType,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
let name: string;
let start: number;
let end: number;
@@ -31,7 +32,7 @@ export function resolvePeriodFromCommand(
const startDaysAgoMin = Math.min(startOption, endOption);
const endDaysAgoMax = Math.max(startOption, endOption);
name = translate('bot.common.periods.custom', [
name = literals(strings.bot.common.periods.custom, [
`${startDaysAgoMin}`,
`${endDaysAgoMax}`,
]);
@@ -1,4 +1,5 @@
import { idToRegion, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
@@ -14,7 +15,7 @@ export interface ResolvedPlayer {
export async function resolvePlayerFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const commandUsername = interaction.options.getString('username');
if (commandUsername !== null) {
@@ -36,7 +37,7 @@ export async function resolvePlayerFromCommand(
} satisfies ResolvedPlayer;
} else {
throw new UserError(
translate('bot.common.errors.player_not_found', [
literals(strings.bot.common.errors.player_not_found, [
markdownEscape(commandUsername),
]),
);
@@ -47,6 +48,6 @@ export async function resolvePlayerFromCommand(
if (account) return account;
throw new UserError(t`bot.common.errors.player_not_linked`);
throw new UserError(strings.bot.common.errors.player_not_linked);
}
}
@@ -1,14 +1,16 @@
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/core';
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
en: Locale.EnglishUS,
es: Locale.SpanishES,
hr: Locale.Croatian,
pt: Locale.PortugueseBR,
ru: Locale.Russian,
uk: Locale.Ukrainian,
zh: Locale.ChineseCN,
fr: Locale.French,
ja: Locale.Japanese,
pl: Locale.Polish,
};
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
@@ -1,24 +0,0 @@
import { Locale } from 'discord.js';
import {
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './constants';
export type TranslationFragmentTree = {
[key: string]: TranslationFragment;
} & {
$?: string;
};
export type TranslationFragment = string | TranslationFragmentTree;
export const translations = SUPPORTED_LOCALES_DISCORD.reduce<
Partial<Record<Locale, TranslationFragment>>
>(
(table, locale) => ({
...table,
[locale]: require(
`../../../../../core/lang/${SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]}.json`,
),
}),
{},
);
@@ -1,8 +1,9 @@
import { STRINGS, unwrapper } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translations } from './strings';
import {
DEFAULT_LOCALE_DISCORD,
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './strings/constants';
export function translator(localeRaw: Locale) {
@@ -11,68 +12,9 @@ export function translator(localeRaw: Locale) {
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: DEFAULT_LOCALE_DISCORD;
const strings = translations[locale]!;
const bkLocale = SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale];
const strings = STRINGS[bkLocale];
const unwrap = unwrapper(bkLocale);
function translate(path: string, literals?: string[]): string {
const pathArray = path.split('.');
let fragment = strings;
let resolvedString: string;
for (const pathItem of pathArray) {
if (typeof fragment === 'string') {
throw new SyntaxError(
`Attempted to access string with key "${pathItem}" in "${path}" from within a string instead of an object in locale "${locale}"`,
);
} else {
fragment = fragment[pathItem];
if (typeof fragment === 'undefined') {
const message = `Undefined translation at "${pathItem}" in "${path}" for locale "${locale}"`;
if (locale === DEFAULT_LOCALE_DISCORD) {
throw new Error(message);
} else {
console.warn(`${message} falling back to en-US`);
return translator(DEFAULT_LOCALE_DISCORD).translate(path, literals);
}
}
}
}
if (typeof fragment === 'string') {
resolvedString = fragment;
} else if (fragment.$) {
resolvedString = fragment.$!;
} else {
throw new Error(
`Unresolved tree ending for "${path}" in locale "${locale}"`,
);
}
if (literals) {
const chunks = resolvedString.split('%s');
let embeddedString = '';
chunks.forEach((chunk, index) => {
if (index !== 0) embeddedString += literals[index - 1];
embeddedString += chunk;
});
return embeddedString;
}
return resolvedString;
}
function t(paths: TemplateStringsArray, ...embeds: string[]) {
return paths
.map((path, index) =>
path.length === 0
? ''
: `${translate(path)}${index === embeds.length ? '' : embeds[index]}`,
)
.join('');
}
return { locale, translate, t };
return { locale, strings, unwrap, bkLocale };
}
@@ -8,22 +8,22 @@ import {
InteractionReplyOptions,
} from 'discord.js';
import { InteractionRawReturnable, commands } from '..';
import { UserError } from '../../../core/blitzkit/userError';
import { buttonLink } from '../../../core/discord/buttonLink';
import { embedWarning } from '../../../core/discord/embedWarning';
import { normalizeInteractionReturnable } from '../../../core/discord/normalizeInteractionReturnable';
import { psa } from '../../../core/discord/psa';
import { translator } from '../../../core/localization/translator';
import { Writeable } from '../../../types/writable';
import { UserError } from '../../../core/blitzkit/userError';
export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
await interaction.deferReply();
const awaitedCommands = await commands;
const registry = awaitedCommands[interaction.commandName];
await interaction.deferReply();
try {
const returnable = await registry.handler(interaction);
@@ -126,16 +126,16 @@ export async function handleChatInputCommand(
index++;
}
} catch (error) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const components = [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel(t`bot.common.errors.get_help`)
.setLabel(strings.bot.common.errors.get_help)
.setURL('https://discord.gg/nDt7AjGJQH')
.setStyle(ButtonStyle.Link),
),
];
if (error instanceof UserError) {
interaction.editReply({
content: error.message,
@@ -143,9 +143,8 @@ export async function handleChatInputCommand(
});
} else {
console.error(interaction.commandName, error);
interaction.editReply({
content: t`bot.common.errors.uncaught_error`,
content: strings.bot.common.errors.uncaught_error,
components,
});
}
@@ -15,7 +15,6 @@ import {
SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js';
import { aboutCommand } from '../../commands/about';
import { auctionCommand } from '../../commands/auction';
import { breakdownCommand } from '../../commands/breakdown';
import { debugCommand } from '../../commands/debug';
import { evolutionCommand } from '../../commands/evolution';
@@ -64,20 +63,23 @@ interface CommandRegistryBase {
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
}
interface CommandRegistryImplicit extends CommandRegistryBase {
handlesInteraction: true;
handler: (interaction: ChatInputCommandInteraction<CacheType>) => void;
}
interface CommandRegistryExplicit extends CommandRegistryBase {
handlesInteraction?: false;
handler: (
interaction: ChatInputCommandInteraction<CacheType>,
) => InteractionReturnable;
}
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
auctionCommand,
// auctionCommand,
permissionsCommand,
debugCommand,
aboutCommand,
@@ -107,10 +109,8 @@ export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
`Command ${index} failed to load; skipping...`,
registry.reason,
);
return commands;
}
return { ...commands, [registry.value.command.name]: registry.value };
},
{},
+8 -9
View File
@@ -1,10 +1,3 @@
// import { assertSecret, EventManager } from '@blitzkit/core';
// import { ActivityType, ShardingManager } from 'discord.js';
// import {
// discoveredIdsDefinitions,
// tankDefinitions,
// } from './core/blitzkit/nonBlockingPromises';
import { assertSecret } from '@blitzkit/core';
import { ActivityType, ShardingManager } from 'discord.js';
import {
@@ -36,7 +29,6 @@ const interval = setInterval(async () => {
let users = 0;
for (const [, shard] of shards) {
// wtf is this typing bro???
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
servers += guilds.length;
@@ -53,7 +45,7 @@ const interval = setInterval(async () => {
const usernames = awaitedDiscoveredIdsDefinitions.count;
manager.broadcastEval(
(client, { servers, users, channels, tanks, usernames }) => {
async (client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
`Serving ${users.toLocaleString()} users`,
@@ -70,6 +62,13 @@ const interval = setInterval(async () => {
},
],
});
for (const guild of client.guilds.cache.values()) {
try {
const botMember = await guild.members.fetch(client.user.id);
await botMember.setNickname('BlitzKit');
} catch {}
}
},
{ context: { servers, channels, users, tanks, usernames } },
);
+1 -1
View File
@@ -23,7 +23,7 @@ export const theme = {
spaces: createSpaces(),
colors: {
...createColors(slateDark),
...createColors(mauveDark),
...createColors(slateDarkA, 'alpha'),
...createColors(slate, 'light'),
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*", "../core/src/blitzkit/supportedLocales.ts"]
"include": ["src/**/*", "../core/src/blitzkit/supportedLocales.ts", "../i18n/src/unwrapper.ts"]
}
-683
View File
@@ -1,683 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Muy malo",
"1": "Malo",
"2": "Inferior al promedio",
"3": "Promedio",
"4": "Superior al promedio",
"5": "Bueno",
"6": "Muy bueno",
"7": "Genial",
"8": "Unicum",
"9": "Súper unicum"
},
"bkni_percentile": {
"0": "Súper malum",
"1": "Malum",
"2": "Muy malo",
"3": "Malo",
"4": "Inferior al promedio",
"5": "Promedio",
"6": "Superior al promedio",
"7": "Bueno",
"8": "Muy bueno",
"9": "Unicum",
"10": "Súper unicum"
},
"nations": {
"ussr": "URSS",
"germany": "Alemania",
"usa": "EE. UU.",
"china": "China",
"uk": "R. U.",
"france": "Francia",
"japan": "Japón",
"european": "Nación europea",
"other": "Nación híbrida"
},
"nations_adjectives": {
"ussr": "URSS",
"germany": "Alemán",
"usa": "EE. UU.",
"china": "Chino",
"uk": "R. U.",
"france": "Francés",
"japan": "Japonés",
"european": "Europeo",
"other": "Híbrido"
},
"tank_class_short": {
"-1": "no reconocido",
"0": "Ligero",
"1": "Medio",
"2": "Pesado",
"3": "Cazacarro"
},
"tank_class_short_plural_lowercase": {
"0": "ligeros",
"1": "medios",
"2": "pesados",
"3": "Cazacarros"
},
"tank_class_medium": {
"-1": "no reconocido",
"0": "Ligero",
"1": "Medio",
"2": "Pesado",
"3": "Cazacarro"
},
"tree_type": {
"-1": "no reconocido",
"0": "Árbol tecnológico",
"1": "Prémium",
"2": "Coleccionista"
},
"regions": {
"normal": {
"com": "Norteamérica",
"eu": "Europa",
"asia": "Asia-Pacífico"
},
"short": {
"com": "NA",
"eu": "Europa",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamante",
"platinum": "Platino",
"gold": "Oro",
"silver": "Plata",
"bronze": "Bronce"
},
"composite_stats": {
"normalized_damage_dealt": "Daño",
"normalized_damage_received": "Daño recibido",
"normalized_deaths": "Tasa de muertes",
"normalized_frags": "Bajas",
"normalized_hits": "Aciertos",
"normalized_losses": "Tasa de derrotas",
"normalized_misses": "Fallos",
"normalized_shots": "Disparos",
"normalized_spotted": "Disparos",
"normalized_survived_battles": "Tasa de supervivencia",
"normalized_win_and_survived": "Tasa de victorias y supervivencia",
"normalized_wins": "Tasa de victorias",
"normalized_xp": "EXP",
"normalized_battle_life_time": "Tiempo de batalla",
"cumulative_accuracy": "Precisión",
"cumulative_battles": "Batallas",
"cumulative_capture_points": "Puntos de captura totales",
"cumulative_damage_dealt": "Daño total",
"cumulative_damage_ratio": "Relación de daño",
"cumulative_damage_received": "Daño total recibido",
"cumulative_deaths": "Muertes totales",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Bajas totales",
"cumulative_hits": "Aciertos totales",
"cumulative_inaccuracy": "Imprecisión",
"cumulative_kills_to_death_ratio": "Relación bajas-muertes",
"cumulative_losses": "Derrotas",
"cumulative_misses": "Fallos totales",
"cumulative_shots": "Disparos totales",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Batallas sobrevividas",
"cumulative_win_and_survived": "Batallas ganadas y sobrevividas",
"cumulative_wins": "Victorias",
"cumulative_xp": "EXP total",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Tiempo total de batalla"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# No se pudo encontrar el clan\nNo se pudo encontrar \"%s\". Prueba seleccionando un clan de los resultados de búsqueda.",
"player_not_found": "# No se pudo encontrar el usuario\nNo pude encontrar al usuario \"%s\". Prueba eligiendo un usuario de los resultados de búsqueda, escribiendo un nombre de usuario aceptable o usando el comando `/vincular`.",
"player_not_linked": "# Usa `/vincular` para empezar\n¡No sé quién eres, pero puedes decírmelo hoy!",
"tank_name_not_found": "# No se pudo encontrar el tanque\nNo se pudo encontrar el tanque con el nombre \"%s\".",
"tank_id_not_found": "# Tanque no encontrado\nNo se pudo encontrar al tanque del ID \"%s\". Prueba escribiendo el nombre del tanque en vez de un número.",
"no_tank_stats": "# No hay estadísticas de tanque disponibles\nEste jugador no tiene estadísticas disponibles. Este podría no ser el jugador que estás buscando.",
"get_help": "Servidor de ayuda de Discord",
"uncaught_error": "# ¡Blitzkrieg sufrió un error!\nLo sentimos mucho. Siéntete libre de [unirte al servidor oficial de Discord](https://discord.gg/nDt7AjGJQH) para obtener ayuda.",
"preview": "# Este comando es una vista previa\n\nSolo puedes acceder a este comando en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH). Generalmente este comando estará disponible pronto.",
"query_too_small": "# Tu búsqueda es demasiado corta\nDebe ser por lo menos de 3 caracteres de largo.",
"query_too_long": "# Tu búsqueda es demasiado larga\nDebe ser como mucho de 100 caracteres de largo."
},
"wn8": {
"$": "WN8",
"very_bad": "Muy malo",
"bad": "Malo",
"below_average": "Inferior al promedio",
"average": "Promedio",
"above_average": "Superior al promedio",
"good": "Bueno",
"very_good": "Muy bueno",
"great": "Genial",
"unicum": "Unicum",
"super_unicum": "Súper unicum"
},
"subcommands": {
"today": {
"$": "hoy",
"description": "Hoy"
},
"30": {
"$": "30",
"description": "30 días"
},
"60": {
"$": "60",
"description": "60 días"
},
"90": {
"$": "90",
"description": "90 días"
},
"career": {
"$": "carrera",
"description": "Carrera"
},
"custom": {
"$": "personalizado",
"description": "Rango personalizado",
"options": {
"start": {
"$": "comienzo",
"description": "Comenzar en cantidad de días pasados desde hoy"
},
"end": {
"$": "fin",
"description": "Terminar en cantidad de días pasados desde hoy"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "El nombre o etiqueta del clan"
},
"tank": {
"$": "tanque",
"description": "El tanque al cual conseguirle estadísticas"
},
"username": {
"$": "usuario",
"description": "El nombre de usuario de Blitz"
},
"filters": {
"nation": {
"$": "nación",
"description": "Nación"
},
"class": {
"$": "clase",
"description": "Clase"
},
"type": {
"$": "tipo",
"description": "Tipo"
}
},
"region": {
"$": "región",
"description": "Región"
},
"tier": {
"$": "nivel",
"description": "Nivel",
"choices": {
"tier": "Nivel %s - %s"
}
}
},
"periods": {
"career": "Carrera",
"today": "Hoy",
"days": "%s días",
"custom": "%s - %s días"
},
"filters": {
"none": "Sin filtros",
"tier": "Nivel %s"
},
"no_data": {
"battles_in_period": "No hay batallas jugadas en este período",
"battles_in_season": "No hay batallas jugadas en esta temporada",
"players_in_period": "No hay jugadores en este período",
"tanks_found": "No se encontraron tanques"
},
"buttons": {
"refresh": "Actualizar"
},
"hero_stats": {
"battles": "Batallas",
"winrate": "Tasa de victorias",
"damage": "Daño",
"damage_ratio": "Relación de daño",
"tier": "Nivel",
"survival": "Supervivencia",
"accuracy": "Precisión",
"kills": "Bajas"
}
},
"commands": {
"auction": {
"$": "subasta",
"description": "Tanques disponibles ahora mismo en la subasta",
"body": {
"title": "Subasta Blitz",
"subtitle": "%s disponible • siguientes precios %s\nPujar ahora: %s",
"next": "Siguiente precio: %s",
"available": "Disponible: %s / %s"
},
"errors": {
"no_auction": "# No hay ninguna subasta\nActualmente no hay tanques disponibles para comprar en la subasta. Si crees que esto es un error, por favor, reporta esta discrepancia en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Pujar ahora"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "investigar",
"description": "Enumera los gastos por investigar una línea",
"options": {
"target_tank": {
"$": "tanque-objetivo",
"description": "El último tanque a investigar en la línea"
},
"starting_tank": {
"$": "tanque-inicial",
"description": "El tanque inicial en la línea (se deduce automáticamente si no se proporciona)"
}
},
"body": {
"subtitle": "Investigar",
"total": "Total",
"research": "Investigar: %s",
"upgrades": "Mejoras: %s",
"purchase": "Comprar: %s",
"equipment": "Equipamiento: %s",
"estimation": "# ¡Esto es una estimación!\n\nEsta lista podría incluir tanques ya investigados debido a la información errónea que proporciona Wargaming. Si puedes, prueba proporcionando un `tanque-inicial` de la línea al usar el comando."
},
"errors": {
"non_tech_tree": "# %s no está en el árbol tecnológico\n\nNo puedes investigar un tanque prémium o coleccionista.",
"tier_1": "# %s es un nivel I\n\nLos tanque de nivel I están siempre desbloqueados gratuitamente para todos los jugadores.",
"already_researched": "# %s ya está investigado\n\nPrueba proporcionando un `tanque-inicial` al usar el comando para ver los totales para una línea que ya has investigado.",
"start_end_equal": "# %s fue proporcionado dos veces\n\nAsegúrate de que `tanque-inicial` y `tanque-objetivo` son diferentes.",
"tanks_not_on_line": "# %s y %s no están en la misma línea\n\nAsegúrate de que `tanque-inicial` y `tanque-objetivo` están en la misma línea del árbol tecnológico.",
"unordered": "# %s de nivel superior a %s\n\nAsegúrate de proporcionar %s como el `tanque-inicial` en su lugar ya que es el tanque de nivel inferior."
}
},
"about": {
"$": "sobre",
"description": "Toda la información que necesitas sobre el bot",
"subcommands": {
"commands": {
"$": "comandos",
"description": "Lista de todos los comandos y sus descripciones",
"body": "# Comandos\n\nBlitzkrieg ofrece los siguientes comandos:\n\n"
},
"embeds": {
"$": "embeds",
"description": "Cómo funcionan los embeds"
},
"introduction": {
"$": "introducción",
"description": "Sobre el bot"
},
"invite": {
"$": "invitar",
"description": "Cómo invitar al bot"
},
"timezones": {
"$": "zonas-horarias",
"description": "Cómo Blitzkrieg infiere y utiliza tu zona horaria"
},
"credits": {
"$": "créditos",
"description": "Personas que han ayudado a BlitzKit"
}
}
},
"breakdown": {
"$": "resumen",
"description": "Resumen de un período por tanques jugados",
"body": {
"total": "Total",
"battles": "Batallas",
"wn8": "WN8",
"winrate": "Tasa de victorias",
"damage": "Daño",
"unknown_tank": "Tanque desconocido %s"
}
},
"debug": {
"$": "depurar",
"description": "Muestra más información sobre el bot",
"body": {
"title": "# Depuración\n\n",
"version": "Versión",
"shard": {
"$": "Partición",
"default": "predeterminado"
},
"uptime": "Tiempo en línea"
}
},
"evolution": {
"$": "evolución",
"description": "Evolución de estadísticas",
"groups": {
"player": {
"$": "jugador",
"description": "Estadísticas del jugador"
},
"tank": {
"$": "tanque",
"description": "Estadísticas del tanque"
}
}
},
"inactive": {
"$": "inactivo",
"description": "Enumera a todos los jugadores inactivos en un clan por encima de un umbral",
"options": {
"threshold": {
"$": "umbral",
"description": "El número de días inactivo (predeterminado: %s días)"
}
},
"body": {
"subtitle": "Inactivo por %s+ días",
"listing": "%s días"
}
},
"full_stats": {
"$": "estadísticas-completas",
"description": "Estadísticas completas del juego",
"body": {
"cards": {
"games": {
"$": "Partidas",
"winrate": "Tasa de victorias",
"battles": "Batallas",
"wins_losses": "Victorias / derrotas"
},
"efficiency": {
"$": "Eficiencia",
"wn8": "WN8",
"survival": "Supervivencia",
"accuracy": "Precisión"
},
"lethality": {
"$": "Letalidad",
"average_damage": "Daño promedio",
"damage_per_tier": "Daño por nivel",
"damage_ratio": "Relación de daño",
"average_kills": "Promedio de bajas"
},
"miscellaneous": {
"$": "Misceláneo",
"average_tier": "Nivel promedio",
"average_spots": "Promedio de detecciones",
"average_xp": "Promedio de XP",
"kills_to_death_ratio": "Relación bajas-muertes"
}
}
}
},
"link": {
"$": "vincular",
"description": "Vincula tu cuenta de Blitz y Discord",
"body": {
"accounts_linked": "Cuentas vinculadas",
"no_role_permissions": {
"$": "%s no se pudo verificar",
"description": "No tengo permiso para administrar roles."
}
},
"embed": {
"title": "¿Cuenta incorrecta?",
"description": "Usa el comando de nuevo y selecciona un usuario de los resultados de búsqueda."
}
},
"search_clans": {
"$": "buscar-clanes",
"description": "Buscar clanes",
"options": {
"limit": {
"$": "límite",
"description": "El número máximo de resultados de clanes (predeterminado: %s)"
}
},
"body": {
"title": "Búsqueda del clan %s en la región %s",
"no_results": "No se encontraron clanes"
}
},
"search_players": {
"$": "buscar-jugadores",
"description": "Buscar jugadores",
"options": {
"limit": {
"$": "límite",
"description": "El número máximo para resultados de jugador (predeterminado: %s)"
}
},
"body": {
"title": "Búsqueda del jugador %s en la región %s",
"no_results": "No se encontraron jugadores"
}
},
"rating_leaderboard": {
"$": "clasificatoria-tabla",
"description": "Estadísticas de batallas de clasificatoria",
"subcommands": {
"neighbors": {
"$": "vecinos",
"description": "Jugadores vecinos en la tabla de posiciones"
},
"league": {
"$": "liga",
"description": "Mejores jugadores de una liga"
}
},
"options": {
"limit": {
"$": "límite",
"description": "Cuántos vecinos a mostrar (predeterminado: %s)"
},
"season": {
"$": "temporada",
"description": "Número de temporada (predeterminado: actual)",
"choices": {
"current": "%s (actual)"
}
},
"league": {
"$": "liga",
"description": "La liga a mostrar"
}
},
"body": {
"top_players": "Mejores %s jugadores",
"no_participation": "# %s no jugó clasificatoria en la temporada %s\n\nEste jugador no participó en esta temporada o no pasó de la calibración.",
"no_ongoing_season": "# No hay temporada en curso\n\nActualmente no hay ninguna temporada de clasificatoria.",
"deleted_player": "%s eliminado",
"neighbors": "Vecinos de clasificatoria",
"more": "+ %s más"
}
},
"today": {
"$": "hoy",
"description": "Tanques jugados hoy y estadísticas"
},
"stats": {
"$": "estadísticas",
"description": "Estadísticas rápidas para un período"
},
"owned_tanks": {
"$": "tanques-propios",
"description": "Muestra todos los tanques propios en un nivel",
"body": {
"title": "# Tanques de %s de nivel %s\n\n"
}
},
"permissions": {
"$": "permisos",
"description": "Comprueba si el bot tiene los permisos requeridos para funcionar",
"body": {
"title": "# Permisos\n\n%s",
"view_channels": "Ver canales: necesario para encontrar el mensaje en el cual el botón \"actualizar\" fue usado",
"read_message_history": "Leer historial de mensajes: necesario para encontrar el mensaje a actualizar",
"attach_files": "Adjuntar archivos: necesario para añadir imágenes a los comandos basados en estas",
"external_emojis": "Emoticonos externos: necesario para añadir emoticonos de banderas nacionales y clases de tanque"
}
},
"ping": {
"$": "ping",
"description": "Comprueba si el bot está vivo.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Hacerle ping a BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Hacerle un ping a World of Tanks Blitz"
}
}
},
"player_info": {
"$": "info-jugador",
"description": "Información básica sobre un jugador",
"body": {
"title": "Información de %s",
"nickname": "Apodo",
"battles": "Batallas",
"winrate": "Tasa de victorias",
"account_id": "ID de cuenta",
"created": "Creado",
"last_battle": "Última batalla"
}
},
"rating": {
"$": "clasificatoria",
"description": "Estadísticas de clasificatoria del jugador",
"subcommands": {
"today": {
"$": "hoy",
"description": "Estadísticas de clasificatoria de hoy"
},
"season": {
"$": "temporada",
"description": "Estadísticas de clasificatoria de esta temporada"
}
},
"body": {
"subtitle": {
"today": "Estadísticas de clasificatoria de hoy",
"season": "Estadísticas de clasificatoria de esta temporada"
}
},
"errors": {
"no_participation": "# %s no ha jugado clasificatoria\n\nEste jugador no ha participado en el modo clasificatoria esta temporada.",
"unrecorded_stats": "# Estadísticas no disponibles actualmente\n\nWargaming podría estar sufriendo una caída del servidor. Si hay alguna temporada en curso, las estadísticas deberían estar disponibles en 24 horas."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Очень плохо",
"1": "Плохо",
"2": "Ниже среднего",
"3": "Средне",
"4": "Выше среднего",
"5": "Хорошо",
"6": "Очень хорошо",
"7": "Отлично",
"8": "Уникально",
"9": "Крайне уникально"
},
"bkni_percentile": {
"0": "Крайне ужасно",
"1": "Ужасно",
"2": "Очень плохо",
"3": "Плохо",
"4": "Ниже среднего",
"5": "Средне",
"6": "Выше среднего",
"7": "Хорошо",
"8": "Очень хорошо",
"9": "Уникально",
"10": "Крайне уникально"
},
"nations": {
"ussr": "СССР",
"germany": "Германия",
"usa": "США",
"china": "Китай",
"uk": "Великобритания",
"france": "Франция",
"japan": "Япония",
"european": "Сборная Европы",
"other": "Сборная нация"
},
"nations_adjectives": {
"ussr": "Советский",
"germany": "Немецкий",
"usa": "Американский",
"china": "Китайский",
"uk": "Великобританский",
"france": "Французский",
"japan": "Японский",
"european": "Европеуский",
"other": "Сборной нации"
},
"tank_class_short": {
"-1": "неизвестно",
"0": "Лёгкий",
"1": "Средний",
"2": "Тяжёлый",
"3": "ПТ"
},
"tank_class_short_plural_lowercase": {
"0": "лёгкие",
"1": "средние",
"2": "тяжёлые",
"3": "ПТ"
},
"tank_class_medium": {
"-1": "неизвестно",
"0": "Лёгкий",
"1": "Средний",
"2": "Тяжёлый",
"3": "ПТ"
},
"tree_type": {
"-1": "неизвестно",
"0": "Дерево танков",
"1": "Премиальный",
"2": "Коллекционный"
},
"regions": {
"normal": {
"com": "Северная Америка",
"eu": "Европа",
"asia": "Азиатско-Тихоокеанский"
},
"short": {
"com": "СА",
"eu": "ЕС",
"asia": "АТР"
}
},
"leagues": {
"diamond": "Алмазная",
"platinum": "Платиновая",
"gold": "Золотая",
"silver": "Серебряная",
"bronze": "Бронзовая"
},
"composite_stats": {
"normalized_damage_dealt": "Урон",
"normalized_damage_received": "Полученный урон",
"normalized_deaths": "Коэффициент смертей",
"normalized_frags": "Уничтожил",
"normalized_hits": "Уничтожен",
"normalized_losses": "Коэффициент поражений",
"normalized_misses": "Промахов",
"normalized_shots": "Выстрелов",
"normalized_spotted": "Попаданий",
"normalized_survived_battles": "Коэффициент выживания",
"normalized_win_and_survived": "Коэффициент побед и выживания",
"normalized_wins": "Коэффициент побед",
"normalized_xp": "Опыт",
"normalized_battle_life_time": "Время в бою",
"cumulative_accuracy": "Точность стрельбы",
"cumulative_battles": "Боёв",
"cumulative_capture_points": "Всего очков захвата",
"cumulative_damage_dealt": "Всего урона",
"cumulative_damage_ratio": "Коэффициент урона",
"cumulative_damage_received": "Всего получено урона",
"cumulative_deaths": "Всего смертей",
"cumulative_dropped_capture_points": "Всего сбито очков захвата",
"cumulative_frags": "Всего уничтожено",
"cumulative_hits": "Всего уничтожен",
"cumulative_inaccuracy": "Неточность",
"cumulative_kills_to_death_ratio": "Коэффициент убийств и смертей",
"cumulative_losses": "Поражения",
"cumulative_misses": "Всего промахов",
"cumulative_shots": "Всего выстрелов",
"cumulative_spotted": "Всего попаданий",
"cumulative_survived_battles": "Выжил в боях",
"cumulative_win_and_survived": "Выжил и победил в боях",
"cumulative_wins": "Победы",
"cumulative_xp": "Всего опыта",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Общее время в боях"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Не удалось найти клан\nНе найден «%s». Попробуйте выбрать клан из результатов поиска.",
"player_not_found": "# Не удалось найти пользователя\nЯ не могу найти пользователя «%s». Попробуйте выбрать пользователя из результатов поиска, введя правильное имя пользователя или используя команду «/link».",
"player_not_linked": "# Используйте «/link», чтобы начать\nЯ не знаю, кто ты, но можешь мне сказать сегодня!",
"tank_name_not_found": "# Танк не найден\nНе удалось найти танк по названию «%s».",
"tank_id_not_found": "# Танк не найден\nНе удалось найти танк по ID «%s». Попробуйте ввести название танка вместо числа.",
"no_tank_stats": "# Статистика танка недоступна\nУ этого игрока нет доступной статистики. Возможно, это не тот игрок, которого вы ищете.",
"get_help": "Помочь Discord серверу",
"uncaught_error": "# BlitzKit столкнулся с ошибкой!\nНам так жаль об этом. Не стесняйтесь [присоединиться к официальному серверу Discord](https://discord.gg/nDt7AjGJQH), чтобы получить помощь.",
"preview": "# Эта команда находится в предварительном просмотре\n\nВы можете получить доступ к этой команде только на [официальном сервере Discord] (https://discord.gg/nDt7AjGJQH). Команда скоро станет общедоступной.",
"query_too_small": "# Ваш поисковой запрос слишком короткий\nДлина вашего запроса должна составлять не менее 3 символов.",
"query_too_long": "# Ваш поисковой запрос слишком длинный\nДлина вашего запроса должна составлять не более 100 символов."
},
"wn8": {
"$": "WN8",
"very_bad": "Очень плохо",
"bad": "Плохо",
"below_average": "Ниже среднего",
"average": "Средне",
"above_average": "Выше среднего",
"good": "Хорошо",
"very_good": "Очень хорошо",
"great": "Отлично",
"unicum": "Уникально",
"super_unicum": "Крайне уникально"
},
"subcommands": {
"today": {
"$": "cегодня",
"description": "Cегодня"
},
"30": {
"$": "30",
"description": "30 дней"
},
"60": {
"$": "60",
"description": "60 дней"
},
"90": {
"$": "90",
"description": "90 дней"
},
"career": {
"$": "карьера",
"description": "Карьера"
},
"custom": {
"$": "собственный",
"description": "Собственный диапазон",
"options": {
"start": {
"$": "начало",
"description": "Начать через несколько дней с сегодняшнего дня"
},
"end": {
"$": "конец",
"description": "Закончить через несколько дней с сегодняшнего дня"
}
}
}
},
"options": {
"clan": {
"$": "клан",
"description": "Название или тег клана"
},
"tank": {
"$": "танк",
"description": "Танк для получения статистики для"
},
"username": {
"$": "имя пользователя",
"description": "Blitz никнейм игрока"
},
"filters": {
"nation": {
"$": "нация",
"description": "Нация"
},
"class": {
"$": "класс",
"description": "Класс"
},
"type": {
"$": "тип",
"description": "Тип"
}
},
"region": {
"$": "регион",
"description": "Регион"
},
"tier": {
"$": "уровень",
"description": "Уровень",
"choices": {
"tier": "Уровень %s - %s"
}
}
},
"periods": {
"career": "Карьера",
"today": "Cегодня",
"days": "%s дней",
"custom": "%s - %s дней"
},
"filters": {
"none": "Без фильтров",
"tier": "Уровень %s"
},
"no_data": {
"battles_in_period": "За этот период боёв не было",
"battles_in_season": "За этот сезон боёв не было",
"players_in_period": "Нет игроков за этот период",
"tanks_found": "Танки не найдены"
},
"buttons": {
"refresh": "Обновить"
},
"hero_stats": {
"battles": "Боёв",
"winrate": "Коэффициент побед",
"damage": "Урон",
"damage_ratio": "Коэффициент урона",
"tier": "Уровень",
"survival": "Выживание",
"accuracy": "Точность стрельбы",
"kills": "Уничтожил"
}
},
"commands": {
"auction": {
"$": "аукцион",
"description": "Танки доступные на аукционе сейчас",
"body": {
"title": "Blitz аукцион",
"subtitle": "%s доступные • следующие цены %s\nПокупка сейчас: %s",
"next": "Следующая цена: %s",
"available": "Доступно: %s / %s"
},
"errors": {
"no_auction": "# Аукцион сейчас не проходит\n\nНа данный момент нет танков для покупки на аукционе. Если считаете, что это ошибка, пожалуйста о несоответствии на [официальном сервере Discord](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Купить сейчас"
}
},
"replay": {
"$": "повтор",
"description": "Показать результаты повтора"
},
"research": {
"$": "исследование",
"description": "Список ресурсов для исследования ветки",
"options": {
"target_tank": {
"$": "целевой-танк",
"description": "Конечный танк исследования ветки"
},
"starting_tank": {
"$": "стартовый-танк",
"description": "Стартовый танк в ветке (назначен автоматически, если не предусмотрен)"
}
},
"body": {
"subtitle": "Исследование",
"total": "Всего",
"research": "Исследование: %s",
"upgrades": "Улучшения: %s",
"purchase": "Покупка: %s",
"equipment": "Снаряжение: %s",
"estimation": "# Это приблизительная оценка!\n\nВ этот список могут входить уже исследованные танки из-за неточной информации, предоставляемой Wargaming. Попробуйте указать «стартовый танк» в строке при использовании команды, если сможете."
},
"errors": {
"non_tech_tree": "# %s отсутствует в дереве танков\n\nВы не можете исследовать премиум / коллекционный танк.",
"tier_1": "# %s - это уровень I\n\nУровень I всегда открыт бесплатно для всех игроков.",
"already_researched": "# %s уже исследован\n\nПопробуйте предоставить «starting-tank» при использовании команды для просмотра итогов уже исследованной ветки.",
"start_end_equal": "# %s был предоставлен дважды\n\nУбедитесь, что «starting-tank» и «target-tank» различаются.",
"tanks_not_on_line": "# %s и %s находятся не в одной ветке\n\nУбедитесь, что «starting-tank» и «target-tank» находятся на той же ветке дерева танков.",
"unordered": "# У %s более высокий уровень чем у %s\n\nОбязательно укажите %s в качестве «starting-tank», так как это танк более низкого уровня."
}
},
"about": {
"$": "о-нас",
"description": "Вся необходимая информация о боте",
"subcommands": {
"commands": {
"$": "команды",
"description": "Список всех команд и их описаний",
"body": "# Команды\n\nBlitzKit предлагает следующие команды:\n\n"
},
"embeds": {
"$": "встраиваемые-элементы",
"description": "Как работают встраиваемые элементы"
},
"introduction": {
"$": "введение",
"description": "О боте"
},
"invite": {
"$": "пригласить",
"description": "Как пригласить бота"
},
"timezones": {
"$": "часовой-пояс",
"description": "Как BlitzKit вводит и использует ваш часовой пояс"
},
"credits": {
"$": "credits",
"description": "Люди, которые помогли BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "Разбивка периода по сыгранным танкам",
"body": {
"total": "Всего",
"battles": "Боёв",
"wn8": "WN8",
"winrate": "Коэффициент побед",
"damage": "Урон",
"unknown_tank": "Неизвестный танк %s"
}
},
"debug": {
"$": "отладка",
"description": "Отладочная информация о боте",
"body": {
"title": "# Отладка",
"version": "Версия",
"shard": {
"$": "Фрагмент",
"default": "по умолчанию"
},
"uptime": "Время работы"
}
},
"evolution": {
"$": "изменение",
"description": "Изменения статистики",
"groups": {
"player": {
"$": "игрок",
"description": "Статистика игрока"
},
"tank": {
"$": "танк",
"description": "Статистика танков"
}
}
},
"inactive": {
"$": "неактивно",
"description": "Список всех неактивных игроков в клане выше порога",
"options": {
"threshold": {
"$": "порог",
"description": "Количество дней не активности (по умолчанию: %s дней)"
}
},
"body": {
"subtitle": "Неактивен %s+ дней",
"listing": "%s дней"
}
},
"full_stats": {
"$": "полная-статистика",
"description": "Полная статистика в игре",
"body": {
"cards": {
"games": {
"$": "Игры",
"winrate": "Коэффициент побед",
"battles": "Боёв",
"wins_losses": "Побед / поражений"
},
"efficiency": {
"$": "Эффективность",
"wn8": "WN8",
"survival": "Выжил",
"accuracy": "Точность стрельбы"
},
"lethality": {
"$": "Смертность",
"average_damage": "Средний урон",
"damage_per_tier": "Урон по уровню",
"damage_ratio": "Коэффициент урона",
"average_kills": "Среднее число уничтоженных"
},
"miscellaneous": {
"$": "Прочее",
"average_tier": "Средний уровень",
"average_spots": "Среднее число попаданий",
"average_xp": "Средний опыт",
"kills_to_death_ratio": "Коэффициент убийств и смертей"
}
}
}
},
"link": {
"$": "привязать",
"description": "Привяжите учётную запись Blitz и Discord",
"body": {
"accounts_linked": "Аккаунты привязаны",
"no_role_permissions": {
"$": "%s не удалось пройти проверку",
"description": "У меня нет разрешения на управление ролями."
}
},
"embed": {
"title": "Неверный аккаунт?",
"description": "Используйте команду снова и выберите имя пользователя из результатов поиска."
}
},
"search_clans": {
"$": "поиск-кланов",
"description": "Поиск кланов",
"options": {
"limit": {
"$": "лимит",
"description": "Максимальное количество результатов клана (по умолчанию: %s)"
}
},
"body": {
"title": "Поиск клана %s в регионе %s",
"no_results": "Кланов не найдено"
}
},
"search_players": {
"$": "поиск-игроков",
"description": "Поиск игроков",
"options": {
"limit": {
"$": "лимит",
"description": "Максимальное число результатов игрока (по умолчанию: %s)"
}
},
"body": {
"title": "Поиск игрока %s в регионе %s",
"no_results": "Игроков не найдено"
}
},
"rating_leaderboard": {
"$": "таблица лидеров",
"description": "Статистика рейтингов боёв",
"subcommands": {
"neighbors": {
"$": "соседние",
"description": "Соседние игроки в таблице лидеров"
},
"league": {
"$": "лига",
"description": "Топ игроков лиги"
}
},
"options": {
"limit": {
"$": "лимит",
"description": "Сколько отображаемых соседних (по умолчанию: %s)"
},
"season": {
"$": "сезон",
"description": "Номер сезона (по умолчанию: текущий)",
"choices": {
"current": "%s (текущая)"
}
},
"league": {
"$": "лига",
"description": "Отображаемая лига"
}
},
"body": {
"top_players": "Топ %s игроков",
"no_participation": "# %s не играл в рейтинг в сезоне %s\n\nЭтот игрок не участвовал в этом сезоне или не получил прошлую калибровку.",
"no_ongoing_season": "# Нет текущего сезона\n\nВ данный момент нет рейтингового сезона.",
"deleted_player": "Удалено %s",
"neighbors": "Рейтинг соседних",
"more": "+ ещё %s"
}
},
"today": {
"$": "cегодня",
"description": "Танки, на которых сыграли сегодня и статистика"
},
"stats": {
"$": "статистика",
"description": "Быстрая статистика за период"
},
"owned_tanks": {
"$": "принадлежащие-танки",
"description": "Показывает все принадлежащие танки на уровне",
"body": {
"title": "# %s принадлежал уровень %s танков\n\n"
}
},
"permissions": {
"$": "разрешения",
"description": "Проверяет, есть ли у бота необходимые разрешения для работы",
"body": {
"title": "# Разрешения\n\n%s",
"view_channels": "Просмотр каналов: необходимо найти сообщение, на котором была использована кнопка обновления",
"read_message_history": "Просмотр истории сообщений: необходимо найти сообщение для обновления",
"attach_files": "Прикрепить файлы: необходимо для добавления изображений в команды на основе изображения",
"external_emojis": "Внешние эмоции: необходимы для добавления национальных флагов и эмодзи класса танков"
}
},
"ping": {
"$": "пинг",
"description": "Проверьте, что бот жив.",
"body": {
"pong": "Понг 🏓",
"pong_with_time": "Понг 🏓 - %sмс"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Пинг BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Пинг World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Основная информация о игроке",
"body": {
"title": "Информация о %s",
"nickname": "Никнейм",
"battles": "Боёв",
"winrate": "Коэффициент побед",
"account_id": "ID учётной записи",
"created": "Создано",
"last_battle": "Последний бой"
}
},
"rating": {
"$": "рейтинг",
"description": "Статистика рейтинга игрока",
"subcommands": {
"today": {
"$": "cегодня",
"description": "Статистика рейтинга за сегодня"
},
"season": {
"$": "сезон",
"description": "Статистика рейтинга в этом сезоне"
}
},
"body": {
"subtitle": {
"today": "Статистика рейтинга за сегодня",
"season": "Статистика рейтинга в этом сезоне"
}
},
"errors": {
"no_participation": "# %s не получил рейтинга\n\nЭтот игрок в этом сезоне не участвовал в рейтинговом режиме.",
"unrecorded_stats": "# Статистика недоступна в настоящее время\n\nВ настоящее время Wargaming, возможно, сталкивается с простоями сервера. Если сезон продолжается, статистика должна быть доступна в течение 24 часов."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "非常差",
"1": "差",
"2": "较差",
"3": "平均",
"4": "高于平均",
"5": "好",
"6": "非常好",
"7": "很好",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "差",
"4": "较差",
"5": "平均",
"6": "高于平均",
"7": "好",
"8": "非常好",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "苏联 (S系)",
"germany": "德国 (D系)",
"usa": "美国 (M系)",
"china": "中国 (C系)",
"uk": "英国 (Y系)",
"france": "法国 (F系)",
"japan": "日本 (R系)",
"european": "欧盟 (E系)",
"other": "X系"
},
"nations_adjectives": {
"ussr": "苏联 (S系)",
"germany": "德国 (D系)",
"usa": "美国 (M系)",
"china": "中国 (C系)",
"uk": "英国 (Y系)",
"france": "法国 (F系)",
"japan": "日本 (R系)",
"european": "欧盟 (E系)",
"other": "X系"
},
"tank_class_short": {
"-1": "无法识别",
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tank_class_short_plural_lowercase": {
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tank_class_medium": {
"-1": "无法识别",
"0": "轻型坦克",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "战损比",
"cumulative_losses": "失败",
"cumulative_misses": "总射击失误数",
"cumulative_shots": "总射击数",
"cumulative_spotted": "场均发现敌人",
"cumulative_survived_battles": "幸存场次",
"cumulative_win_and_survived": "胜利和幸存场次",
"cumulative_wins": "胜利",
"cumulative_xp": "总经验",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "总战斗时间"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# 找不到军团\n找不到\"%s\"。请尝试从搜索结果中选择一个军团。",
"player_not_found": "# 找不到用户\n无法找到用户 \"%s\"。 尝试从搜索结果中挑选用户,输入一个有效的用户名或使用 \"/link\" 命令。",
"player_not_linked": "# 使用 `/link` 来开始\n我不知道你是谁,但你今天可以告诉我!",
"tank_name_not_found": "# 坦克找不到\n无法找到名为 \"%s \"的坦克。",
"tank_id_not_found": "# 坦克找不到\n无法找到ID \"%s\"的坦克。 试着输入坦克的名称而不是数字。",
"no_tank_stats": "# 没有这个坦克的数据\n这个玩家没有数据。你可能找错人了。",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit 崩溃了!\n非常抱歉。请加入[官方Discord](https://discord.gg/nDt7AjGJQH),以方便帮助你的问题。",
"preview": "# 此命令处于预览\n\n您只能在[官方Discord服务器](https://discord.gg/nDt7AjGJQH)访问此命令。该命令将很快可用。",
"query_too_small": "# 您的搜索太短了\n您的搜索必须至少 3 个字符长。",
"query_too_long": "# 您的搜索太长了\n您的搜索必须最长为 100 个字符。"
},
"wn8": {
"$": "WN8",
"very_bad": "非常差",
"bad": "差",
"below_average": "较差",
"average": "平均",
"above_average": "高于平均",
"good": "好",
"very_good": "非常好",
"great": "很好",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "今日:",
"description": "今日:"
},
"30": {
"$": "30",
"description": "30天"
},
"60": {
"$": "60",
"description": "60天"
},
"90": {
"$": "90",
"description": "90天"
},
"career": {
"$": "玩家资料",
"description": "玩家资料"
},
"custom": {
"$": "自定义",
"description": "自定义范围",
"options": {
"start": {
"$": "开始时间",
"description": "从今天开始于数天前"
},
"end": {
"$": "结束时间",
"description": "从今天开始于数天前"
}
}
}
},
"options": {
"clan": {
"$": "军团",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-1
View File
@@ -46,5 +46,4 @@ export * from './votes';
export * from './createDefaultProvisions';
export * from './metaSortTank';
export * from './romanize';
export * from './supportedLocales';
export * from './youtubers';
@@ -1,13 +0,0 @@
export const SUPPORTED_LOCALES = [
'en',
'es',
'hr',
'pt',
'ru',
'uk',
'zh',
] as const;
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
@@ -1,11 +1,5 @@
import { times } from 'lodash-es';
import {
CrewType,
GunDefinition,
ShellType,
TankClass,
TankType,
} from '../../protos';
import { CrewType, ShellType, TankClass, TankType } from '../../protos';
export const SHELL_NAMES: Record<ShellType, string> = {
[ShellType.AP]: 'AP',
@@ -13,21 +7,14 @@ export const SHELL_NAMES: Record<ShellType, string> = {
[ShellType.HEAT]: 'HEAT',
[ShellType.HE]: 'HE',
};
export const CREW_MEMBER_NAMES: Record<CrewType, string> = {
[CrewType.COMMANDER]: 'Commander',
[CrewType.DRIVER]: 'Driver',
[CrewType.GUNNER]: 'Gunner',
[CrewType.LOADER]: 'Loader',
[CrewType.RADIOMAN]: 'Radioman',
};
export const GUN_TYPE_NAMES: Record<
Exclude<GunDefinition['gun_type'], undefined>['$case'],
string
> = {
auto_loader: 'Auto loader',
auto_reloader: 'Auto reloader',
regular: 'Regular',
};
export const CREW_MEMBER_NAMES = {
[CrewType.COMMANDER]: 'commander',
[CrewType.DRIVER]: 'driver',
[CrewType.GUNNER]: 'gunner',
[CrewType.LOADER]: 'loader',
[CrewType.RADIOMAN]: 'radioman',
} as const;
/**
* @deprecated use svg
*/
+20 -2
View File
@@ -1,4 +1,6 @@
import { deburr } from 'lodash-es';
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
import { I18nString } from '../protos';
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
import { fetchTankDefinitions } from './tankDefinitions';
@@ -11,18 +13,34 @@ export async function fetchTankNames() {
return await Promise.all(
tankDefinitionsArray.map(async (tank) => {
const searchableNameDeburr: I18nString = { locales: {} };
Object.entries(tank.name_full ?? tank.name).forEach(([key, value]) => {
searchableNameDeburr.locales[key] = deburr(value);
});
return {
id: tank.id,
name: tank.name,
nameFull: tank.name_full,
searchableName: tank.name_full ?? tank.name,
searchableNameDeburr: deburr(tank.name_full ?? tank.name),
searchableNameDeburr,
camouflages: tank.camouflages
?.map((id) => camouflageDefinitions.camouflages[id]?.name)
?.map((id) => SUPPORTED_LOCALES.map((locale) => camouflageDefinitions.camouflages[id]?.name.locales[locale]))
.flat()
.filter(Boolean)
.map(deburr)
.join(' '),
treeType: tank.type,
};
}),
);
}
export const SEARCH_KEYS = [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.locales.${locale}`,
`searchableNameDeburr.locales.${locale}`,
]).flat(),
'camouflages',
];
+2 -3
View File
@@ -1,5 +1,4 @@
const numberFormat = Intl.NumberFormat('en', { notation: 'compact' });
export function formatCompact(number: number) {
export function formatCompact(locale: string, number: number) {
const numberFormat = Intl.NumberFormat(locale, { notation: 'compact' });
return numberFormat.format(number);
}
@@ -251,7 +251,7 @@ export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_Ave
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? AverageDefinitionsEntry.fromPartial(object.value)
: createBaseAverageDefinitionsEntry();
: undefined;
return message;
},
};
@@ -2,13 +2,15 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message CamouflageDefinitions {
map<uint32, Camouflage> camouflages = 1;
}
message Camouflage {
required uint32 id = 1;
required string name = 2;
optional string tank_name = 3;
optional string tank_name_full = 4;
required I18nString name = 2;
optional I18nString tank_name = 3;
optional I18nString tank_name_full = 4;
}
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -20,9 +21,9 @@ export interface CamouflageDefinitions_CamouflagesEntry {
export interface Camouflage {
id: number;
name: string;
tank_name?: string | undefined;
tank_name_full?: string | undefined;
name: I18nString;
tank_name?: I18nString | undefined;
tank_name_full?: I18nString | undefined;
}
function createBaseCamouflageDefinitions(): CamouflageDefinitions {
@@ -184,13 +185,13 @@ export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefini
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Camouflage.fromPartial(object.value)
: createBaseCamouflage();
: undefined;
return message;
},
};
function createBaseCamouflage(): Camouflage {
return { id: 0, name: "", tank_name: undefined, tank_name_full: undefined };
return { id: 0, name: createBaseI18nString(), tank_name: undefined, tank_name_full: undefined };
}
export const Camouflage: MessageFns<Camouflage> = {
@@ -198,14 +199,14 @@ export const Camouflage: MessageFns<Camouflage> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.tank_name !== undefined && message.tank_name !== undefined) {
writer.uint32(26).string(message.tank_name);
if (message.tank_name !== undefined) {
I18nString.encode(message.tank_name, writer.uint32(26).fork()).join();
}
if (message.tank_name_full !== undefined && message.tank_name_full !== undefined) {
writer.uint32(34).string(message.tank_name_full);
if (message.tank_name_full !== undefined) {
I18nString.encode(message.tank_name_full, writer.uint32(34).fork()).join();
}
return writer;
},
@@ -230,7 +231,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -238,7 +239,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.tank_name = reader.string();
message.tank_name = I18nString.decode(reader, reader.uint32());
continue;
}
case 4: {
@@ -246,7 +247,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.tank_name_full = reader.string();
message.tank_name_full = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -261,9 +262,9 @@ export const Camouflage: MessageFns<Camouflage> = {
fromJSON(object: any): Camouflage {
return {
id: globalThis.Number(assertSet("Camouflage.id", object.id)),
name: globalThis.String(assertSet("Camouflage.name", object.name)),
tank_name: isSet(object.tank_name) ? globalThis.String(object.tank_name) : undefined,
tank_name_full: isSet(object.tank_name_full) ? globalThis.String(object.tank_name_full) : undefined,
name: I18nString.fromJSON(assertSet("Camouflage.name", object.name)),
tank_name: isSet(object.tank_name) ? I18nString.fromJSON(object.tank_name) : undefined,
tank_name_full: isSet(object.tank_name_full) ? I18nString.fromJSON(object.tank_name_full) : undefined,
};
},
@@ -272,14 +273,14 @@ export const Camouflage: MessageFns<Camouflage> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tank_name !== undefined && message.tank_name !== undefined) {
obj.tank_name = message.tank_name;
if (message.tank_name !== undefined) {
obj.tank_name = I18nString.toJSON(message.tank_name);
}
if (message.tank_name_full !== undefined && message.tank_name_full !== undefined) {
obj.tank_name_full = message.tank_name_full;
if (message.tank_name_full !== undefined) {
obj.tank_name_full = I18nString.toJSON(message.tank_name_full);
}
return obj;
},
@@ -290,9 +291,15 @@ export const Camouflage: MessageFns<Camouflage> = {
fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>(object: I): Camouflage {
const message = createBaseCamouflage();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.tank_name = object.tank_name ?? undefined;
message.tank_name_full = object.tank_name_full ?? undefined;
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tank_name = (object.tank_name !== undefined && object.tank_name !== null)
? I18nString.fromPartial(object.tank_name)
: undefined;
message.tank_name_full = (object.tank_name_full !== undefined && object.tank_name_full !== null)
? I18nString.fromPartial(object.tank_name_full)
: undefined;
return message;
},
};
@@ -2,13 +2,15 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message ConsumableDefinitions {
map<uint32, Consumable> consumables = 1;
}
message Consumable {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
required uint32 cooldown = 3;
optional float duration = 4;
required bool game_mode_exclusive = 5;
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -47,7 +48,7 @@ export interface ConsumableDefinitions_ConsumablesEntry {
export interface Consumable {
id: number;
name: string;
name: I18nString;
cooldown: number;
duration?: number | undefined;
game_mode_exclusive: boolean;
@@ -240,13 +241,21 @@ export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefini
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Consumable.fromPartial(object.value)
: createBaseConsumable();
: undefined;
return message;
},
};
function createBaseConsumable(): Consumable {
return { id: 0, name: "", cooldown: 0, duration: undefined, game_mode_exclusive: false, include: [], exclude: [] };
return {
id: 0,
name: createBaseI18nString(),
cooldown: 0,
duration: undefined,
game_mode_exclusive: false,
include: [],
exclude: [],
};
}
export const Consumable: MessageFns<Consumable> = {
@@ -254,8 +263,8 @@ export const Consumable: MessageFns<Consumable> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.cooldown !== 0) {
writer.uint32(24).uint32(message.cooldown);
@@ -295,7 +304,7 @@ export const Consumable: MessageFns<Consumable> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -350,7 +359,7 @@ export const Consumable: MessageFns<Consumable> = {
fromJSON(object: any): Consumable {
return {
id: globalThis.Number(assertSet("Consumable.id", object.id)),
name: globalThis.String(assertSet("Consumable.name", object.name)),
name: I18nString.fromJSON(assertSet("Consumable.name", object.name)),
cooldown: globalThis.Number(assertSet("Consumable.cooldown", object.cooldown)),
duration: isSet(object.duration) ? globalThis.Number(object.duration) : undefined,
game_mode_exclusive: globalThis.Boolean(assertSet("Consumable.game_mode_exclusive", object.game_mode_exclusive)),
@@ -368,8 +377,8 @@ export const Consumable: MessageFns<Consumable> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.cooldown !== 0) {
obj.cooldown = Math.round(message.cooldown);
@@ -395,7 +404,9 @@ export const Consumable: MessageFns<Consumable> = {
fromPartial<I extends Exact<DeepPartial<Consumable>, I>>(object: I): Consumable {
const message = createBaseConsumable();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.cooldown = object.cooldown ?? 0;
message.duration = object.duration ?? undefined;
message.game_mode_exclusive = object.game_mode_exclusive ?? false;
@@ -2,14 +2,16 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message EquipmentDefinitions {
map<string, EquipmentPreset> presets = 1;
map<uint32, Equipment> equipments = 2;
}
message Equipment {
required string name = 1;
required string description = 2;
required I18nString name = 1;
required I18nString description = 2;
}
message EquipmentPreset {
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -25,8 +26,8 @@ export interface EquipmentDefinitions_EquipmentsEntry {
}
export interface Equipment {
name: string;
description: string;
name: I18nString;
description: I18nString;
}
export interface EquipmentPreset {
@@ -235,7 +236,7 @@ export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_
message.key = object.key ?? "";
message.value = (object.value !== undefined && object.value !== null)
? EquipmentPreset.fromPartial(object.value)
: createBaseEquipmentPreset();
: undefined;
return message;
},
};
@@ -317,22 +318,22 @@ export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitio
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Equipment.fromPartial(object.value)
: createBaseEquipment();
: undefined;
return message;
},
};
function createBaseEquipment(): Equipment {
return { name: "", description: "" };
return { name: createBaseI18nString(), description: createBaseI18nString() };
}
export const Equipment: MessageFns<Equipment> = {
encode(message: Equipment, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.name !== "") {
writer.uint32(10).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(10).fork()).join();
}
if (message.description !== "") {
writer.uint32(18).string(message.description);
if (message.description !== undefined) {
I18nString.encode(message.description, writer.uint32(18).fork()).join();
}
return writer;
},
@@ -349,7 +350,7 @@ export const Equipment: MessageFns<Equipment> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 2: {
@@ -357,7 +358,7 @@ export const Equipment: MessageFns<Equipment> = {
break;
}
message.description = reader.string();
message.description = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -371,18 +372,18 @@ export const Equipment: MessageFns<Equipment> = {
fromJSON(object: any): Equipment {
return {
name: globalThis.String(assertSet("Equipment.name", object.name)),
description: globalThis.String(assertSet("Equipment.description", object.description)),
name: I18nString.fromJSON(assertSet("Equipment.name", object.name)),
description: I18nString.fromJSON(assertSet("Equipment.description", object.description)),
};
},
toJSON(message: Equipment): unknown {
const obj: any = {};
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.description !== "") {
obj.description = message.description;
if (message.description !== undefined) {
obj.description = I18nString.toJSON(message.description);
}
return obj;
},
@@ -392,8 +393,12 @@ export const Equipment: MessageFns<Equipment> = {
},
fromPartial<I extends Exact<DeepPartial<Equipment>, I>>(object: I): Equipment {
const message = createBaseEquipment();
message.name = object.name ?? "";
message.description = object.description ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.description = (object.description !== undefined && object.description !== null)
? I18nString.fromPartial(object.description)
: createBaseI18nString();
return message;
},
};
@@ -2,6 +2,8 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message GameDefinitions {
required string version = 1;
repeated string nations = 2;
@@ -10,7 +12,7 @@ message GameDefinitions {
}
message GameMode {
required string name = 1;
required I18nString name = 1;
}
message GameModeRole {
+14 -11
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -27,7 +28,7 @@ export interface GameDefinitions_RolesEntry {
}
export interface GameMode {
name: string;
name: I18nString;
}
export interface GameModeRole {
@@ -259,7 +260,7 @@ export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameMode
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GameMode.fromPartial(object.value)
: createBaseGameMode();
: undefined;
return message;
},
};
@@ -337,19 +338,19 @@ export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry>
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GameModeRole.fromPartial(object.value)
: createBaseGameModeRole();
: undefined;
return message;
},
};
function createBaseGameMode(): GameMode {
return { name: "" };
return { name: createBaseI18nString() };
}
export const GameMode: MessageFns<GameMode> = {
encode(message: GameMode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.name !== "") {
writer.uint32(10).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(10).fork()).join();
}
return writer;
},
@@ -366,7 +367,7 @@ export const GameMode: MessageFns<GameMode> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -379,13 +380,13 @@ export const GameMode: MessageFns<GameMode> = {
},
fromJSON(object: any): GameMode {
return { name: globalThis.String(assertSet("GameMode.name", object.name)) };
return { name: I18nString.fromJSON(assertSet("GameMode.name", object.name)) };
},
toJSON(message: GameMode): unknown {
const obj: any = {};
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
return obj;
},
@@ -395,7 +396,9 @@ export const GameMode: MessageFns<GameMode> = {
},
fromPartial<I extends Exact<DeepPartial<GameMode>, I>>(object: I): GameMode {
const message = createBaseGameMode();
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
return message;
},
};
+7
View File
@@ -0,0 +1,7 @@
syntax = "proto2";
package blitzkit;
message I18nString {
map<string, string> locales = 1;
}
+212
View File
@@ -0,0 +1,212 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.2.2
// protoc v5.28.2
// source: packages/core/src/protos/i18n.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "blitzkit";
export interface I18nString {
locales: { [key: string]: string };
}
export interface I18nString_LocalesEntry {
key: string;
value: string;
}
export function createBaseI18nString(): I18nString {
return { locales: {} };
}
export const I18nString: MessageFns<I18nString> = {
encode(message: I18nString, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.locales).forEach(([key, value]) => {
I18nString_LocalesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
});
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): I18nString {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
const entry1 = I18nString_LocalesEntry.decode(reader, reader.uint32());
if (entry1.value !== undefined) {
message.locales[entry1.key] = entry1.value;
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): I18nString {
return {
locales: isObject(object.locales)
? Object.entries(object.locales).reduce<{ [key: string]: string }>((acc, [key, value]) => {
acc[key] = String(value);
return acc;
}, {})
: {},
};
},
toJSON(message: I18nString): unknown {
const obj: any = {};
if (message.locales) {
const entries = Object.entries(message.locales);
if (entries.length > 0) {
obj.locales = {};
entries.forEach(([k, v]) => {
obj.locales[k] = v;
});
}
}
return obj;
},
create<I extends Exact<DeepPartial<I18nString>, I>>(base?: I): I18nString {
return I18nString.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<I18nString>, I>>(object: I): I18nString {
const message = createBaseI18nString();
message.locales = Object.entries(object.locales ?? {}).reduce<{ [key: string]: string }>((acc, [key, value]) => {
if (value !== undefined) {
acc[key] = globalThis.String(value);
}
return acc;
}, {});
return message;
},
};
function createBaseI18nString_LocalesEntry(): I18nString_LocalesEntry {
return { key: "", value: "" };
}
export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = {
encode(message: I18nString_LocalesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) {
writer.uint32(10).string(message.key);
}
if (message.value !== undefined) {
writer.uint32(18).string(message.value);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): I18nString_LocalesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString_LocalesEntry();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.key = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.value = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): I18nString_LocalesEntry {
return {
key: globalThis.String(assertSet("I18nString_LocalesEntry.key", object.key)),
value: globalThis.String(assertSet("I18nString_LocalesEntry.value", object.value)),
};
},
toJSON(message: I18nString_LocalesEntry): unknown {
const obj: any = {};
if (message.key !== undefined) {
obj.key = message.key;
}
if (message.value !== undefined) {
obj.value = message.value;
}
return obj;
},
create<I extends Exact<DeepPartial<I18nString_LocalesEntry>, I>>(base?: I): I18nString_LocalesEntry {
return I18nString_LocalesEntry.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<I18nString_LocalesEntry>, I>>(object: I): I18nString_LocalesEntry {
const message = createBaseI18nString_LocalesEntry();
message.key = object.key ?? "";
message.value = object.value ?? "";
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial<T["value"]> }
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin ? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
function isObject(value: any): boolean {
return typeof value === "object" && value !== null;
}
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}
+1
View File
@@ -4,6 +4,7 @@ export * from './camouflage_definitions.ts';
export * from './consumable_definitions.ts';
export * from './equipment_definitions.ts';
export * from './game_definitions.ts';
export * from './i18n.ts';
export * from './map_definitions.ts';
export * from './model_definitions.ts';
export * from './provision_definitions.ts';
@@ -2,11 +2,13 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message MapDefinitions {
map<int32, MapDefinition> maps = 1;
}
message MapDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
}
+13 -10
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -20,7 +21,7 @@ export interface MapDefinitions_MapsEntry {
export interface MapDefinition {
id: number;
name: string;
name: I18nString;
}
function createBaseMapDefinitions(): MapDefinitions {
@@ -175,13 +176,13 @@ export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? MapDefinition.fromPartial(object.value)
: createBaseMapDefinition();
: undefined;
return message;
},
};
function createBaseMapDefinition(): MapDefinition {
return { id: 0, name: "" };
return { id: 0, name: createBaseI18nString() };
}
export const MapDefinition: MessageFns<MapDefinition> = {
@@ -189,8 +190,8 @@ export const MapDefinition: MessageFns<MapDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
return writer;
},
@@ -215,7 +216,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -230,7 +231,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
fromJSON(object: any): MapDefinition {
return {
id: globalThis.Number(assertSet("MapDefinition.id", object.id)),
name: globalThis.String(assertSet("MapDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("MapDefinition.name", object.name)),
};
},
@@ -239,8 +240,8 @@ export const MapDefinition: MessageFns<MapDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
return obj;
},
@@ -251,7 +252,9 @@ export const MapDefinition: MessageFns<MapDefinition> = {
fromPartial<I extends Exact<DeepPartial<MapDefinition>, I>>(object: I): MapDefinition {
const message = createBaseMapDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
return message;
},
};
@@ -265,7 +265,7 @@ export const ModelDefinitions_ModelsEntry: MessageFns<ModelDefinitions_ModelsEnt
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? ModelDefinition.fromPartial(object.value)
: createBaseModelDefinition();
: undefined;
return message;
},
};
@@ -545,7 +545,7 @@ export const ModelDefinition_TurretsEntry: MessageFns<ModelDefinition_TurretsEnt
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TurretModelDefinition.fromPartial(object.value)
: createBaseTurretModelDefinition();
: undefined;
return message;
},
};
@@ -623,7 +623,7 @@ export const ModelDefinition_TracksEntry: MessageFns<ModelDefinition_TracksEntry
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TrackModelDefinition.fromPartial(object.value)
: createBaseTrackModelDefinition();
: undefined;
return message;
},
};
@@ -1248,7 +1248,7 @@ export const TurretModelDefinition_GunsEntry: MessageFns<TurretModelDefinition_G
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GunModelDefinition.fromPartial(object.value)
: createBaseGunModelDefinition();
: undefined;
return message;
},
};
@@ -3,6 +3,7 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/consumable_definitions.proto";
import "packages/core/src/protos/i18n.proto";
message ProvisionDefinitions {
map<uint32, Provision> provisions = 1;
@@ -10,7 +11,7 @@ message ProvisionDefinitions {
message Provision {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
optional float crew = 3;
required bool game_mode_exclusive = 5;
repeated ConsumableTankFilter include = 6;
@@ -7,6 +7,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { ConsumableTankFilter } from "./consumable_definitions";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -21,7 +22,7 @@ export interface ProvisionDefinitions_ProvisionsEntry {
export interface Provision {
id: number;
name: string;
name: I18nString;
crew?: number | undefined;
game_mode_exclusive: boolean;
include: ConsumableTankFilter[];
@@ -187,13 +188,13 @@ export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitio
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Provision.fromPartial(object.value)
: createBaseProvision();
: undefined;
return message;
},
};
function createBaseProvision(): Provision {
return { id: 0, name: "", crew: undefined, game_mode_exclusive: false, include: [], exclude: [] };
return { id: 0, name: createBaseI18nString(), crew: undefined, game_mode_exclusive: false, include: [], exclude: [] };
}
export const Provision: MessageFns<Provision> = {
@@ -201,8 +202,8 @@ export const Provision: MessageFns<Provision> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.crew !== undefined && message.crew !== undefined) {
writer.uint32(29).float(message.crew);
@@ -239,7 +240,7 @@ export const Provision: MessageFns<Provision> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -286,7 +287,7 @@ export const Provision: MessageFns<Provision> = {
fromJSON(object: any): Provision {
return {
id: globalThis.Number(assertSet("Provision.id", object.id)),
name: globalThis.String(assertSet("Provision.name", object.name)),
name: I18nString.fromJSON(assertSet("Provision.name", object.name)),
crew: isSet(object.crew) ? globalThis.Number(object.crew) : undefined,
game_mode_exclusive: globalThis.Boolean(assertSet("Provision.game_mode_exclusive", object.game_mode_exclusive)),
include: globalThis.Array.isArray(object?.include)
@@ -303,8 +304,8 @@ export const Provision: MessageFns<Provision> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.crew !== undefined && message.crew !== undefined) {
obj.crew = message.crew;
@@ -327,7 +328,9 @@ export const Provision: MessageFns<Provision> = {
fromPartial<I extends Exact<DeepPartial<Provision>, I>>(object: I): Provision {
const message = createBaseProvision();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.crew = object.crew ?? undefined;
message.game_mode_exclusive = object.game_mode_exclusive ?? false;
message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
+1 -1
View File
@@ -180,7 +180,7 @@ export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Review.fromPartial(object.value)
: createBaseReview();
: undefined;
return message;
},
};
@@ -175,9 +175,7 @@ export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesE
): SkillDefinitions_ClassesEntry {
const message = createBaseSkillDefinitions_ClassesEntry();
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Skill.fromPartial(object.value)
: createBaseSkill();
message.value = (object.value !== undefined && object.value !== null) ? Skill.fromPartial(object.value) : undefined;
return message;
},
};
@@ -2,6 +2,8 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message TankDefinitions {
map<uint32, TankDefinition> tanks = 1;
}
@@ -9,7 +11,6 @@ message TankDefinitions {
message TankDefinition {
required uint32 id = 1;
map<uint32, uint32> roles = 2;
optional string description = 3;
required bool fixed_camouflage = 4;
repeated int32 camouflages = 5;
repeated int32 ancestors = 6;
@@ -17,8 +18,8 @@ message TankDefinition {
repeated Crew crew = 8;
required uint32 health = 9;
required string nation = 10;
required string name = 11;
optional string name_full = 12;
required I18nString name = 11;
optional I18nString name_full = 12;
required TankType type = 13;
required uint32 max_consumables = 14;
required uint32 max_provisions = 15;
@@ -65,7 +66,7 @@ message TankPrice {
message TrackDefinition {
required uint32 id = 1;
required uint32 tier = 2;
required string name = 3;
required I18nString name = 3;
required uint32 weight = 4;
required float traverse_speed = 5;
optional ResearchCost research_cost = 6;
@@ -79,7 +80,7 @@ message TrackDefinition {
message EngineDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
optional ResearchCost research_cost = 3;
required uint32 tier = 4;
required float fire_chance = 5;
@@ -94,7 +95,7 @@ message TurretDefinition {
required uint32 view_range = 3;
required float traverse_speed = 4;
optional ResearchCost research_cost = 5;
required string name = 6;
required I18nString name = 6;
required uint32 tier = 7;
required uint32 weight = 8;
repeated GunDefinition guns = 9;
@@ -145,7 +146,7 @@ message GunDefinitionBase {
required float rotation_speed = 2;
optional ResearchCost research_cost = 3;
required uint32 weight = 4;
required string name = 5;
required I18nString name = 5;
required uint32 tier = 6;
repeated ShellDefinition shells = 7;
required float camouflage_loss = 8;
@@ -160,7 +161,7 @@ message GunDefinitionBase {
message ShellDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
required uint32 velocity = 3;
required uint32 armor_damage = 4;
required uint32 module_damage = 5;
+87 -81
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -261,7 +262,6 @@ export interface TankDefinitions_TanksEntry {
export interface TankDefinition {
id: number;
roles: { [key: number]: number };
description?: string | undefined;
fixed_camouflage: boolean;
camouflages: number[];
ancestors: number[];
@@ -269,8 +269,8 @@ export interface TankDefinition {
crew: Crew[];
health: number;
nation: string;
name: string;
name_full?: string | undefined;
name: I18nString;
name_full?: I18nString | undefined;
type: TankType;
max_consumables: number;
max_provisions: number;
@@ -317,7 +317,7 @@ export interface TankPrice {
export interface TrackDefinition {
id: number;
tier: number;
name: string;
name: I18nString;
weight: number;
traverse_speed: number;
research_cost?: ResearchCost | undefined;
@@ -331,7 +331,7 @@ export interface TrackDefinition {
export interface EngineDefinition {
id: number;
name: string;
name: I18nString;
research_cost?: ResearchCost | undefined;
tier: number;
fire_chance: number;
@@ -346,7 +346,7 @@ export interface TurretDefinition {
view_range: number;
traverse_speed: number;
research_cost?: ResearchCost | undefined;
name: string;
name: I18nString;
tier: number;
weight: number;
guns: GunDefinition[];
@@ -397,7 +397,7 @@ export interface GunDefinitionBase {
rotation_speed: number;
research_cost?: ResearchCost | undefined;
weight: number;
name: string;
name: I18nString;
tier: number;
shells: ShellDefinition[];
camouflage_loss: number;
@@ -412,7 +412,7 @@ export interface GunDefinitionBase {
export interface ShellDefinition {
id: number;
name: string;
name: I18nString;
velocity: number;
armor_damage: number;
module_damage: number;
@@ -603,7 +603,7 @@ export const TankDefinitions_TanksEntry: MessageFns<TankDefinitions_TanksEntry>
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TankDefinition.fromPartial(object.value)
: createBaseTankDefinition();
: undefined;
return message;
},
};
@@ -612,7 +612,6 @@ function createBaseTankDefinition(): TankDefinition {
return {
id: 0,
roles: {},
description: undefined,
fixed_camouflage: false,
camouflages: [],
ancestors: [],
@@ -620,7 +619,7 @@ function createBaseTankDefinition(): TankDefinition {
crew: [],
health: 0,
nation: "",
name: "",
name: createBaseI18nString(),
name_full: undefined,
type: 0,
max_consumables: 0,
@@ -652,9 +651,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
Object.entries(message.roles).forEach(([key, value]) => {
TankDefinition_RolesEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join();
});
if (message.description !== undefined && message.description !== undefined) {
writer.uint32(26).string(message.description);
}
if (message.fixed_camouflage !== false) {
writer.uint32(32).bool(message.fixed_camouflage);
}
@@ -682,11 +678,11 @@ export const TankDefinition: MessageFns<TankDefinition> = {
if (message.nation !== "") {
writer.uint32(82).string(message.nation);
}
if (message.name !== "") {
writer.uint32(90).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(90).fork()).join();
}
if (message.name_full !== undefined && message.name_full !== undefined) {
writer.uint32(98).string(message.name_full);
if (message.name_full !== undefined) {
I18nString.encode(message.name_full, writer.uint32(98).fork()).join();
}
if (message.type !== 0) {
writer.uint32(104).int32(message.type);
@@ -774,14 +770,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
}
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.description = reader.string();
continue;
}
case 4: {
if (tag !== 32) {
break;
@@ -873,7 +861,7 @@ export const TankDefinition: MessageFns<TankDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 12: {
@@ -881,7 +869,7 @@ export const TankDefinition: MessageFns<TankDefinition> = {
break;
}
message.name_full = reader.string();
message.name_full = I18nString.decode(reader, reader.uint32());
continue;
}
case 13: {
@@ -1054,7 +1042,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
return acc;
}, {})
: {},
description: isSet(object.description) ? globalThis.String(object.description) : undefined,
fixed_camouflage: globalThis.Boolean(assertSet("TankDefinition.fixed_camouflage", object.fixed_camouflage)),
camouflages: globalThis.Array.isArray(object?.camouflages)
? object.camouflages.map((e: any) => globalThis.Number(e))
@@ -1068,8 +1055,8 @@ export const TankDefinition: MessageFns<TankDefinition> = {
crew: globalThis.Array.isArray(object?.crew) ? object.crew.map((e: any) => Crew.fromJSON(e)) : [],
health: globalThis.Number(assertSet("TankDefinition.health", object.health)),
nation: globalThis.String(assertSet("TankDefinition.nation", object.nation)),
name: globalThis.String(assertSet("TankDefinition.name", object.name)),
name_full: isSet(object.name_full) ? globalThis.String(object.name_full) : undefined,
name: I18nString.fromJSON(assertSet("TankDefinition.name", object.name)),
name_full: isSet(object.name_full) ? I18nString.fromJSON(object.name_full) : undefined,
type: tankTypeFromJSON(assertSet("TankDefinition.type", object.type)),
max_consumables: globalThis.Number(assertSet("TankDefinition.max_consumables", object.max_consumables)),
max_provisions: globalThis.Number(assertSet("TankDefinition.max_provisions", object.max_provisions)),
@@ -1112,9 +1099,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
});
}
}
if (message.description !== undefined && message.description !== undefined) {
obj.description = message.description;
}
if (message.fixed_camouflage !== false) {
obj.fixed_camouflage = message.fixed_camouflage;
}
@@ -1136,11 +1120,11 @@ export const TankDefinition: MessageFns<TankDefinition> = {
if (message.nation !== "") {
obj.nation = message.nation;
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.name_full !== undefined && message.name_full !== undefined) {
obj.name_full = message.name_full;
if (message.name_full !== undefined) {
obj.name_full = I18nString.toJSON(message.name_full);
}
if (message.type !== 0) {
obj.type = tankTypeToJSON(message.type);
@@ -1214,7 +1198,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
}
return acc;
}, {});
message.description = object.description ?? undefined;
message.fixed_camouflage = object.fixed_camouflage ?? false;
message.camouflages = object.camouflages?.map((e) => e) || [];
message.ancestors = object.ancestors?.map((e) => e) || [];
@@ -1222,8 +1205,12 @@ export const TankDefinition: MessageFns<TankDefinition> = {
message.crew = object.crew?.map((e) => Crew.fromPartial(e)) || [];
message.health = object.health ?? 0;
message.nation = object.nation ?? "";
message.name = object.name ?? "";
message.name_full = object.name_full ?? undefined;
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.name_full = (object.name_full !== undefined && object.name_full !== null)
? I18nString.fromPartial(object.name_full)
: undefined;
message.type = object.type ?? 0;
message.max_consumables = object.max_consumables ?? 0;
message.max_provisions = object.max_provisions ?? 0;
@@ -1582,7 +1569,7 @@ function createBaseTrackDefinition(): TrackDefinition {
return {
id: 0,
tier: 0,
name: "",
name: createBaseI18nString(),
weight: 0,
traverse_speed: 0,
research_cost: undefined,
@@ -1603,8 +1590,8 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
if (message.tier !== 0) {
writer.uint32(16).uint32(message.tier);
}
if (message.name !== "") {
writer.uint32(26).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(26).fork()).join();
}
if (message.weight !== 0) {
writer.uint32(32).uint32(message.weight);
@@ -1664,7 +1651,7 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 4: {
@@ -1752,7 +1739,7 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
return {
id: globalThis.Number(assertSet("TrackDefinition.id", object.id)),
tier: globalThis.Number(assertSet("TrackDefinition.tier", object.tier)),
name: globalThis.String(assertSet("TrackDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("TrackDefinition.name", object.name)),
weight: globalThis.Number(assertSet("TrackDefinition.weight", object.weight)),
traverse_speed: globalThis.Number(assertSet("TrackDefinition.traverse_speed", object.traverse_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
@@ -1775,8 +1762,8 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.weight !== 0) {
obj.weight = Math.round(message.weight);
@@ -1815,7 +1802,9 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
const message = createBaseTrackDefinition();
message.id = object.id ?? 0;
message.tier = object.tier ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.weight = object.weight ?? 0;
message.traverse_speed = object.traverse_speed ?? 0;
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
@@ -1832,7 +1821,16 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
};
function createBaseEngineDefinition(): EngineDefinition {
return { id: 0, name: "", research_cost: undefined, tier: 0, fire_chance: 0, power: 0, weight: 0, unlocks: [] };
return {
id: 0,
name: createBaseI18nString(),
research_cost: undefined,
tier: 0,
fire_chance: 0,
power: 0,
weight: 0,
unlocks: [],
};
}
export const EngineDefinition: MessageFns<EngineDefinition> = {
@@ -1840,8 +1838,8 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.research_cost !== undefined) {
ResearchCost.encode(message.research_cost, writer.uint32(26).fork()).join();
@@ -1884,7 +1882,7 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -1947,7 +1945,7 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
fromJSON(object: any): EngineDefinition {
return {
id: globalThis.Number(assertSet("EngineDefinition.id", object.id)),
name: globalThis.String(assertSet("EngineDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("EngineDefinition.name", object.name)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
tier: globalThis.Number(assertSet("EngineDefinition.tier", object.tier)),
fire_chance: globalThis.Number(assertSet("EngineDefinition.fire_chance", object.fire_chance)),
@@ -1962,8 +1960,8 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.research_cost !== undefined) {
obj.research_cost = ResearchCost.toJSON(message.research_cost);
@@ -1992,7 +1990,9 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
fromPartial<I extends Exact<DeepPartial<EngineDefinition>, I>>(object: I): EngineDefinition {
const message = createBaseEngineDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
? ResearchCost.fromPartial(object.research_cost)
: undefined;
@@ -2012,7 +2012,7 @@ function createBaseTurretDefinition(): TurretDefinition {
view_range: 0,
traverse_speed: 0,
research_cost: undefined,
name: "",
name: createBaseI18nString(),
tier: 0,
weight: 0,
guns: [],
@@ -2037,8 +2037,8 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
if (message.research_cost !== undefined) {
ResearchCost.encode(message.research_cost, writer.uint32(42).fork()).join();
}
if (message.name !== "") {
writer.uint32(50).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(50).fork()).join();
}
if (message.tier !== 0) {
writer.uint32(56).uint32(message.tier);
@@ -2107,7 +2107,7 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 7: {
@@ -2158,7 +2158,7 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
view_range: globalThis.Number(assertSet("TurretDefinition.view_range", object.view_range)),
traverse_speed: globalThis.Number(assertSet("TurretDefinition.traverse_speed", object.traverse_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
name: globalThis.String(assertSet("TurretDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("TurretDefinition.name", object.name)),
tier: globalThis.Number(assertSet("TurretDefinition.tier", object.tier)),
weight: globalThis.Number(assertSet("TurretDefinition.weight", object.weight)),
guns: globalThis.Array.isArray(object?.guns) ? object.guns.map((e: any) => GunDefinition.fromJSON(e)) : [],
@@ -2183,8 +2183,8 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
if (message.research_cost !== undefined) {
obj.research_cost = ResearchCost.toJSON(message.research_cost);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
@@ -2213,7 +2213,9 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
? ResearchCost.fromPartial(object.research_cost)
: undefined;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tier = object.tier ?? 0;
message.weight = object.weight ?? 0;
message.guns = object.guns?.map((e) => GunDefinition.fromPartial(e)) || [];
@@ -2858,7 +2860,7 @@ function createBaseGunDefinitionBase(): GunDefinitionBase {
rotation_speed: 0,
research_cost: undefined,
weight: 0,
name: "",
name: createBaseI18nString(),
tier: 0,
shells: [],
camouflage_loss: 0,
@@ -2886,8 +2888,8 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
if (message.weight !== 0) {
writer.uint32(32).uint32(message.weight);
}
if (message.name !== "") {
writer.uint32(42).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(42).fork()).join();
}
if (message.tier !== 0) {
writer.uint32(48).uint32(message.tier);
@@ -2966,7 +2968,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 6: {
@@ -3064,7 +3066,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
rotation_speed: globalThis.Number(assertSet("GunDefinitionBase.rotation_speed", object.rotation_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
weight: globalThis.Number(assertSet("GunDefinitionBase.weight", object.weight)),
name: globalThis.String(assertSet("GunDefinitionBase.name", object.name)),
name: I18nString.fromJSON(assertSet("GunDefinitionBase.name", object.name)),
tier: globalThis.Number(assertSet("GunDefinitionBase.tier", object.tier)),
shells: globalThis.Array.isArray(object?.shells)
? object.shells.map((e: any) => ShellDefinition.fromJSON(e))
@@ -3098,8 +3100,8 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
if (message.weight !== 0) {
obj.weight = Math.round(message.weight);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
@@ -3145,7 +3147,9 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
? ResearchCost.fromPartial(object.research_cost)
: undefined;
message.weight = object.weight ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tier = object.tier ?? 0;
message.shells = object.shells?.map((e) => ShellDefinition.fromPartial(e)) || [];
message.camouflage_loss = object.camouflage_loss ?? 0;
@@ -3163,7 +3167,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
function createBaseShellDefinition(): ShellDefinition {
return {
id: 0,
name: "",
name: createBaseI18nString(),
velocity: 0,
armor_damage: 0,
module_damage: 0,
@@ -3183,8 +3187,8 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.velocity !== 0) {
writer.uint32(24).uint32(message.velocity);
@@ -3242,7 +3246,7 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -3345,7 +3349,7 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
fromJSON(object: any): ShellDefinition {
return {
id: globalThis.Number(assertSet("ShellDefinition.id", object.id)),
name: globalThis.String(assertSet("ShellDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("ShellDefinition.name", object.name)),
velocity: globalThis.Number(assertSet("ShellDefinition.velocity", object.velocity)),
armor_damage: globalThis.Number(assertSet("ShellDefinition.armor_damage", object.armor_damage)),
module_damage: globalThis.Number(assertSet("ShellDefinition.module_damage", object.module_damage)),
@@ -3365,8 +3369,8 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.velocity !== 0) {
obj.velocity = Math.round(message.velocity);
@@ -3410,7 +3414,9 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
fromPartial<I extends Exact<DeepPartial<ShellDefinition>, I>>(object: I): ShellDefinition {
const message = createBaseShellDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.velocity = object.velocity ?? 0;
message.armor_damage = object.armor_damage ?? 0;
message.module_damage = object.module_damage ?? 0;
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"]
"include": ["src/**/*", "../i18n/src/strings.ts"]
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "@blitzkit/i18n",
"private": true,
"type": "module",
"main": "src/index.ts",
"dependencies": {
"ts-deepmerge": "^7.0.2"
},
"devDependencies": {}
}
+3
View File
@@ -0,0 +1,3 @@
export * from './literals';
export * from './strings';
export * from './unwrapper';
+5
View File
@@ -0,0 +1,5 @@
export function literals(string: string, literals: string[]) {
return literals.reduce((accumulator, literal, index) => {
return accumulator.replace(`%s${index + 1}`, literal);
}, string);
}
+100
View File
@@ -0,0 +1,100 @@
import { merge } from 'ts-deepmerge';
import en from '../strings/en.json';
import es from '../strings/es.json';
import fr from '../strings/fr.json';
import ja from '../strings/ja.json';
import pl from '../strings/pl.json';
import pt from '../strings/pt.json';
import ru from '../strings/ru.json';
import uk from '../strings/uk.json';
import zh from '../strings/zh.json';
export type BlitzKitStrings = typeof en;
const stringsRaw = { en, es, fr, ja, pl, pt, ru, uk, zh };
export const SUPPORTED_LOCALES = Object.keys(stringsRaw) as SupportedLocale[];
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
function redact(string: string) {
const matches = [...string.matchAll(/%s\d+/g)];
const characters = string
.split('')
.map((char) => (char === ' ' ? ' ' : '█')) as string[];
matches.forEach((match) => {
const start = match.index;
const matchText = match[0];
for (let i = 0; i < matchText.length; i++) {
characters[start + i] = matchText[i];
}
});
return characters.join('');
}
function nuke(object: TranslationTree) {
for (const key in object) {
const typedKey = key as keyof typeof object;
if (typeof object[typedKey] === 'string') {
const value = object[typedKey];
object[typedKey] = redact(value as string);
} else nuke(object[typedKey] as TranslationTree);
}
}
const stringsPartial: Partial<Record<SupportedLocale, TranslationTree>> = {};
for (const locale in stringsRaw) {
if (locale === DEFAULT_LOCALE) {
stringsPartial[DEFAULT_LOCALE] = stringsRaw[DEFAULT_LOCALE];
continue;
}
const localeStrings = stringsRaw[locale as SupportedLocale];
stringsPartial[locale as SupportedLocale] = merge(
stringsRaw[DEFAULT_LOCALE],
localeStrings,
);
if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') {
nuke(stringsPartial[locale as SupportedLocale]!);
}
}
export const STRINGS = stringsPartial as Record<
SupportedLocale,
BlitzKitStrings
>;
export type SupportedLocale = keyof typeof stringsRaw;
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
en: 'en',
es: 'es',
fr: 'fr',
ja: 'ja',
pt: 'pt',
ru: 'ru',
uk: 'uk',
zh: 'zh-Hans',
pl: 'pl',
};
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
en: '🇺🇸',
es: '🇪🇸',
fr: '🇫🇷',
ja: '🇯🇵',
pt: '🇵🇹',
ru: '🇷🇺',
uk: '🇺🇦',
zh: '🇨🇳',
pl: '🇵🇱',
};
export type TranslationTree = { [key: string]: TranslationNode };
export type TranslationNode = string | TranslationTree;
+12
View File
@@ -0,0 +1,12 @@
import { I18nString } from '@blitzkit/core';
import { DEFAULT_LOCALE } from './strings';
export function unwrapper(locale: string) {
return function (i18nString: I18nString) {
if (locale in i18nString.locales) {
return i18nString.locales[locale];
}
return i18nString.locales[DEFAULT_LOCALE];
};
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Loaded 100 of 300 files, more files were not shown because too many files have changed in this diff. Show more