diff --git a/.github/workflows/assets.yml b/.github/workflows/assets.yml index 2615428af..446da6c10 100644 --- a/.github/workflows/assets.yml +++ b/.github/workflows/assets.yml @@ -39,6 +39,17 @@ env: PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }} PUBLIC_ASSET_BRANCH: ${{ github.event.inputs.cdn_branch || secrets.PUBLIC_ASSET_BRANCH }} + WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }} + WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }} + PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }} + ALLOW_ROBOTS: true + PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }} + PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }} + PUBLIC_PATREON_CLIENT_ID: ${{ secrets.PUBLIC_PATREON_CLIENT_ID }} + PUBLIC_PATREON_REDIRECT_URI: ${{ secrets.PUBLIC_PATREON_REDIRECT_URI }} + PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }} + PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }} + PUBLIC_DEBUG_MISSING_I18N: false jobs: download_client: @@ -106,6 +117,9 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive + token: ${{ secrets.GH_TOKEN }} - name: Setup bun uses: oven-sh/setup-bun@v1 @@ -127,7 +141,7 @@ jobs: - name: Start server and build assets run: | - bun dev:website & + bun dev:website --verbosepa & SERVER_PID=$! bun run build:assets --all-targets --depot --production diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index c7e02863f..5ec5db354 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -2,7 +2,6 @@ name: Website on: push: - branches: main workflow_dispatch: schedule: @@ -27,6 +26,8 @@ env: PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }} PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }} GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} + WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }} + WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }} jobs: build: @@ -53,6 +54,7 @@ jobs: path: dist/website deploy-github-pages: + if: ${{ github.ref == 'refs/heads/main' }} name: Deploy to GitHub Pages runs-on: ubuntu-latest needs: build diff --git a/.gitignore b/.gitignore index efe1d5f02..af17ed83d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ target /.vercel /.astro .vercel +/src \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6be5a5ada..7817465ba 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -189,6 +189,7 @@ "unpickled", "Unregistering", "unrotate", + "unwrapper", "vercel", "vickers", "vitepress", diff --git a/bun.lockb b/bun.lockb index b09f501d1..9c71f03bf 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/crowdin.yml b/crowdin.yml index c329bb496..b7da98827 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,3 @@ files: - - source: /packages/core/lang/en.json + - source: /packages/i18n/strings/en.json translation: /%original_path%/%locale%.json diff --git a/docs/changelogs/1.27.0.md b/docs/changelogs/1.27.0.md new file mode 100644 index 000000000..94eb87113 --- /dev/null +++ b/docs/changelogs/1.27.0.md @@ -0,0 +1,14 @@ +# BlitzKit 1.27.0 + +PLEASE HELP TRANSLATE BLITZKIT: https://crowdin.com/project/blitzkrieg + +This update will be the last major update before the release of World of Tanks Blitz Unreal Engine. + +## Features + +- Added support for English, Spanish, French, Japanese, Polish, Portuguese, Russian, Ukrainian, and Chinese to the website and bot + - You can switch languages through the home page, the footer of any page, or the settings page + +## Fixes + +- Fixed the units for shell capacity diff --git a/docs/changelogs/1.27.1.md b/docs/changelogs/1.27.1.md new file mode 100644 index 000000000..85fae4021 --- /dev/null +++ b/docs/changelogs/1.27.1.md @@ -0,0 +1,7 @@ +# BlitzKit 1.27.1 + +## Fixes + +- Fixed a crash that occurs if tanks are deleted in Blitz and were once available in BlitzKit +- All tank icons are now back +- The tank search once again returns results diff --git a/docs/changelogs/1.27.2.md b/docs/changelogs/1.27.2.md new file mode 100644 index 000000000..687f37199 --- /dev/null +++ b/docs/changelogs/1.27.2.md @@ -0,0 +1,7 @@ +# BlitzKit 1.27.2 + +This update only affects the website. + +## Fixes + +- The new Improved Vertical Stabilizer equipment which only affects the gun depression found on the Type 63 now works diff --git a/docs/changelogs/1.27.3.md b/docs/changelogs/1.27.3.md new file mode 100644 index 000000000..73c49da1e --- /dev/null +++ b/docs/changelogs/1.27.3.md @@ -0,0 +1,9 @@ +# BlitzKit 1.27.3 + +This update only affects the bot. + +## Fixes + +- Added support for 64-bit IDs allowing users with new accounts to link with BlitzKit + - This comes at the added cost of resetting the links of all other accounts + - Just re-link and you're good to go diff --git a/docs/changelogs/1.27.4.md b/docs/changelogs/1.27.4.md new file mode 100644 index 000000000..eb1851f3f --- /dev/null +++ b/docs/changelogs/1.27.4.md @@ -0,0 +1,3 @@ +# FreakKit 1.27.4 + +It has come to our attention that tanks are very harsh objects. To even it out, we made the website a pretty pretty princess <3 diff --git a/docs/changelogs/1.27.5.md b/docs/changelogs/1.27.5.md new file mode 100644 index 000000000..ec1b77275 --- /dev/null +++ b/docs/changelogs/1.27.5.md @@ -0,0 +1,11 @@ +# BlitzKit 1.27.5 + +This update only affects the bot. + +## Changes + +- Removed `/auction` + +## Fixes + +- Fixed a bug that caused `/rating` to disappear diff --git a/package.json b/package.json index b0bd27fad..c07be3556 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "blitzkit", "private": true, - "version": "1.26.6-preview.1", + "version": "1.27.5", "description": "🎉 Tools for everything World of Tanks Blitz", "type": "module", "types": "./types.d.ts", @@ -35,7 +35,8 @@ "build:protos": "bun packages/scripts/src/protos.ts", "preview:website": "cd packages/website && bun run preview", "build:reviews": "cd packages/scripts && bun build:reviews", - "build:ids": "cd packages/scripts && bun build:ids" + "build:ids": "cd packages/scripts && bun build:ids", + "prunestrings": "cd packages/scripts && bun prunestrings" }, "license": "MIT", "dependencies": { diff --git a/packages/bot/package.json b/packages/bot/package.json index 6e5c7e2fe..aae56319d 100644 --- a/packages/bot/package.json +++ b/packages/bot/package.json @@ -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", diff --git a/packages/bot/src/commands/about.ts b/packages/bot/src/commands/about.ts index 8908b63de..8a5bdb3ad 100644 --- a/packages/bot/src/commands/about.ts +++ b/packages/bot/src/commands/about.ts @@ -28,10 +28,10 @@ export const aboutCommand = new Promise(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) => diff --git a/packages/bot/src/commands/auction.ts b/packages/bot/src/commands/auction.ts index cc8da1920..68c647f46 100644 --- a/packages/bot/src/commands/auction.ts +++ b/packages/bot/src/commands/auction.ts @@ -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((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((resolve) => { pageSize }&type[]=vehicle&saleable=${saleable}`, ).then((response) => response.json() as Promise); - 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}`, ``, '', @@ -112,15 +113,18 @@ export const auctionCommand = new Promise((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; diff --git a/packages/bot/src/commands/breakdown/render.tsx b/packages/bot/src/commands/breakdown/render.tsx index 13f8e6e11..c62869b4e 100644 --- a/packages/bot/src/commands/breakdown/render.tsx +++ b/packages/bot/src/commands/breakdown/render.tsx @@ -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( ((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((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')}`; }, diff --git a/packages/bot/src/commands/inactive.tsx b/packages/bot/src/commands/inactive.tsx index 333acbec8..ad3f49415 100644 --- a/packages/bot/src/commands/inactive.tsx +++ b/packages/bot/src/commands/inactive.tsx @@ -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((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((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((resolve) => { {!hasInactiveMembers && ( diff --git a/packages/bot/src/commands/link.tsx b/packages/bot/src/commands/link.tsx index 314177f5b..a80f15c3a 100644 --- a/packages/bot/src/commands/link.tsx +++ b/packages/bot/src/commands/link.tsx @@ -22,7 +22,7 @@ export const verifyCommand = new Promise((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((resolve) => { height: 16, }} /> - {t`bot.commands.link.body.accounts_linked`} + + {strings.bot.commands.link.body.accounts_linked} +
((resolve) => { {clanAccountInfo?.clan ? `[${clanAccountInfo?.clan?.tag}] • ` : ''} - {translate(`common.regions.normal.${region}`)} + {strings.common.regions.normal[region]}
@@ -185,8 +185,8 @@ export const verifyCommand = new Promise((resolve) => { , 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, ), ]; }, diff --git a/packages/bot/src/commands/ownedTanks.tsx b/packages/bot/src/commands/ownedTanks.tsx index 4f98f9781..af67a00ad 100644 --- a/packages/bot/src/commands/ownedTanks.tsx +++ b/packages/bot/src/commands/ownedTanks.tsx @@ -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((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((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((resolve) => { ) .map( (tank) => - `[${flags[tank.nation]} ${markdownEscape(tank.name)}]()`, + `[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}]()`, ); const lines = [title, ...tankList]; diff --git a/packages/bot/src/commands/permissions.ts b/packages/bot/src/commands/permissions.ts index 9afd85be9..8dad1039a 100644 --- a/packages/bot/src/commands/permissions.ts +++ b/packages/bot/src/commands/permissions.ts @@ -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((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((resolve) => { }) .join('\n'); - return translate('bot.commands.permissions.body.title', [body]); + return literals(strings.bot.commands.permissions.body.title, [body]); }, }); }); diff --git a/packages/bot/src/commands/ping.ts b/packages/bot/src/commands/ping.ts index 1823f3a33..fcf896313 100644 --- a/packages/bot/src/commands/ping.ts +++ b/packages/bot/src/commands/ping.ts @@ -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((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)}`, ]), ); diff --git a/packages/bot/src/commands/playerInfo.ts b/packages/bot/src/commands/playerInfo.ts index cdb7d7d72..5d4be9776 100644 --- a/packages/bot/src/commands/playerInfo.ts +++ b/packages/bot/src/commands/playerInfo.ts @@ -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((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((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, ), diff --git a/packages/bot/src/commands/rating.tsx b/packages/bot/src/commands/rating.tsx index 1188c3706..eb0a38613 100644 --- a/packages/bot/src/commands/rating.tsx +++ b/packages/bot/src/commands/rating.tsx @@ -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((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((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((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((resolve) => {
( ); 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( ) .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( 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( 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 + )[name], value: `${value}`, name_localizations: localizationObject( - `common.leagues.${name}`, + (strings) => + ( + strings.common.leagues as Record + )[name], ), }) satisfies APIApplicationCommandOptionChoice, ), @@ -189,7 +211,7 @@ export const ratingLeaderboardCommand = new Promise( // 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( 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( 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( : regionRatingInfo.count) - result[result.length - 1].position; titleName = `${ leagueInfo - ? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`) + ? (strings.common.leagues as Record)[ + 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( 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( 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( ? `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( {items} {playersAfter > 0 && ( diff --git a/packages/bot/src/commands/replay.tsx b/packages/bot/src/commands/replay.tsx index c8dacfce8..d7af17ca5 100644 --- a/packages/bot/src/commands/replay.tsx +++ b/packages/bot/src/commands/replay.tsx @@ -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( }} > {tank.name} - {tank.name} + {unwrap(tank.name)}
@@ -530,6 +534,7 @@ export const replayCommand = new Promise((resolve) => { ) .map((player) => playerListing( + interaction.locale, player, true, replayData.winner_team, @@ -553,7 +558,12 @@ export const replayCommand = new Promise((resolve) => { players .filter((player) => player.team === antagonistTeamId) .map((player) => - playerListing(player, false, replayData.winner_team), + playerListing( + interaction.locale, + player, + false, + replayData.winner_team, + ), ), )} diff --git a/packages/bot/src/commands/research.tsx b/packages/bot/src/commands/research.tsx index 5e3925279..c7e98558e 100644 --- a/packages/bot/src/commands/research.tsx +++ b/packages/bot/src/commands/research.tsx @@ -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((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((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((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((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((resolve) => { @@ -292,7 +302,7 @@ export const researchCommand = new Promise((resolve) => { color: theme.colors.textHighContrast, }} > - {t`bot.commands.research.body.total`} + {strings.bot.commands.research.body.total} @@ -452,7 +462,7 @@ export const researchCommand = new Promise((resolve) => { }} > {tank.name}((resolve) => { color: theme.colors.textHighContrast, }} > - {tank.name} + {unwrap(tank.name)} @@ -497,11 +507,14 @@ export const researchCommand = new Promise((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, + )}`, + ], + )} @@ -527,9 +540,10 @@ export const researchCommand = new Promise((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()], + )} )} @@ -555,11 +569,14 @@ export const researchCommand = new Promise((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, + ), + ], + )} @@ -584,11 +601,14 @@ export const researchCommand = new Promise((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, + ), + ], + )} @@ -602,7 +622,7 @@ export const researchCommand = new Promise((resolve) => { return startingTankRaw ? image - : [t`bot.commands.research.body.estimation`, image]; + : [strings.bot.commands.research.body.estimation, image]; }, autocomplete(interaction) { diff --git a/packages/bot/src/commands/searchClans.ts b/packages/bot/src/commands/searchClans.ts index 03229ded0..0d481d68f 100644 --- a/packages/bot/src/commands/searchClans.ts +++ b/packages/bot/src/commands/searchClans.ts @@ -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((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((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((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\`\`\``, diff --git a/packages/bot/src/commands/searchPlayers.ts b/packages/bot/src/commands/searchPlayers.ts index 4efa80c3c..0b766cb76 100644 --- a/packages/bot/src/commands/searchPlayers.ts +++ b/packages/bot/src/commands/searchPlayers.ts @@ -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((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((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((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((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') }\`\`\``, ); diff --git a/packages/bot/src/components/AllStatsOverview/index.tsx b/packages/bot/src/components/AllStatsOverview/index.tsx index c7eccb958..6783c4b5e 100644 --- a/packages/bot/src/components/AllStatsOverview/index.tsx +++ b/packages/bot/src/components/AllStatsOverview/index.tsx @@ -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({ }} > @@ -90,7 +90,7 @@ export function AllStatsOverview({ @@ -103,25 +103,25 @@ export function AllStatsOverview({ }} > diff --git a/packages/bot/src/components/CommandWrapper.tsx b/packages/bot/src/components/CommandWrapper.tsx index e03c2efca..8fe8f612c 100644 --- a/packages/bot/src/components/CommandWrapper.tsx +++ b/packages/bot/src/components/CommandWrapper.tsx @@ -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} diff --git a/packages/bot/src/components/GenericAllStats/index.tsx b/packages/bot/src/components/GenericAllStats/index.tsx index 30e41cb8c..a5650c848 100644 --- a/packages/bot/src/components/GenericAllStats/index.tsx +++ b/packages/bot/src/components/GenericAllStats/index.tsx @@ -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({ - {translate(`bot.common.no_data.${type}`)} + {strings.bot.common.no_data[type]} ); diff --git a/packages/bot/src/core/blitz/resolveTankId.ts b/packages/bot/src/core/blitz/resolveTankId.ts index 01d2034be..65eda9de6 100644 --- a/packages/bot/src/core/blitz/resolveTankId.ts +++ b/packages/bot/src/core/blitz/resolveTankId.ts @@ -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}`]), ); } } diff --git a/packages/bot/src/core/blitz/searchClansAcrossRegionsBotWrapper.ts b/packages/bot/src/core/blitz/searchClansAcrossRegionsBotWrapper.ts index 9f9909bf4..c06e03b30 100644 --- a/packages/bot/src/core/blitz/searchClansAcrossRegionsBotWrapper.ts +++ b/packages/bot/src/core/blitz/searchClansAcrossRegionsBotWrapper.ts @@ -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); diff --git a/packages/bot/src/core/blitz/searchPlayersAcrossRegionsBotWrapper.ts b/packages/bot/src/core/blitz/searchPlayersAcrossRegionsBotWrapper.ts index 2e8c10779..89e473a8d 100644 --- a/packages/bot/src/core/blitz/searchPlayersAcrossRegionsBotWrapper.ts +++ b/packages/bot/src/core/blitz/searchPlayersAcrossRegionsBotWrapper.ts @@ -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); diff --git a/packages/bot/src/core/blitzkit/filtersToDescription.ts b/packages/bot/src/core/blitzkit/filtersToDescription.ts index f022a9006..a146c58cd 100644 --- a/packages/bot/src/core/blitzkit/filtersToDescription.ts +++ b/packages/bot/src/core/blitzkit/filtersToDescription.ts @@ -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)[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(', '); } diff --git a/packages/bot/src/core/blitzkit/getBlitzFromDiscord.ts b/packages/bot/src/core/blitzkit/getBlitzFromDiscord.ts index 4821d1291..4bb31df02 100644 --- a/packages/bot/src/core/blitzkit/getBlitzFromDiscord.ts +++ b/packages/bot/src/core/blitzkit/getBlitzFromDiscord.ts @@ -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) }; } diff --git a/packages/bot/src/core/blitzstars/getTankHistories.ts b/packages/bot/src/core/blitzstars/getTankHistories.ts index fd45a2f17..87cbbf2b2 100644 --- a/packages/bot/src/core/blitzstars/getTankHistories.ts +++ b/packages/bot/src/core/blitzstars/getTankHistories.ts @@ -38,7 +38,7 @@ export async function getTankHistories( locale: Locale, options?: Partial, ) { - 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) => { diff --git a/packages/bot/src/core/discord/addClanChoices.ts b/packages/bot/src/core/discord/addClanChoices.ts index f1d2f6ea7..328ee5c34 100644 --- a/packages/bot/src/core/discord/addClanChoices.ts +++ b/packages/bot/src/core/discord/addClanChoices.ts @@ -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) diff --git a/packages/bot/src/core/discord/addFilterOptions.ts b/packages/bot/src/core/discord/addFilterOptions.ts index e1a91121c..5eb27d5ef 100644 --- a/packages/bot/src/core/discord/addFilterOptions.ts +++ b/packages/bot/src/core/discord/addFilterOptions.ts @@ -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)[ + nation + ], name_localizations: localizationObject( - `common.nations.${nation}`, + (strings) => + (strings.common.nations as Record)[ + 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, ), diff --git a/packages/bot/src/core/discord/addPeriodSubCommands.ts b/packages/bot/src/core/discord/addPeriodSubCommands.ts index cbb3af751..94cf335d4 100644 --- a/packages/bot/src/core/discord/addPeriodSubCommands.ts +++ b/packages/bot/src/core/discord/addPeriodSubCommands.ts @@ -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), ), diff --git a/packages/bot/src/core/discord/addRegionChoices.ts b/packages/bot/src/core/discord/addRegionChoices.ts index 940ce69af..6d6fce252 100644 --- a/packages/bot/src/core/discord/addRegionChoices.ts +++ b/packages/bot/src/core/discord/addRegionChoices.ts @@ -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, ), diff --git a/packages/bot/src/core/discord/addTankChoices.ts b/packages/bot/src/core/discord/addTankChoices.ts index 9db698e2a..0cfafc6f8 100644 --- a/packages/bot/src/core/discord/addTankChoices.ts +++ b/packages/bot/src/core/discord/addTankChoices.ts @@ -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); diff --git a/packages/bot/src/core/discord/addTierChoices.ts b/packages/bot/src/core/discord/addTierChoices.ts index 693632df4..76ae80cb3 100644 --- a/packages/bot/src/core/discord/addTierChoices.ts +++ b/packages/bot/src/core/discord/addTierChoices.ts @@ -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, diff --git a/packages/bot/src/core/discord/addUsernameChoices.ts b/packages/bot/src/core/discord/addUsernameChoices.ts index 65de3e955..35d851fdd 100644 --- a/packages/bot/src/core/discord/addUsernameChoices.ts +++ b/packages/bot/src/core/discord/addUsernameChoices.ts @@ -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); } diff --git a/packages/bot/src/core/discord/autocompleteClan/index.ts b/packages/bot/src/core/discord/autocompleteClan/index.ts index a36c15713..5ed5d8d3f 100644 --- a/packages/bot/src/core/discord/autocompleteClan/index.ts +++ b/packages/bot/src/core/discord/autocompleteClan/index.ts @@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants'; export async function autocompleteClan( interaction: AutocompleteInteraction, ) { - 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 { diff --git a/packages/bot/src/core/discord/autocompleteTanks.ts b/packages/bot/src/core/discord/autocompleteTanks.ts index 7acafcb94..82d4d2013 100644 --- a/packages/bot/src/core/discord/autocompleteTanks.ts +++ b/packages/bot/src/core/discord/autocompleteTanks.ts @@ -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}`; diff --git a/packages/bot/src/core/discord/autocompleteUsername.ts b/packages/bot/src/core/discord/autocompleteUsername.ts index 7c4c1dfe1..522721c84 100644 --- a/packages/bot/src/core/discord/autocompleteUsername.ts +++ b/packages/bot/src/core/discord/autocompleteUsername.ts @@ -13,7 +13,7 @@ import { export async function autocompleteUsername( interaction: AutocompleteInteraction, ) { - 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 { diff --git a/packages/bot/src/core/discord/buttonRefresh.ts b/packages/bot/src/core/discord/buttonRefresh.ts index caef84b41..fdf21e961 100644 --- a/packages/bot/src/core/discord/buttonRefresh.ts +++ b/packages/bot/src/core/discord/buttonRefresh.ts @@ -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; diff --git a/packages/bot/src/core/discord/createLocalizedCommand.ts b/packages/bot/src/core/discord/createLocalizedCommand.ts index 280b8c1a7..32f0e20b3 100644 --- a/packages/bot/src/core/discord/createLocalizedCommand.ts +++ b/packages/bot/src/core/discord/createLocalizedCommand.ts @@ -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; + } + ).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; + } + ).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; + } + ).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; + } + ).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; + } + ).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; + } + ).groups[subItem.group].description, ) .setDescriptionLocalizations(subcommandDescriptionLocalizations); }); diff --git a/packages/bot/src/core/discord/getPeriodOptionName.ts b/packages/bot/src/core/discord/getPeriodOptionName.ts index f4a58e3ad..d72d8027d 100644 --- a/packages/bot/src/core/discord/getPeriodOptionName.ts +++ b/packages/bot/src/core/discord/getPeriodOptionName.ts @@ -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]); } diff --git a/packages/bot/src/core/discord/localizationObject.ts b/packages/bot/src/core/discord/localizationObject.ts index c0f3298a3..d3e5560f8 100644 --- a/packages/bot/src/core/discord/localizationObject.ts +++ b/packages/bot/src/core/discord/localizationObject.ts @@ -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>>( (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( diff --git a/packages/bot/src/core/discord/normalizeInteractionReturnable.ts b/packages/bot/src/core/discord/normalizeInteractionReturnable.tsx similarity index 100% rename from packages/bot/src/core/discord/normalizeInteractionReturnable.ts rename to packages/bot/src/core/discord/normalizeInteractionReturnable.tsx diff --git a/packages/bot/src/core/discord/resolveClanFromCommand.ts b/packages/bot/src/core/discord/resolveClanFromCommand.ts index b4cfa3a30..6603acacc 100644 --- a/packages/bot/src/core/discord/resolveClanFromCommand.ts +++ b/packages/bot/src/core/discord/resolveClanFromCommand.ts @@ -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), + ]), ); } } diff --git a/packages/bot/src/core/discord/resolvePeriodFromCommand.ts b/packages/bot/src/core/discord/resolvePeriodFromCommand.ts index 61569e087..4b8555d93 100644 --- a/packages/bot/src/core/discord/resolvePeriodFromCommand.ts +++ b/packages/bot/src/core/discord/resolvePeriodFromCommand.ts @@ -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, 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}`, ]); diff --git a/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts b/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts index 88c5d2264..2d6039b40 100644 --- a/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts +++ b/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts @@ -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, ) { - 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); } } diff --git a/packages/bot/src/core/localization/strings/constants.ts b/packages/bot/src/core/localization/strings/constants.ts index 85a3a24aa..e6ba71cc2 100644 --- a/packages/bot/src/core/localization/strings/constants.ts +++ b/packages/bot/src/core/localization/strings/constants.ts @@ -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 = { 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( diff --git a/packages/bot/src/core/localization/strings/index.ts b/packages/bot/src/core/localization/strings/index.ts deleted file mode 100644 index 8ac34208a..000000000 --- a/packages/bot/src/core/localization/strings/index.ts +++ /dev/null @@ -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> ->( - (table, locale) => ({ - ...table, - [locale]: require( - `../../../../../core/lang/${SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]}.json`, - ), - }), - {}, -); diff --git a/packages/bot/src/core/localization/translator.ts b/packages/bot/src/core/localization/translator.ts index 2b09fb8b5..37168ac30 100644 --- a/packages/bot/src/core/localization/translator.ts +++ b/packages/bot/src/core/localization/translator.ts @@ -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 }; } diff --git a/packages/bot/src/events/interactionCreate/handlers/chatInputCommand.ts b/packages/bot/src/events/interactionCreate/handlers/chatInputCommand.ts index e75a0f8d3..f48f91950 100644 --- a/packages/bot/src/events/interactionCreate/handlers/chatInputCommand.ts +++ b/packages/bot/src/events/interactionCreate/handlers/chatInputCommand.ts @@ -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, ) { + 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().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, }); } diff --git a/packages/bot/src/events/interactionCreate/index.ts b/packages/bot/src/events/interactionCreate/index.ts index a7f2b3906..611b41e88 100644 --- a/packages/bot/src/events/interactionCreate/index.ts +++ b/packages/bot/src/events/interactionCreate/index.ts @@ -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) => void; button?: (interaction: ButtonInteraction) => InteractionReturnable; } + interface CommandRegistryImplicit extends CommandRegistryBase { handlesInteraction: true; handler: (interaction: ChatInputCommandInteraction) => void; } + interface CommandRegistryExplicit extends CommandRegistryBase { handlesInteraction?: false; handler: ( interaction: ChatInputCommandInteraction, ) => InteractionReturnable; } + export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit; export const COMMANDS_RAW: Promise[] = [ - 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 }; }, {}, diff --git a/packages/bot/src/main.ts b/packages/bot/src/main.ts index e1538f01e..c494ba6ad 100644 --- a/packages/bot/src/main.ts +++ b/packages/bot/src/main.ts @@ -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 } }, ); diff --git a/packages/bot/src/stitches.config.ts b/packages/bot/src/stitches.config.ts index 07d1f0f0e..f50f1a5fa 100644 --- a/packages/bot/src/stitches.config.ts +++ b/packages/bot/src/stitches.config.ts @@ -23,7 +23,7 @@ export const theme = { spaces: createSpaces(), colors: { - ...createColors(slateDark), + ...createColors(mauveDark), ...createColors(slateDarkA, 'alpha'), ...createColors(slate, 'light'), diff --git a/packages/bot/tsconfig.json b/packages/bot/tsconfig.json index e86b8943b..d24722b0d 100644 --- a/packages/bot/tsconfig.json +++ b/packages/bot/tsconfig.json @@ -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"] } diff --git a/packages/core/lang/en.json b/packages/core/lang/en.json deleted file mode 100644 index 804560ee6..000000000 --- a/packages/core/lang/en.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/es.json b/packages/core/lang/es.json deleted file mode 100644 index c4544bb46..000000000 --- a/packages/core/lang/es.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/hr.json b/packages/core/lang/hr.json deleted file mode 100644 index d4b26dca9..000000000 --- a/packages/core/lang/hr.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/pt.json b/packages/core/lang/pt.json deleted file mode 100644 index d4b26dca9..000000000 --- a/packages/core/lang/pt.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/ru.json b/packages/core/lang/ru.json deleted file mode 100644 index ad71c1a72..000000000 --- a/packages/core/lang/ru.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/uk.json b/packages/core/lang/uk.json deleted file mode 100644 index d4b26dca9..000000000 --- a/packages/core/lang/uk.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/lang/zh.json b/packages/core/lang/zh.json deleted file mode 100644 index 1022e7325..000000000 --- a/packages/core/lang/zh.json +++ /dev/null @@ -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": {} -} diff --git a/packages/core/src/blitzkit/index.ts b/packages/core/src/blitzkit/index.ts index 3f3cf0daa..983def4d7 100644 --- a/packages/core/src/blitzkit/index.ts +++ b/packages/core/src/blitzkit/index.ts @@ -46,5 +46,4 @@ export * from './votes'; export * from './createDefaultProvisions'; export * from './metaSortTank'; export * from './romanize'; -export * from './supportedLocales'; export * from './youtubers'; diff --git a/packages/core/src/blitzkit/supportedLocales.ts b/packages/core/src/blitzkit/supportedLocales.ts deleted file mode 100644 index 864646de8..000000000 --- a/packages/core/src/blitzkit/supportedLocales.ts +++ /dev/null @@ -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]; diff --git a/packages/core/src/blitzkit/tankDefinitions/constants.ts b/packages/core/src/blitzkit/tankDefinitions/constants.ts index cebc801ed..d2d70b602 100644 --- a/packages/core/src/blitzkit/tankDefinitions/constants.ts +++ b/packages/core/src/blitzkit/tankDefinitions/constants.ts @@ -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.AP]: 'AP', @@ -13,21 +7,14 @@ export const SHELL_NAMES: Record = { [ShellType.HEAT]: 'HEAT', [ShellType.HE]: 'HE', }; -export const CREW_MEMBER_NAMES: Record = { - [CrewType.COMMANDER]: 'Commander', - [CrewType.DRIVER]: 'Driver', - [CrewType.GUNNER]: 'Gunner', - [CrewType.LOADER]: 'Loader', - [CrewType.RADIOMAN]: 'Radioman', -}; -export const GUN_TYPE_NAMES: Record< - Exclude['$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 */ diff --git a/packages/core/src/blitzkit/tankNames.ts b/packages/core/src/blitzkit/tankNames.ts index c871ea40c..cee94d9fc 100644 --- a/packages/core/src/blitzkit/tankNames.ts +++ b/packages/core/src/blitzkit/tankNames.ts @@ -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', +]; diff --git a/packages/core/src/math/formatCompact.ts b/packages/core/src/math/formatCompact.ts index 9871953fc..cdc771bd4 100644 --- a/packages/core/src/math/formatCompact.ts +++ b/packages/core/src/math/formatCompact.ts @@ -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); } diff --git a/packages/core/src/protos/average_definitions.ts b/packages/core/src/protos/average_definitions.ts index 806ce7d72..507678f6d 100644 --- a/packages/core/src/protos/average_definitions.ts +++ b/packages/core/src/protos/average_definitions.ts @@ -251,7 +251,7 @@ export const AverageDefinitions_AveragesEntry: MessageFns 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; } \ No newline at end of file diff --git a/packages/core/src/protos/camouflage_definitions.ts b/packages/core/src/protos/camouflage_definitions.ts index 2dc973d98..02d3e846d 100644 --- a/packages/core/src/protos/camouflage_definitions.ts +++ b/packages/core/src/protos/camouflage_definitions.ts @@ -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 = { @@ -198,14 +199,14 @@ export const Camouflage: MessageFns = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 3: { @@ -238,7 +239,7 @@ export const Camouflage: MessageFns = { 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 = { 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 = { 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 = { 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 = { fromPartial, 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; }, }; diff --git a/packages/core/src/protos/consumable_definitions.proto b/packages/core/src/protos/consumable_definitions.proto index 7d877d942..0d65daaf3 100644 --- a/packages/core/src/protos/consumable_definitions.proto +++ b/packages/core/src/protos/consumable_definitions.proto @@ -2,13 +2,15 @@ syntax = "proto2"; package blitzkit; +import "packages/core/src/protos/i18n.proto"; + message ConsumableDefinitions { map 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; diff --git a/packages/core/src/protos/consumable_definitions.ts b/packages/core/src/protos/consumable_definitions.ts index b1b0256ae..27bbbe6d3 100644 --- a/packages/core/src/protos/consumable_definitions.ts +++ b/packages/core/src/protos/consumable_definitions.ts @@ -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 = { @@ -254,8 +263,8 @@ export const Consumable: MessageFns = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 3: { @@ -350,7 +359,7 @@ export const Consumable: MessageFns = { 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 = { 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 = { fromPartial, 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; diff --git a/packages/core/src/protos/equipment_definitions.proto b/packages/core/src/protos/equipment_definitions.proto index c954424d4..536b7c2e0 100644 --- a/packages/core/src/protos/equipment_definitions.proto +++ b/packages/core/src/protos/equipment_definitions.proto @@ -2,14 +2,16 @@ syntax = "proto2"; package blitzkit; +import "packages/core/src/protos/i18n.proto"; + message EquipmentDefinitions { map presets = 1; map equipments = 2; } message Equipment { - required string name = 1; - required string description = 2; + required I18nString name = 1; + required I18nString description = 2; } message EquipmentPreset { diff --git a/packages/core/src/protos/equipment_definitions.ts b/packages/core/src/protos/equipment_definitions.ts index 1bb5d76d4..9742df57c 100644 --- a/packages/core/src/protos/equipment_definitions.ts +++ b/packages/core/src/protos/equipment_definitions.ts @@ -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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 2: { @@ -357,7 +358,7 @@ export const Equipment: MessageFns = { break; } - message.description = reader.string(); + message.description = I18nString.decode(reader, reader.uint32()); continue; } } @@ -371,18 +372,18 @@ export const Equipment: MessageFns = { 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 = { }, fromPartial, 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; }, }; diff --git a/packages/core/src/protos/game_definitions.proto b/packages/core/src/protos/game_definitions.proto index 7d3ffcb48..af8dff450 100644 --- a/packages/core/src/protos/game_definitions.proto +++ b/packages/core/src/protos/game_definitions.proto @@ -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 { diff --git a/packages/core/src/protos/game_definitions.ts b/packages/core/src/protos/game_definitions.ts index 089a1cf43..5cfe6cf12 100644 --- a/packages/core/src/protos/game_definitions.ts +++ b/packages/core/src/protos/game_definitions.ts @@ -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 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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } } @@ -379,13 +380,13 @@ export const GameMode: MessageFns = { }, 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 = { }, fromPartial, 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; }, }; diff --git a/packages/core/src/protos/i18n.proto b/packages/core/src/protos/i18n.proto new file mode 100644 index 000000000..2c31070df --- /dev/null +++ b/packages/core/src/protos/i18n.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +package blitzkit; + +message I18nString { + map locales = 1; +} \ No newline at end of file diff --git a/packages/core/src/protos/i18n.ts b/packages/core/src/protos/i18n.ts new file mode 100644 index 000000000..ceea83dc8 --- /dev/null +++ b/packages/core/src/protos/i18n.ts @@ -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 = { + 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>>(base?: I): I18nString { + return I18nString.fromPartial(base ?? ({} as any)); + }, + fromPartial, 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 = { + 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>>(base?: I): I18nString_LocalesEntry { + return I18nString_LocalesEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, 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 extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +function assertSet(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 { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/packages/core/src/protos/index.ts b/packages/core/src/protos/index.ts index 29b3b3368..a0b2eb4fc 100644 --- a/packages/core/src/protos/index.ts +++ b/packages/core/src/protos/index.ts @@ -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'; diff --git a/packages/core/src/protos/map_definitions.proto b/packages/core/src/protos/map_definitions.proto index 27c11112e..1d67d331a 100644 --- a/packages/core/src/protos/map_definitions.proto +++ b/packages/core/src/protos/map_definitions.proto @@ -2,11 +2,13 @@ syntax = "proto2"; package blitzkit; +import "packages/core/src/protos/i18n.proto"; + message MapDefinitions { map maps = 1; } message MapDefinition { required uint32 id = 1; - required string name = 2; + required I18nString name = 2; } \ No newline at end of file diff --git a/packages/core/src/protos/map_definitions.ts b/packages/core/src/protos/map_definitions.ts index b0228f2d2..08b505919 100644 --- a/packages/core/src/protos/map_definitions.ts +++ b/packages/core/src/protos/map_definitions.ts @@ -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 = { 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 = { @@ -189,8 +190,8 @@ export const MapDefinition: MessageFns = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } } @@ -230,7 +231,7 @@ export const MapDefinition: MessageFns = { 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 = { 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 = { fromPartial, 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; }, }; diff --git a/packages/core/src/protos/model_definitions.ts b/packages/core/src/protos/model_definitions.ts index e864ea44e..3adb2b49a 100644 --- a/packages/core/src/protos/model_definitions.ts +++ b/packages/core/src/protos/model_definitions.ts @@ -265,7 +265,7 @@ export const ModelDefinitions_ModelsEntry: MessageFns 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; diff --git a/packages/core/src/protos/provision_definitions.ts b/packages/core/src/protos/provision_definitions.ts index 9fd678ff7..3fc9a5e41 100644 --- a/packages/core/src/protos/provision_definitions.ts +++ b/packages/core/src/protos/provision_definitions.ts @@ -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 = { @@ -201,8 +202,8 @@ export const Provision: MessageFns = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 3: { @@ -286,7 +287,7 @@ export const Provision: MessageFns = { 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 = { 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 = { fromPartial, 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)) || []; diff --git a/packages/core/src/protos/reviews.ts b/packages/core/src/protos/reviews.ts index c98358f06..4081dafb5 100644 --- a/packages/core/src/protos/reviews.ts +++ b/packages/core/src/protos/reviews.ts @@ -180,7 +180,7 @@ export const Reviews_ReviewsEntry: MessageFns = { message.key = object.key ?? 0; message.value = (object.value !== undefined && object.value !== null) ? Review.fromPartial(object.value) - : createBaseReview(); + : undefined; return message; }, }; diff --git a/packages/core/src/protos/skill_definitions.ts b/packages/core/src/protos/skill_definitions.ts index b371fb625..e59372ba3 100644 --- a/packages/core/src/protos/skill_definitions.ts +++ b/packages/core/src/protos/skill_definitions.ts @@ -175,9 +175,7 @@ export const SkillDefinitions_ClassesEntry: MessageFns tanks = 1; } @@ -9,7 +11,6 @@ message TankDefinitions { message TankDefinition { required uint32 id = 1; map 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; diff --git a/packages/core/src/protos/tank_definitions.ts b/packages/core/src/protos/tank_definitions.ts index 38eb5cac6..01fd44dae 100644 --- a/packages/core/src/protos/tank_definitions.ts +++ b/packages/core/src/protos/tank_definitions.ts @@ -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 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 = { 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 = { 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 = { } 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 12: { @@ -881,7 +869,7 @@ export const TankDefinition: MessageFns = { 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 = { 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 = { 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 = { }); } } - 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 = { 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 = { } 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 = { 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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 4: { @@ -1752,7 +1739,7 @@ export const TrackDefinition: MessageFns = { 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 = { 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 = { 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 = { }; 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 = { @@ -1840,8 +1838,8 @@ export const EngineDefinition: MessageFns = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 3: { @@ -1947,7 +1945,7 @@ export const EngineDefinition: MessageFns = { 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 = { 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 = { fromPartial, 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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 7: { @@ -2158,7 +2158,7 @@ export const TurretDefinition: MessageFns = { 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 = { 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 = { 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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 6: { @@ -3064,7 +3066,7 @@ export const GunDefinitionBase: MessageFns = { 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 = { 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 = { ? 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 = { 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 = { 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 = { break; } - message.name = reader.string(); + message.name = I18nString.decode(reader, reader.uint32()); continue; } case 3: { @@ -3345,7 +3349,7 @@ export const ShellDefinition: MessageFns = { 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 = { 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 = { fromPartial, 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; diff --git a/packages/core/src/vite-env.d.ts b/packages/core/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/packages/core/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 52d43eaaa..2ee5d8b21 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"] + "include": ["src/**/*", "../i18n/src/strings.ts"] } diff --git a/packages/i18n/package.json b/packages/i18n/package.json new file mode 100644 index 000000000..6e5d9d87d --- /dev/null +++ b/packages/i18n/package.json @@ -0,0 +1,10 @@ +{ + "name": "@blitzkit/i18n", + "private": true, + "type": "module", + "main": "src/index.ts", + "dependencies": { + "ts-deepmerge": "^7.0.2" + }, + "devDependencies": {} +} diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts new file mode 100644 index 000000000..1d7e6b8b6 --- /dev/null +++ b/packages/i18n/src/index.ts @@ -0,0 +1,3 @@ +export * from './literals'; +export * from './strings'; +export * from './unwrapper'; diff --git a/packages/i18n/src/literals.ts b/packages/i18n/src/literals.ts new file mode 100644 index 000000000..357e350ac --- /dev/null +++ b/packages/i18n/src/literals.ts @@ -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); +} diff --git a/packages/i18n/src/strings.ts b/packages/i18n/src/strings.ts new file mode 100644 index 000000000..25c022efd --- /dev/null +++ b/packages/i18n/src/strings.ts @@ -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> = {}; + +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 = { + 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 = { + en: '🇺🇸', + es: '🇪🇸', + fr: '🇫🇷', + ja: '🇯🇵', + pt: '🇵🇹', + ru: '🇷🇺', + uk: '🇺🇦', + zh: '🇨🇳', + pl: '🇵🇱', +}; + +export type TranslationTree = { [key: string]: TranslationNode }; + +export type TranslationNode = string | TranslationTree; diff --git a/packages/i18n/src/unwrapper.ts b/packages/i18n/src/unwrapper.ts new file mode 100644 index 000000000..7c18ae5ba --- /dev/null +++ b/packages/i18n/src/unwrapper.ts @@ -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]; + }; +} diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json new file mode 100644 index 000000000..c8484b19c --- /dev/null +++ b/packages/i18n/strings/en.json @@ -0,0 +1,1395 @@ +{ + "test": { + "default": "test", + "long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.", + "fox": "The quick brown fox jumps over the lazy dog." + }, + + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + + "gun_types": { + "auto_loader": "Auto loader", + "auto_reloader": "Auto reloader", + "regular": "Regular" + }, + + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "HEAT", + "he": "HE" + }, + + "player_count_period": { + "total": "Total", + "d_120": "Past 120 days", + "d_90": "Past 90 days", + "d_60": "Past 60 days", + "d_30": "Past month", + "d_7": "Past week", + "d_1": "Yesterday" + }, + + "branches": { + "local": "dev", + "dev": "beta", + "opentest": "opentest", + "preview": "preview" + }, + + "locales": { + "en": "English", + "es": "Spanish", + "pt": "Portuguese", + "ru": "Russian", + "uk": "Ukrainian", + "zh": "Chinese", + "ja": "Japanese", + "fr": "French", + "pl": "Polish" + }, + + "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 \"%s1\". Try selecting a clan from the search result.", + "player_not_found": "# Could not find user\nI couldn't find user \"%s1\". 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 \"%s1\".", + "tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s1\". 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": { + "name": "WN8", + + "types": { + "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": { + "name": "today", + "description": "Today" + }, + "30": { + "name": "30", + "description": "30 days" + }, + "60": { + "name": "60", + "description": "60 days" + }, + "90": { + "name": "90", + "description": "90 days" + }, + "career": { + "name": "career", + "description": "Career" + }, + "custom": { + "name": "custom", + "description": "Custom range", + + "options": { + "start": { + "name": "start", + "description": "Start in number of days ago from today" + }, + "end": { + "name": "end", + "description": "End in number of days ago from today" + } + } + } + }, + + "options": { + "clan": { + "name": "clan", + "description": "The clan name or tag" + }, + + "tank": { + "name": "tank", + "description": "The tank to get stats for" + }, + + "username": { + "name": "username", + "description": "The Blitz username of the player" + }, + + "filters": { + "nation": { + "name": "nation", + "description": "Nation" + }, + "class": { + "name": "class", + "description": "Class" + }, + "type": { + "name": "type", + "description": "Type" + } + }, + + "region": { + "name": "region", + "description": "Region" + }, + + "tier": { + "name": "tier", + "description": "Tier", + + "choices": { + "tier": "Tier %s1 - %s2" + } + } + }, + + "periods": { + "career": "Career", + "today": "Today", + "days": "%s1 days", + "custom": "%s1 - %s2 days" + }, + + "filters": { + "none": "No filters", + "tier": "Tier %s1" + }, + + "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": { + "name": "auction", + "description": "Tanks available right now in the auction", + + "body": { + "title": "Blitz Auction", + "subtitle": "%s1 available • next prices %s2\nBid now: %s3", + "next": "Next price: %s1", + "available": "Available: %s1 / %s2" + }, + "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": { + "name": "replay", + "description": "Display replay results" + }, + + "research": { + "name": "research", + "description": "Lists the expenses in researching a line", + + "options": { + "target_tank": { + "name": "target-tank", + "description": "The final tank to research in the line" + }, + "starting_tank": { + "name": "starting-tank", + "description": "The starting tank in the line (inferred automatically if not provided)" + } + }, + + "body": { + "subtitle": "Research", + "total": "Total", + "research": "Research: %s1", + "upgrades": "Upgrades: %s1", + "purchase": "Purchase: %s1", + "equipment": "Equipment: %s1", + "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": "# %s1 is not on the tech tree\n\nYou cannot research a premium/collector tank.", + "tier_1": "# %s1 is a tier I\n\nTier I tanks are always unlocked for free for all players.", + "already_researched": "# %s1 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": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.", + "tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.", + "unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank." + } + }, + + "about": { + "name": "about", + "description": "All the info you need about the bot", + "subcommands": { + "commands": { + "name": "commands", + "description": "List of all commands and their descriptions", + "body": "# Commands\n\nBlitzKit offers the following commands:\n\n" + }, + "embeds": { + "name": "embeds", + "description": "How embeds work" + }, + "introduction": { + "name": "introduction", + "description": "About the bot" + }, + "invite": { + "name": "invite", + "description": "How to invite the bot" + }, + "timezones": { + "name": "timezones", + "description": "How BlitzKit infers and uses your timezone" + }, + "credits": { + "name": "credits", + "description": "People who have helped BlitzKit" + } + } + }, + + "breakdown": { + "name": "breakdown", + "description": "A period's breakdown by tanks played", + + "body": { + "total": "Total", + "battles": "Battles", + "wn8": "WN8", + "winrate": "Winrate", + "damage": "Damage", + "unknown_tank": "Unknown tank %s1" + } + }, + + "debug": { + "name": "debug", + "description": "Debug information about the bot", + + "body": { + "title": "# Debug\n\n", + "version": "Version", + "shard": { + "label": "Shard", + "default": "default" + }, + "uptime": "Uptime" + } + }, + + "evolution": { + "name": "evolution", + "description": "Evolution of statistics", + + "groups": { + "player": { + "name": "player", + "description": "Player statistics" + }, + + "tank": { + "name": "tank", + "description": "Tank statistics" + } + } + }, + + "inactive": { + "name": "inactive", + "description": "Lists all inactive players in a clan above a threshold", + + "options": { + "threshold": { + "name": "threshold", + "description": "The number of days inactive (default: %s1 days)" + } + }, + + "body": { + "subtitle": "Inactive for %s1+ days", + "listing": "%s1 days" + } + }, + + "full_stats": { + "name": "full-stats", + "description": "Full in-game statistics", + + "body": { + "cards": { + "games": { + "title": "Games", + "winrate": "Winrate", + "battles": "Battles", + "wins_losses": "Wins / losses" + }, + "efficiency": { + "title": "Efficiency", + "wn8": "WN8", + "survival": "Survival", + "accuracy": "Accuracy" + }, + "lethality": { + "title": "Lethality", + "average_damage": "Average damage", + "damage_per_tier": "Damage per tier", + "damage_ratio": "Damage ratio", + "average_kills": "Average kills" + }, + "miscellaneous": { + "title": "Miscellaneous", + "average_tier": "Average tier", + "average_spots": "Average spots", + "average_xp": "Average XP", + "kills_to_death_ratio": "Kills to death ratio" + } + } + } + }, + + "link": { + "name": "link", + "description": "Links your Blitz and Discord account", + + "body": { + "accounts_linked": "Accounts linked", + "no_role_permissions": { + "title": "%s1 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": { + "name": "search-clans", + "description": "Search clans", + + "options": { + "limit": { + "name": "limit", + "description": "The max number of clan results (default: %s1)" + } + }, + + "body": { + "title": "Clan search for %s1 in region %s2", + "no_results": "No clans found" + } + }, + + "search_players": { + "name": "search-players", + "description": "Search players", + + "options": { + "limit": { + "name": "limit", + "description": "The max number of player results (default: %s1)" + } + }, + + "body": { + "title": "Player search for %s1 in region %s2", + "no_results": "No players found" + } + }, + + "rating_leaderboard": { + "name": "rating-leaderboard", + "description": "Rating battles statistics", + + "subcommands": { + "neighbors": { + "name": "neighbors", + "description": "Neighboring players on the leaderboard" + }, + + "league": { + "name": "league", + "description": "Top players of a league" + } + }, + + "options": { + "limit": { + "name": "limit", + "description": "How many neighbors to display (default: %s1)" + }, + + "season": { + "name": "season", + "description": "Season number (default: current)", + + "choices": { + "current": "%s1 (current)" + } + }, + + "league": { + "name": "league", + "description": "The league to display" + } + }, + + "body": { + "top_players": "Top %s1 players", + "no_participation": "# %s1 didn't play rating in season %s2\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 %s1", + "neighbors": "Rating neighbors", + "more": "+ %s1 more" + } + }, + + "today": { + "name": "today", + "description": "Today's played tanks and statistics" + }, + + "stats": { + "name": "stats", + "description": "Quick statistics for a period" + }, + + "owned_tanks": { + "name": "owned-tanks", + "description": "Shows all own tanks in a tier", + + "body": { + "title": "# %s1's owned tier %s2 tanks\n\n" + } + }, + + "permissions": { + "name": "permissions", + "description": "Checks wether the bot has the required permissions to function", + + "body": { + "title": "# Permissions\n\n%s1", + "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": { + "name": "ping", + "description": "Check wether the bot is alive.", + + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + + "player_info": { + "name": "player-info", + "description": "Basic information about a player", + + "body": { + "title": "%s1's information", + "nickname": "Nickname", + "battles": "Battles", + "winrate": "Winrate", + "account_id": "Account ID", + "created": "Created", + "last_battle": "Last battle" + } + }, + + "rating": { + "name": "rating", + "description": "Rating player's statistics", + + "subcommands": { + "today": { + "name": "today", + "description": "Today's rating statistics" + }, + "season": { + "name": "season", + "description": "This season's rating statistics" + } + }, + + "body": { + "subtitle": { + "today": "Today's rating statistics", + "season": "This season's rating statistics" + } + }, + + "errors": { + "no_participation": "# %s1 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": { + "settings": { + "dev_mode": "Development mode", + "language": "Language" + }, + + "common": { + "player_search": { + "hint": "Search players...", + "no_results": "No results", + "searching": "Searching..." + }, + + "copy_button": { + "copied": "Copied!" + }, + + "tank_search": { + "search_bar_hint": "Search tanks...", + "recent": "Recently viewed", + "select_all": "Select all", + "count_singular": "1 tank", + "count_plural": "%s1 tanks", + "sorting_by": "Sorting by %s1", + "no_tanks_found": { + "body": "No tanks found.", + "search_again": "Try searching again.", + "clear_filters": "Try clearing filters." + }, + "sort": { + "meta.none": "none", + "survivability.health": "health", + "survivability.viewRange": "view range", + "survivability.camouflageStill": "camouflage still", + "survivability.camouflageMoving": "camouflage moving", + "survivability.camouflageShooting": "camouflage shooting", + "survivability.volume": "volume", + "survivability.length": "length", + "fire.dpm": "standard DPM", + "fire.dpmPremium": "premium DPM", + "fire.reload": "reload", + "fire.caliber": "caliber", + "fire.standardPenetration": "standard penetration", + "fire.premiumPenetration": "premium penetration", + "fire.damage": "damage", + "fire.moduleDamage": "module damage", + "fire.shellVelocity": "shell velocity", + "fire.shellRange": "shell range", + "fire.shellCapacity": "shell capacity", + "fire.aimTime": "aim time", + "fire.dispersionStill": "dispersion still", + "fire.dispersionMoving": "dispersion moving", + "fire.gunDepression": "gun depression", + "fire.gunElevation": "gun elevation", + "maneuverability.forwardsSpeed": "forwards speed", + "maneuverability.backwardsSpeed": "backwards speed", + "maneuverability.power": "power", + "maneuverability.powerToWeight": "power to weight", + "maneuverability.weight": "weight", + "maneuverability.traverseSpeed": "traverse speed" + }, + "sort_dropdown": { + "label": "Sort", + "groups": { + "sort": { + "no_sort": "No sort", + "label": "Sort by", + "fire": "Fire", + "maneuverability": "Maneuverability", + "survivability": "Survivability" + }, + "direction": { + "label": "Direction", + "ascending": "Ascending", + "descending": "Descending" + } + } + } + }, + + "warnings": { + "test": "Tanks in testing are subject to change and may not represent the final product." + } + }, + + "home": { + "hero": { + "credit": "By %s1 ❤️" + }, + "hot_tanks": { + "title": "Popular tanks", + "subtitle": "Last %s1 days" + }, + "other_projects": { + "title": "Other cool projects" + }, + "help_translate": "Please help translate %s1 to %s2!", + "seo_description": "Tools for everything World of Tanks Blitz" + }, + + "tools": { + "tankopedia": { + "name": "Tankopedia", + "button": "Find tanks", + "description": "Blitz tank encyclopedia", + "seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz", + "tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.", + + "sandbox": { + "poses": { + "hull_down": "Hull down", + "face_hug": "Face hug", + "default": "Default" + }, + + "settings": { + "armor": "Armor", + "green_penetration": "Green penetration", + "hide_model_under_armor": "Hide model under armor", + "opaque": "Opaque", + "dev_wireframe": "DEV: wireframe", + "environment": "Environment", + "show_grid": "Show grid" + }, + + "screenshot": { + "download": "Download", + "copy": "Copy" + }, + + "model": { + "name": "Model" + }, + "dynamic": { + "name": "Dynamic armor", + + "custom_shell": { + "type": "Type", + "caliber": "Caliber", + "penetration": "Penetration", + "normalization": "Normalization", + "ricochet": "Ricochet", + "explosion_radius": "Explosion radius", + "exit": "Exit", + "reset": "Reset" + }, + + "advanced": "Advanced highlighting", + "shooter": "Shooter:", + + "shot_card": { + "element": { + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "gap": "Gap", + "gun": "Gun", + "track": "Tracks" + }, + "status": { + "penetration": "%s1hp penetration", + "blocked": "Blocked", + "ricochet": "Ricochet", + "splash": "%s1hp splash" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetration" + } + } + }, + "static": { + "name": "Static armor", + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "edit": { + "label": "Edit", + "reset": "Reset" + } + } + }, + + "meta": { + "back": "Back", + "compare": "Compare", + "swap": { + "button": "Swap", + "title": "Swap tanks" + }, + "vanilla": "Vanilla", + "opentest": "OpenTest", + "full_name": "Full name", + "nation": "Nation", + "class": "Class", + "tier": "Tier", + "type": "Type", + "dev_id": "DEV: ID", + "purchase": "Purchase price", + "restoration": "Restoration price", + "sale": "Sale price", + "research": "Research XP" + }, + + "tech_tree": { + "title": "Tech tree", + "battle": "1 battle", + "battles": "%s1 battles", + "route": "Route %s1 of %s2" + }, + + "game_modes": { + "title": "Game mode abilities" + }, + + "review": { + "title": "Review videos", + "disclaimer": "BlitzKit does not endorse any review videos.", + "join": "Apply to %s1 YouTuber program" + }, + + "configuration": { + "title": "Configuration", + + "modules": { + "title": "Modules", + "stock": "Stock", + "upgrade": "Upgrade" + }, + "equipment": { + "title": "Equipment", + "clear": "Clear" + }, + "provisions": { + "title": "Provisions", + "clear": "Clear" + }, + "consumables": { + "title": "Consumables", + "clear": "Clear", + "info": "Yellow consumables do not affect the statistics." + }, + "skills": { + "title": "Crew skills", + "clear": "Clear", + "maximize": "Maximize", + "info": "Yellow skills only apply under special circumstances. Hold Space to quickly toggle between level 0 and 7." + }, + "miscellaneous": { + "title": "Miscellaneous" + } + }, + + "characteristics": { + "title": "Characteristics", + "compare": { + "label": "Compare against", + "class": "Tier %s1 %s2", + "tier": "All of tier %s1", + "all": "All tanks" + }, + "values": { + "shell_index": "Shell %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "Distance", + "dispersion_at_distance": "Dispersion at %s1m", + "gun_flexibility": "Gun flexibility", + "speed": "Speed", + "terrain_coefficients": "Terrain coefficients", + "raw_terrain_coefficients": "Raw terrain coefficients", + "power_to_weight_ratio": "Power to weight ratio", + "hull_traverse_speed": "Hull traverse speed", + "camouflage": "Camouflage", + "reload": "Reload", + + "crewCount": "Crew count", + "shellNormalization": "Normalization", + "shellRicochet": "Ricochet", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Shells", + "mostOptimalShellIndex": "Most optimal shell", + "shellReloads": "Shell reloads", + "shellReload": "Shell reload", + "intraClip": "Intra-clip", + "caliber": "Caliber", + "penetration": "Penetration", + "damage": "Damage", + "clipDamage": "Clipping potential", + "moduleDamage": "Module damage", + "explosionRadius": "Explosion radius", + "shellVelocity": "Shell velocity", + "aimTime": "Aim time", + "dispersion": "Still", + "dispersionMoving": "Moving", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Shooting", + "dispersionGunDamaged": "Gun damaged", + "gunDepression": "Depression", + "gunElevation": "Elevation", + "gunFrontalDepression": "Frontal depression", + "gunFrontalElevation": "Frontal elevation", + "gunRearDepression": "Rear depression", + "gunRearElevation": "Rear elevation", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Forwards", + "speedBackwards": "Backwards", + "hardTerrainCoefficient": "Hard", + "hardTerrainCoefficientRaw": "Hard", + "mediumTerrainCoefficient": "Medium", + "mediumTerrainCoefficientRaw": "Medium", + "softTerrainCoefficient": "Soft", + "softTerrainCoefficientRaw": "Soft", + "enginePower": "Engine power", + "powerToWeightRatioHardTerrain": "Hard", + "powerToWeightRatioMediumTerrain": "Medium", + "powerToWeightRatioSoftTerrain": "Soft", + "weight": "Weight", + "turretTraverseSpeed": "Turret traverse speed", + "gunTraverseSpeed": "Gun traverse speed", + "hullTraverseHardTerrain": "Hard", + "hullTraverseMediumTerrain": "Medium", + "hullTraverseSoftTerrain": "Soft", + "health": "Health", + "fireChance": "Fire chance", + "viewRange": "View range", + "camouflageStill": "Still", + "camouflageMoving": "Moving", + "camouflageShootingStill": "Still", + "camouflageShootingMoving": "Moving", + "camouflageCaughtOnFire": "Caught on fire", + "width": "Width", + "height": "Height", + "length": "Length", + "volume": "Volume", + "shellRange": "Shell range", + "shellCapacity": "Shell capacity", + "gunType": "Gun type", + "penetrationAt250m": "Penetration at 250m" + } + }, + + "firepower": { + "title": "Firepower" + }, + + "crew": { + "title": "Crew training", + "info": "represents the other roles a crew member can take.", + "count": "Crew count", + "commander": "Commander", + "gunner": "Gunner", + "driver": "Driver", + "radioman": "Radioman", + "loader": "Loader" + }, + + "maneuverability": { + "title": "Maneuverability" + }, + + "survivability": { + "title": "Survivability" + } + }, + "compare": { + "name": "Compare", + "button": "Compare tanks", + "description": "Compare tank statistics", + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + + "actions": { + "add": { + "button": "Add", + "title": "Add tanks", + "description": "Select tanks to add to comparison" + }, + "clear": "Clear", + "deltas": { + "none": "No deltas", + "percentage": "Percentage", + "absolute": "Absolute" + } + }, + + "no_tanks": { + "title": "No tanks selected", + "body": "Press the Add button to get started" + }, + + "table": { + "equipment": { + "clear": "Clear", + "apply_to_all": "Apply to all" + }, + "skills": { + "clear": "Clear", + "maximize": "Maximize" + }, + "modules": { + "stock": "Stock", + "upgrade": "Upgrade" + }, + "provisions": { + "clear": "Clear" + }, + "consumables": { + "clear": "Clear" + } + } + }, + "performance": { + "name": "Performance", + "button": "View tanks", + "description": "Tank performance statistics", + "seo_description": "Best tanks and their performances in World of Tanks Blitz", + "estimation": { + "body": "Estimated stats based on %s1 players. Updated %s2 ago.", + "hours": "%s1 hours", + "minutes": "%s1 minutes" + }, + "player_count": { + "label": "Player count" + }, + "table": { + "tanks": { + "header": "Tank", + "total": "Total" + }, + "stats": { + "winrate": "Winrate", + "players": "Players", + "damage": "Damage", + "survival": "Survival", + "xp": "XP", + "kills": "Kills", + "spots": "Spots", + "accuracy": "Accuracy", + "shots": "Shots", + "hits": "Hits", + "damage_ratio": "Dmg. ratio", + "damage_taken": "Dmg. taken", + "capture_points": "Cap points" + } + } + }, + "gallery": { + "name": "Gallery", + "button": "Browse all", + "description": "Blitz player avatars", + "seo_description": "All World of Tanks Blitz player avatars", + "last_updated": "Last updated on %s1", + "search": { + "hint": "Search avatars...", + "results": "%s1 avatars" + } + }, + "session": { + "name": "Session", + "button": "Start tracking", + "description": "Live session stats", + "seo_description": "Track your stats in real time as you play World of Tanks Blitz", + "search": { + "lookup": "Look up a player to get started", + "hint": "Search players...", + "searching": "Searching...", + "no_results": "No players found" + }, + "controls": { + "title": "Tracking %s1", + "subtitle": "Since %s1", + "reset": "Reset", + "change": "Change" + }, + "no_cc": { + "title": "You're attempting to track a CC account", + "body": "CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned.", + "alright": "Alright" + }, + "no_battles": { + "title": "No battles", + "hint": "Go ahead and play a game" + }, + "table": { + "tank": "Tank", + "total": "Total", + "remove": "Remove" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Rank tanks", + "description": "Rank Blitz tanks", + "seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz", + "buttons": { + "reset": "Reset", + "share": "Share" + } + }, + "embed": { + "name": "Embed", + "button": "Create now", + "description": "Embeds for streaming", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "More embeds coming soon.", + "suggest": "Suggest ideas.", + + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + + "configuration": { + "import": { + "title": "Import", + "description": "Paste your current URL if you have one already to import your settings", + "hint": "Paste URL...", + "apply": "Apply", + "idiot_title": "Invalid URL", + "idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!", + "idiot_button": "Okay, my bad" + }, + + "export": { + "title": "Export", + "description": "Copy all properties for you streaming software", + "generate": { + "button": "Generate URL", + "title": "Generate URL", + "subtitle": "Search an account to track", + "cancel": "Cancel", + "success": { + "title": "Copy URL", + "info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".", + "back": "Back", + "copy": "Copy URL" + } + }, + "css": "Copy CSS", + "width": "Copy Width", + "height": "Copy Height", + "framerate": "Copy Framerate" + }, + + "customize": { + "title": "Customize", + "reset": "Reset", + "description": "Bells and whistles of your embed" + } + }, + + "types": { + "breakdown": { + "name": "Breakdown", + "options": { + "width": "Width", + "height": "Height", + "showTotal": "Show total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Column gap", + "columnValue": "Column value", + "columnLabel": "Column label", + "column1": "Column 1", + "column2": "Column 2", + "column3": "Column 3", + "column4": "Column 4", + "column1CustomLabel": "Column 1 label", + "column2CustomLabel": "Column 2 label", + "column3CustomLabel": "Column 3 label" + } + } + } + }, + "charts": { + "name": "Charts", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "Install now", + "description": "Stats right in Discord", + "seo_description": "Get stats right in Discord with BlitzKit's Discord bot" + }, + "more": { + "name": "More coming soon", + "button": "Join Discord", + "description": "What we're making", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "Players", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Home", + "button": "Get started", + "description": "Get started with BlitzKit", + "seo_description": "Get started with BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analyze now", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "In-game session stats", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + + "footer": { + "motto": "Everything World of Tanks Blitz", + "copyright": "Unaffiliated with Wargaming or any service", + + "about": { + "title": "About", + "credits": "Credits", + "discord": "Discord", + "github": "GitHub", + "patches": "Patches" + }, + "legal": { + "title": "Legal", + "privacy": "Privacy policy", + "terms": "Terms of service" + } + } + } +} diff --git a/packages/i18n/strings/es.json b/packages/i18n/strings/es.json new file mode 100644 index 000000000..1e25798f5 --- /dev/null +++ b/packages/i18n/strings/es.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "test", + "long": "Esta es una cadena de prueba larga. NO DEBES verla NUNCA en la interfaz de usuario. Mi fruta favorita es %s1 porque tiene el color %s2. ¡Me gusta tanto que diré su nombre 3 veces! ¡ %s1! ¡ %s1! ¡ %s1! Una vez más, es %s2, no %s3.", + "fox": "El rápido zorro marrón salta sobre el perro perezoso." + }, + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1pv", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Auto cargador", + "auto_reloader": "Auto recargador", + "regular": "Regular" + }, + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "AEAC", + "he": "AE" + }, + "player_count_period": { + "total": "Total", + "d_120": "Últimos 120 días", + "d_90": "Últimos 90 días", + "d_60": "Últimos 60 días", + "d_30": "El mes pasado", + "d_7": "La semana pasada", + "d_1": "Ayer" + }, + "branches": { + "local": "DEV", + "dev": "Beta", + "opentest": "Prueba Abierta", + "preview": "Avance" + }, + "locales": { + "en": "Ingles", + "es": "Español", + "pt": "Portugués", + "ru": "Ruso", + "uk": "Ucraniano", + "zh": "Chino", + "ja": "Japones", + "fr": "Francés", + "pl": "Polaco" + }, + "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": "U.R.S.S", + "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 - ASIA" + } + }, + "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 de puntos de captura perdidos", + "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 de tanques detectados", + "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": { + "name": "WN8", + "types": { + "very_bad": "Muy Malo", + "bad": "Malo", + "below_average": "Debajo del Promedio", + "average": "Promedio", + "above_average": "Superior al Promedio", + "good": "Bueno", + "very_good": "Muy Bueno", + "great": "Excelente", + "unicum": "Unicum", + "super_unicum": "Super Unicum" + } + }, + "subcommands": { + "today": { + "name": "Hoy", + "description": "Hoy" + }, + "30": { + "name": "30", + "description": "30 días" + }, + "60": { + "name": "60", + "description": "60 días" + }, + "90": { + "name": "90", + "description": "90 días" + }, + "career": { + "name": "carrera", + "description": "Carrera" + }, + "custom": { + "name": "personalizado", + "description": "Rango personalizado", + "options": { + "start": { + "name": "comenzar", + "description": "Comenzar en cantidad de días pasados desde hoy" + }, + "end": { + "name": "fin", + "description": "Terminar en cantidad de días pasados desde hoy" + } + } + } + }, + "options": { + "clan": { + "name": "clan", + "description": "El nombre o etiqueta del clan" + }, + "tank": { + "name": "tanque", + "description": "El tanque al cual conseguirle estadísticas" + }, + "username": { + "name": "nombre de usuario", + "description": "El nombre de usuario de Blitz" + }, + "filters": { + "nation": { + "name": "nación", + "description": "Nación" + }, + "class": { + "name": "clase", + "description": "Clase" + }, + "type": { + "name": "tipo", + "description": "Tipo" + } + }, + "region": { + "name": "región", + "description": "Región" + }, + "tier": { + "name": "tier", + "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": { + "name": "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": { + "name": "repetición", + "description": "Mostrar resultados de la repetición" + }, + "research": { + "name": "desarollar", + "description": "Enumera los gastos por investigar una línea", + "options": { + "target_tank": { + "name": "tanque objetivo", + "description": "El último tanque a investigar en la línea" + }, + "starting_tank": { + "name": "tanque de inicio", + "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": { + "name": "acerca", + "description": "Toda la información que necesitas sobre el bot", + "subcommands": { + "commands": { + "name": "comandos", + "description": "Lista de todos los comandos y sus descripciones", + "body": "# Comandos\n\nBlitzKit ofrece los siguientes comandos:\n\n" + }, + "embeds": { + "name": "embeds", + "description": "Cómo funcionan los embeds" + }, + "introduction": { + "name": "introducción", + "description": "Sobre el bot" + }, + "invite": { + "name": "invitar", + "description": "Cómo invitar al bot" + }, + "timezones": { + "name": "zonas horarias", + "description": "Cómo Blitzkrieg infiere y utiliza tu zona horaria" + }, + "credits": { + "name": "créditos", + "description": "Personas que han ayudado a BlitzKit" + } + } + }, + "breakdown": { + "name": "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": { + "name": "depurar", + "description": "Muestra más información sobre el bot", + "body": { + "title": "# Depuración\n\n", + "version": "Versión", + "shard": { + "label": "Fragmentar", + "default": "predeterminado" + }, + "uptime": "Tiempo en línea" + } + }, + "evolution": { + "name": "evolución", + "description": "Evolución de estadísticas", + "groups": { + "player": { + "name": "jugador", + "description": "Estadísticas del jugador" + }, + "tank": { + "name": "tanque", + "description": "Estadísticas del tanque" + } + } + }, + "inactive": { + "name": "inactivo", + "description": "Enumera a todos los jugadores inactivos en un clan por encima de un umbral", + "options": { + "threshold": { + "name": "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": { + "name": "estadísticas completas", + "description": "Estadísticas completas del juego", + "body": { + "cards": { + "games": { + "title": "Partidas", + "winrate": "Tasa de victorias", + "battles": "Batallas", + "wins_losses": "Victorias / derrotas" + }, + "efficiency": { + "title": "Eficiencia", + "wn8": "WN8", + "survival": "Supervivencia", + "accuracy": "Precisión" + }, + "lethality": { + "title": "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": { + "title": "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": { + "name": "vincular", + "description": "Vincula tu cuenta de Blitz y Discord", + "body": { + "accounts_linked": "Cuentas vinculadas", + "no_role_permissions": { + "title": "%s1 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": { + "name": "buscar clanes", + "description": "Buscar clanes", + "options": { + "limit": { + "name": "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": { + "name": "buscar jugadores", + "description": "Buscar jugadores", + "options": { + "limit": { + "name": "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": { + "name": "tabla de clasificación", + "description": "Estadísticas de batallas de clasificatoria", + "subcommands": { + "neighbors": { + "name": "vecinos", + "description": "Jugadores vecinos en la tabla de posiciones" + }, + "league": { + "name": "liga", + "description": "Mejores jugadores de una liga" + } + }, + "options": { + "limit": { + "name": "limite", + "description": "Cuántos vecinos a mostrar (predeterminado: %s)" + }, + "season": { + "name": "temporada", + "description": "Número de temporada (predeterminado: actual)", + "choices": { + "current": "%s (actual)" + } + }, + "league": { + "name": "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": { + "name": "hoy", + "description": "Tanques jugados hoy y estadísticas" + }, + "stats": { + "name": "estadísticas", + "description": "Estadísticas rápidas para un período" + }, + "owned_tanks": { + "name": "tanques en propiedad", + "description": "Muestra todos los tanques propios en un nivel", + "body": { + "title": "# Tanques de %s de nivel %s\n\n" + } + }, + "permissions": { + "name": "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": { + "name": "ping", + "description": "Comprueba si el bot está vivo.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Hacerle un ping a BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Hacerle un ping a World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "info del 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": { + "name": "clasificatoria", + "description": "Estadísticas de clasificatoria del jugador", + "subcommands": { + "today": { + "name": "hoy", + "description": "Estadísticas de clasificatoria de hoy" + }, + "season": { + "name": "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": { + "settings": { + "dev_mode": "Modo de desarrollador", + "language": "Idioma" + }, + "common": { + "player_search": { + "hint": "Buscar jugadores...", + "no_results": "Sin resultados", + "searching": "Buscando..." + }, + "copy_button": { + "copied": "Copiado!" + }, + "tank_search": { + "search_bar_hint": "Buscar tanques...", + "recent": "Visto recientemente", + "select_all": "Seleccionar todo", + "count_singular": "1 tanque", + "count_plural": "%s1 tanques", + "sorting_by": "Ordenar por %s1", + "no_tanks_found": { + "body": "No se encontraron tanques.", + "search_again": "Intenta buscar de nuevo.", + "clear_filters": "Intenta limpiar los filtros." + }, + "sort": { + "meta.none": "ninguno", + "survivability.health": "salud", + "survivability.viewRange": "Rango de vision", + "survivability.camouflageStill": "camuflaje estando quieto", + "survivability.camouflageMoving": "camuflaje en movimiento", + "survivability.camouflageShooting": "camuflaje al disparar", + "survivability.volume": "volumen", + "survivability.length": "largo", + "fire.dpm": "DPM estándar", + "fire.dpmPremium": "DPM prémium", + "fire.reload": "recargar", + "fire.caliber": "calibre", + "fire.standardPenetration": "penetración estándar", + "fire.premiumPenetration": "penetración prémium", + "fire.damage": "daño", + "fire.moduleDamage": "daño al módulo", + "fire.shellVelocity": "velocidad del proyectil", + "fire.shellRange": "rango del proyectil", + "fire.shellCapacity": "capacidad de proyectiles", + "fire.aimTime": "tiempo de apuntado", + "fire.dispersionStill": "dispersión estando quieto", + "fire.dispersionMoving": "dispersión en movimiento", + "fire.gunDepression": "depresión del cañón", + "fire.gunElevation": "elevación del cañón", + "maneuverability.forwardsSpeed": "velocidad hacia adelante", + "maneuverability.backwardsSpeed": "velocidad hacia atrás", + "maneuverability.power": "potencia", + "maneuverability.powerToWeight": "relación peso potencia", + "maneuverability.weight": "peso", + "maneuverability.traverseSpeed": "velocidad de desplazamiento" + }, + "sort_dropdown": { + "label": "Ordenar", + "groups": { + "sort": { + "no_sort": "Sin ordenar", + "label": "Ordenar por", + "fire": "Fuego", + "maneuverability": "Maniobrabilidad", + "survivability": "Supervivencia" + }, + "direction": { + "label": "Dirección", + "ascending": "Ascendente", + "descending": "Descendente" + } + } + } + }, + "warnings": { + "test": "Los tanques en pruebas están sujetos a cambios y pueden no representar el producto final." + } + }, + "home": { + "hero": { + "credit": "Por %s1 ❤️" + }, + "hot_tanks": { + "title": "Tanques populares", + "subtitle": "Últimos %s1 días" + }, + "other_projects": { + "title": "Otros proyectos interesantes" + }, + "help_translate": "¡Por favor ayuda a traducir %s1 a %s2!", + "seo_description": "Herramientas para todo lo que es World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Tankopedia", + "button": "Encuentra tanques", + "description": "Enciclopedia de tanques de WOTB", + "seo_description": "Estadísticas, armaduras y más para todos los tanques en World of Tanks Blitz", + "tank_seo_title": "%s1 - Nivel %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 blindaje, estadisticas, equipamiento, proviciones y mas.\n%s2 DPM, %3hp alfa, %s4mm penetracion, %s5kph velocidad maxima, %s6 hp salud", + "sandbox": { + "poses": { + "hull_down": "Hull down", + "face_hug": "Cara a cara", + "default": "Predeterminado" + }, + "settings": { + "armor": "Blindaje", + "green_penetration": "Penetración verde", + "hide_model_under_armor": "Ocultar modelo bajo la armadura", + "opaque": "Opaco", + "dev_wireframe": "DEV: wireframe", + "environment": "Entorno", + "show_grid": "Mostrar cuadrícula" + }, + "screenshot": { + "download": "Descargar", + "copy": "Copiar" + }, + "model": { + "name": "Modelo" + }, + "dynamic": { + "name": "Blindaje dinámico", + "custom_shell": { + "type": "Tipo", + "caliber": "Calibre", + "penetration": "Penetración", + "normalization": "Normalización", + "ricochet": "Rebote", + "explosion_radius": "Radio de Explosión", + "exit": "Salir", + "reset": "Restablecer" + }, + "advanced": "Resaltado avanzado", + "shooter": "Tirador:", + "shot_card": { + "element": { + "primary": "Primario", + "spaced": "Espaciado", + "external": "Externa", + "gap": "Brecha", + "gun": "Cañón", + "track": "Orugas" + }, + "status": { + "penetration": "%s1pv penetracion", + "blocked": "Bloqueado", + "ricochet": "Rebote", + "splash": "%s1pv splash" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetracion" + } + } + }, + "static": { + "name": "Blindaje estatico", + "primary": "Primario", + "spaced": "Espaciado", + "external": "Externa", + "edit": { + "label": "Editar", + "reset": "Restablecer" + } + } + }, + "meta": { + "back": "Volver", + "compare": "Comparar", + "swap": { + "button": "Intercambiar", + "title": "Intercambio de tanques" + }, + "vanilla": "Vainilla", + "opentest": "Prueba abierta", + "full_name": "Nombre completo", + "nation": "Nación", + "class": "Clase", + "tier": "Nivel", + "type": "Tipo", + "dev_id": "DEV: ID", + "purchase": "Precio de compra", + "restoration": "Precio de restauración", + "sale": "Precio de venta", + "research": "Investigar experiencia" + }, + "tech_tree": { + "title": "Árbol tecnológico", + "battle": "1 batalla", + "battles": "%s1 batallas", + "route": "Ruta %s1 de %s2" + }, + "game_modes": { + "title": "Habilidades del modo de juego" + }, + "review": { + "title": "Vídeos de reseña", + "disclaimer": "BlitzKit no respalda ningún vídeo de reseñas.", + "join": "Aplica para el programa de YouTube de %s1" + }, + "configuration": { + "title": "", + "modules": { + "title": "Módulos", + "stock": "Base", + "upgrade": "Mejorar" + }, + "equipment": { + "title": "Equipo", + "clear": "Limpiar" + }, + "provisions": { + "title": "Provisiones", + "clear": "Limpiar" + }, + "consumables": { + "title": "Consumibles", + "clear": "Limpiar", + "info": "Consumibles amarillos no afectan las estadísticas." + }, + "skills": { + "title": "Habilidades de tripulación", + "clear": "Limpiar", + "maximize": "Maximizar", + "info": "Las habilidades en amarillo solo se aplican en circunstancias especiales. Mantenga el espacio para cambiar rápidamente entre los niveles 0 y 7." + }, + "miscellaneous": { + "title": "Misceláneo" + } + }, + "characteristics": { + "title": "Características", + "compare": { + "label": "Comparar con", + "class": "Nivel %s1 - %s2", + "tier": "Todos de nivel %s1", + "all": "Todos los tanques" + }, + "values": { + "shell_index": "Proyectil %s1", + "at_distance": "A %s1m", + "at_distance_slider_label": "Distancia", + "dispersion_at_distance": "Dispersión a %s1m", + "gun_flexibility": "Flexibilidad del arma", + "speed": "Velocidad", + "terrain_coefficients": "Coeficientes de terreno", + "raw_terrain_coefficients": "Coeficientes de terreno en bruto", + "power_to_weight_ratio": "Relación peso-potencia", + "hull_traverse_speed": "Velocidad de rotación del casco", + "camouflage": "Camuflaje", + "reload": "Recarga", + "crewCount": "Cantidad de tripulantes", + "shellNormalization": "Normalización", + "shellRicochet": "Rebote", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Proyectiles", + "mostOptimalShellIndex": "Proyectil más óptimo", + "shellReloads": "Tiempos de recarga", + "shellReload": "Tiempo de recarga", + "intraClip": "Intra-clip", + "caliber": "Calibre", + "penetration": "Penetración", + "damage": "Daño", + "clipDamage": "Clipping potential", + "moduleDamage": "Daño a módulos", + "explosionRadius": "Radio de Explosión", + "shellVelocity": "Velocidad del proyectil", + "aimTime": "Tiempo de apuntado", + "dispersion": "Quieto", + "dispersionMoving": "En movimiento", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Disparando", + "dispersionGunDamaged": "Arma dañada", + "gunDepression": "Depresión", + "gunElevation": "Elevación", + "gunFrontalDepression": "Depresión frontal", + "gunFrontalElevation": "Elevación frontal", + "gunRearDepression": "Depresión trasera", + "gunRearElevation": "Elevación trasera", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Avance", + "speedBackwards": "Marcha atrás", + "hardTerrainCoefficient": "Duro", + "hardTerrainCoefficientRaw": "Duro", + "mediumTerrainCoefficient": "Medio", + "mediumTerrainCoefficientRaw": "Medio", + "softTerrainCoefficient": "Suave", + "softTerrainCoefficientRaw": "Suave", + "enginePower": "Potencia del motor", + "powerToWeightRatioHardTerrain": "Duro", + "powerToWeightRatioMediumTerrain": "Medio", + "powerToWeightRatioSoftTerrain": "Suave", + "weight": "Peso", + "turretTraverseSpeed": "Velocidad de recorrido de torreta", + "gunTraverseSpeed": "Velocidad de recorrido del cañón", + "hullTraverseHardTerrain": "Duro", + "hullTraverseMediumTerrain": "Medio", + "hullTraverseSoftTerrain": "Suave", + "health": "Salud", + "fireChance": "Probabilidad de incendio", + "viewRange": "Rango de visión", + "camouflageStill": "Quieto", + "camouflageMoving": "En movimiento", + "camouflageShootingStill": "Quieto", + "camouflageShootingMoving": "En movimiento", + "camouflageCaughtOnFire": "Atrapado en fuego", + "width": "Ancho", + "height": "Altura", + "length": "Largo", + "volume": "Volumen", + "shellRange": "Rango del proyectil", + "shellCapacity": "Capacidad de proyectiles", + "gunType": "Tipo de cañón", + "penetrationAt250m": "Penetración a 250 m" + } + }, + "firepower": { + "title": "Potencia de fuego" + }, + "crew": { + "title": "Entrenamiento de la tripulación", + "info": "representa los otros roles que un miembro de la tripulación puede asumir.", + "count": "Número de tripulaciones", + "commander": "Comandante", + "gunner": "Artillero", + "driver": "Conductor", + "radioman": "Operador de Radio", + "loader": "Cargador" + }, + "maneuverability": { + "title": "Maniobrabilidad" + }, + "survivability": { + "title": "Supervivencia" + } + }, + "compare": { + "name": "Comparar", + "button": "Comparar tanques", + "description": "Comparar estadísticas del tanque", + "seo_description": "Comparar estadísticas y equipamientos de tanques en World of Tanks Blitz", + "actions": { + "add": { + "button": "Añadir", + "title": "Añadir tanques", + "description": "Selecciona los tanques para añadir a la comparación" + }, + "clear": "Limpiar", + "deltas": { + "none": "Sin deltas", + "percentage": "Porcentaje", + "absolute": "Absoluto" + } + }, + "no_tanks": { + "title": "No hay tanques seleccionados", + "body": "Pulse el botón Añadir para empezar" + }, + "table": { + "equipment": { + "clear": "Limpiar", + "apply_to_all": "Aplicar a todo" + }, + "skills": { + "clear": "Limpiar", + "maximize": "Maximizar" + }, + "modules": { + "stock": "Base", + "upgrade": "Mejorar" + }, + "provisions": { + "clear": "Limpiar" + }, + "consumables": { + "clear": "Limpiar" + } + } + }, + "performance": { + "name": "Rendimiento", + "button": "Ver tanques", + "description": "Estadísticas de rendimiento del tanque", + "seo_description": "Los mejores tanques y su rendimiento en World of Tanks Blitz", + "estimation": { + "body": "Estadísticas estimadas basadas en %s1 jugadores. Actualizado hace %s2.", + "hours": "%s1 horas", + "minutes": "%s1 minutos" + }, + "player_count": { + "label": "Contador de jugadores" + }, + "table": { + "tanks": { + "header": "Tanque", + "total": "Total" + }, + "stats": { + "winrate": "Tasa de victorias", + "players": "Jugadores", + "damage": "Daño", + "survival": "Supervivencia", + "xp": "EXP", + "kills": "Bajas", + "spots": "Detectados", + "accuracy": "Precisión", + "shots": "Disparos", + "hits": "Aciertos", + "damage_ratio": "Relación de daño", + "damage_taken": "Daño recibido", + "capture_points": "Puntos de captura" + } + } + }, + "gallery": { + "name": "Galería", + "button": "Explorar todo", + "description": "Avatares para los jugadores de WOTB", + "seo_description": "Todos los avatares del jugador en World of Tanks Blitz", + "last_updated": "Última actualización el %s1", + "search": { + "hint": "Buscar avatares...", + "results": "%s1 avatares" + } + }, + "session": { + "name": "Sesión", + "button": "Iniciar seguimiento", + "description": "Estadísticas de sesión en vivo", + "seo_description": "Sigue tus estadísticas en tiempo real mientras juegas a World of Tanks Blitz", + "search": { + "lookup": "Busca a un jugador para empezar", + "hint": "Busca jugadores...", + "searching": "Buscando...", + "no_results": "No se encontraron jugadores" + }, + "controls": { + "title": "Siguiendo a %s1", + "subtitle": "Desde %s1", + "reset": "Restablecer", + "change": "Change" + }, + "no_cc": { + "title": "You're attempting to track a CC account", + "body": "CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned.", + "alright": "Alright" + }, + "no_battles": { + "title": "No battles", + "hint": "Sigue adelante y juega una partida" + }, + "table": { + "tank": "Tanque", + "total": "Total", + "remove": "Remover" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Rank tanks", + "description": "Rank Blitz tanks", + "seo_description": "Explora las listas de clasificación de los tanques de mejor a peor, basadas en las calificaciones de los jugadores y el rendimiento estadístico en World of Tanks Blitz", + "buttons": { + "reset": "Restablecer", + "share": "Compartir" + } + }, + "embed": { + "name": "Embed", + "button": "Create now", + "description": "Embeds for streaming", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "More embeds coming soon.", + "suggest": "Sugerir ideas.", + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + "configuration": { + "import": { + "title": "Importar", + "description": "Pega tu URL actual si ya tienes una para importar la configuración", + "hint": "Pegar URL...", + "apply": "Aplicar", + "idiot_title": "URL inválida", + "idiot_description": "Intenta pegar la URL que BlitzKit generó anteriormente, ¡Torpe!", + "idiot_button": "Okay, my bad" + }, + "export": { + "title": "Exportar", + "description": "Copiar todas las propiedades para tu software de streaming", + "generate": { + "button": "Generar URL", + "title": "Generar URL", + "subtitle": "Buscar una cuenta para seguir", + "cancel": "Cancelar", + "success": { + "title": "Copiar URL", + "info": "Se generó la URL para %s1. El embebido comenzará a rastrear tan pronto como se pegue. Restablece en cualquier momento haciendo clic derecho en PC o manteniendo presionado en móvil y luego seleccionando \"Restablecer\".", + "back": "Volver", + "copy": "Copiar URL" + } + }, + "css": "Copiar CSS", + "width": "Copiar ancho", + "height": "Copiar altura", + "framerate": "Copy Framerate" + }, + "customize": { + "title": "Personalizar", + "reset": "Restablecer", + "description": "Bells and whistles of your embed" + } + }, + "types": { + "breakdown": { + "name": "Desglose", + "options": { + "width": "Ancho", + "height": "Altura", + "showTotal": "Mostrar total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Espacio entre columnas", + "columnValue": "Valor de columna", + "columnLabel": "Etiqueta de columna", + "column1": "Columna 1", + "column2": "Columna 2", + "column3": "Columna 3", + "column4": "Columna 4", + "column1CustomLabel": "Etiqueta de la columna 1", + "column2CustomLabel": "Etiqueta de la columna 2", + "column3CustomLabel": "Etiqueta de la columna 3" + } + } + } + }, + "charts": { + "name": "Gráficos", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Gráficos y tablas sobre todo lo relacionado con tanques, jugadores y eventos en World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "Install now", + "description": "Estadísticas en Discord", + "seo_description": "Obtén estadísticas en Discord con el bot de Discord de BlitzKit" + }, + "more": { + "name": "Más próximamente", + "button": "Join Discord", + "description": "What we're making", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "Jugadores", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Home", + "button": "Get started", + "description": "Get started with BlitzKit", + "seo_description": "Get started with BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analizar ahora", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "In-game session stats", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + "footer": { + "motto": "Todo sobre World of Tanks Blitz", + "copyright": "No afiliado con Wargaming u otros servicios", + "about": { + "title": "About", + "credits": "Credits", + "discord": "Discord", + "github": "GitHub", + "patches": "Parches" + }, + "legal": { + "title": "Legal", + "privacy": "Política de Privacidad", + "terms": "Términos del servicio" + } + } + } +} diff --git a/packages/i18n/strings/fr.json b/packages/i18n/strings/fr.json new file mode 100644 index 000000000..20b43e35e --- /dev/null +++ b/packages/i18n/strings/fr.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "teste", + "long": "Ceci est une longue chaîne de test. Vous ne DEVEZ JAMAIS le voir dans l'interface utilisateur. Mon fruit préféré est %s1, car il a la couleur %s2. Je l'aime tellement que je vais dire son nom 3 fois ! %s1! %s1! %s1 ! Encore une fois, c'est %s2, pas %s3.", + "fox": "Le renard brun et rapide saute par-dessus le chien paresseux." + }, + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1ps", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Chargement auto", + "auto_reloader": "Rechargement auto", + "regular": "Standard" + }, + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "HEAT", + "he": "HE" + }, + "player_count_period": { + "total": "Total", + "d_120": "120 derniers jours", + "d_90": "90 derniers jours", + "d_60": "60 derniers jours", + "d_30": "Mois dernier", + "d_7": "Semaine dernière", + "d_1": "Hier" + }, + "branches": { + "local": "dev", + "dev": "beta", + "opentest": "test ouvert", + "preview": "aperçu" + }, + "locales": { + "en": "Anglais", + "es": "Espagnol", + "pt": "Portugais", + "ru": "Russe", + "uk": "Ukrainien", + "zh": "Chinois", + "ja": "Japonais", + "fr": "Français", + "pl": "Polonais" + }, + "wn8_percentile": { + "0": "Très nul", + "1": "Mauvais", + "2": "En-dessous de la moyenne", + "3": "Moyen", + "4": "Au-dessus de la moyenne", + "5": "Bon", + "6": "Très bon", + "7": "Super", + "8": "Unique", + "9": "Super unique" + }, + "bkni_percentile": { + "0": "Super mal", + "1": "Mal", + "2": "Très nul", + "3": "Nul", + "4": "En dessous de la moyenne", + "5": "Moyenne", + "6": "Au-dessus de la moyenne", + "7": "Bien", + "8": "Très bien", + "9": "Unique", + "10": "Super unique" + }, + "nations": { + "ussr": "U.R.S.S", + "germany": "Allemagne", + "usa": "\"U.S.A\".", + "china": "Chine", + "uk": "Royaume-Uni.", + "france": "France", + "japan": "Japon", + "european": "Nation européenne", + "other": "Nation hybride" + }, + "nations_adjectives": { + "ussr": "U.R.S.S", + "germany": "Allemand", + "usa": "\"U.S.A\".", + "china": "Chinois", + "uk": "Royaume-Uni.", + "france": "Français", + "japan": "Japonais", + "european": "Européen", + "other": "Hybride" + }, + "tank_class_short": { + "-1": "Non reconnu", + "0": "Léger", + "1": "Moyen", + "2": "Lourd", + "3": "Chasseur de char" + }, + "tank_class_short_plural_lowercase": { + "0": "légers", + "1": "moyens", + "2": "lourds", + "3": "Chasseurs de char" + }, + "tank_class_medium": { + "-1": "Non reconnu", + "0": "Léger", + "1": "Moyen", + "2": "Lourd", + "3": "Chasseur de char" + }, + "tree_type": { + "-1": "Non reconnu", + "0": "Arbre technologique", + "1": "Premium", + "2": "Char de collection" + }, + "regions": { + "normal": { + "com": "Amérique du Nord", + "eu": "Europe", + "asia": "Asie-Pacifique" + }, + "short": { + "com": "NA", + "eu": "Europe", + "asia": "APAC" + } + }, + "leagues": { + "diamond": "Diamant", + "platinum": "Platine", + "gold": "Or", + "silver": "Argent", + "bronze": "Bronze" + }, + "composite_stats": { + "normalized_damage_dealt": "Dégâts", + "normalized_damage_received": "Dégâts reçus", + "normalized_deaths": "Taux de Mort", + "normalized_frags": "Tués", + "normalized_hits": "Coups", + "normalized_losses": "Taux de perte", + "normalized_misses": "Ratés", + "normalized_shots": "Tirs", + "normalized_spotted": "Détections", + "normalized_survived_battles": "Taux de survie", + "normalized_win_and_survived": "Taux de victoires et de survie", + "normalized_wins": "Taux de victoires", + "normalized_xp": "XP", + "normalized_battle_life_time": "Temps de bataille", + "cumulative_accuracy": "Précision", + "cumulative_battles": "Batailles", + "cumulative_capture_points": "Points de capture totaux", + "cumulative_damage_dealt": "Total de dégâts", + "cumulative_damage_ratio": "Rapport de dégâts", + "cumulative_damage_received": "Total de dégât reçus", + "cumulative_deaths": "Total de morts", + "cumulative_dropped_capture_points": "Total de points de capture abandonnés", + "cumulative_frags": "Total de tués", + "cumulative_hits": "Total de coups", + "cumulative_inaccuracy": "Imprécision", + "cumulative_kills_to_death_ratio": "Rapport tués/morts", + "cumulative_losses": "Défaites", + "cumulative_misses": "Total de ratés", + "cumulative_shots": "Total de tirs", + "cumulative_spotted": "Total de détections", + "cumulative_survived_battles": "Batailles survécues", + "cumulative_win_and_survived": "Batailles gagnées et survécues", + "cumulative_wins": "Victoires", + "cumulative_xp": "Total XP", + "cumulative_wn8": "WN8", + "cumulative_battle_life_time": "Total de temps de bataille" + } + }, + "bot": { + "common": { + "errors": { + "clan_not_found": "# Impossible de trouver le clan\nImpossible de trouver «%s1 ». Essayez de sélectionner un clan dans le résultat de la recherche.", + "player_not_found": "# Impossible de trouver l'utilisateur\nJe n'ai pas pu trouver l'utilisateur \"%s1\". Essayez de choisir un utilisateur dans le résultat de la recherche, en tapant un nom d'utilisateur valide, ou en utilisant la commande `/link`.", + "player_not_linked": "# Utilisez `/link` pour commencer\nJe ne sais pas qui vous êtes, mais vous pouvez me le dire aujourd'hui !", + "tank_name_not_found": "Impossible de trouver le char sous le nom \"%s1\".", + "tank_id_not_found": "# Réservoir introuvable\nImpossible de trouver le char avec l'ID \"%s1\". Essayez de taper le nom du char au lieu d'un nombre.", + "no_tank_stats": "# Aucune statistique de tank disponible\nCe joueur n'a pas de statistiques disponibles. Ce n'est peut-être pas le joueur que vous cherchez.", + "get_help": "Aidez le serveur Discord", + "uncaught_error": "# Blitzkit a rencontré une erreur !\nNous sommes désolés de cela. N'hésitez pas à [rejoindre le serveur officiel Discord](https://discord.gg/nDt7AjGJQH) pour obtenir de l'aide.", + "preview": "# Cette commande est en aperçu\n\nVous ne pouvez accéder à cette commande que dans [le serveur officiel Discord](https://discord.gg/nDt7AjGJQH). La commande sera généralement disponible sous peu.", + "query_too_small": "# Votre recherche est trop courte\nVotre recherche doit comporter au moins 3 caractères.", + "query_too_long": "# Votre recherche est trop longue\nVotre recherche doit comporter au maximum 100 caractères." + }, + "wn8": { + "name": "WN8", + "types": { + "very_bad": "Très nul", + "bad": "Nul", + "below_average": "En-dessous de la moyenne", + "average": "Moyenne", + "above_average": "Au-dessus de la moyenne", + "good": "Bien", + "very_good": "Très bien", + "great": "Super", + "unicum": "Unique", + "super_unicum": "Super unique" + } + }, + "subcommands": { + "today": { + "name": "aujourd'hui", + "description": "Aujourd'hui" + }, + "30": { + "name": "30", + "description": "30 jours" + }, + "60": { + "name": "60", + "description": "60 jours" + }, + "90": { + "name": "90", + "description": "90 jours" + }, + "career": { + "name": "carrière", + "description": "Carrière" + }, + "custom": { + "name": "personnaliser", + "description": "Période spécifique", + "options": { + "start": { + "name": "début", + "description": "Commencer il y a un nombre de jours à partir d'aujourd'hui" + }, + "end": { + "name": "fin", + "description": "Fin en nombre de jours à partir d'aujourd'hui" + } + } + } + }, + "options": { + "clan": { + "name": "clan", + "description": "Le nom ou le tag du clan" + }, + "tank": { + "name": "char", + "description": "Le char pour obtenir des statistiques pour" + }, + "username": { + "name": "nom d'utilisateur", + "description": "Le nom d'utilisateur Blitz du joueur" + }, + "filters": { + "nation": { + "name": "pays", + "description": "Pays" + }, + "class": { + "name": "classe", + "description": "Classe" + }, + "type": { + "name": "type", + "description": "Type" + } + }, + "region": { + "name": "région", + "description": "Région" + }, + "tier": { + "name": "rang", + "description": "Rang", + "choices": { + "tier": "Rang %s1 - %s2" + } + } + }, + "periods": { + "career": "Carrière", + "today": "Aujourd'hui", + "days": "%s1 jour", + "custom": "%s1 - %s2 jours" + }, + "filters": { + "none": "Aucun filtre", + "tier": "Rang %s1" + }, + "no_data": { + "battles_in_period": "", + "battles_in_season": "Aucune bataille jouée dans cette saison", + "players_in_period": "Aucun joueur pendant cette période", + "tanks_found": "Aucun char de trouver" + }, + "buttons": { + "refresh": "Actualiser" + }, + "hero_stats": { + "battles": "Batailles", + "winrate": "Taux de victoires", + "damage": "Dégâts", + "damage_ratio": "Rapport de dégâts", + "tier": "Rang", + "survival": "Survie", + "accuracy": "Précision", + "kills": "Tués" + } + }, + "commands": { + "auction": { + "name": "Char cible", + "description": "Des chars sont disponibles dès maintenant dans la vente aux enchères", + "body": { + "title": "Enchères Blitz", + "subtitle": "%s1 disponible • prochains prix %s2\nOffre : %s3", + "next": "Prochain prix : %s1", + "available": "Disponible : %s1 / %s2" + }, + "errors": { + "no_auction": "# Pas de vente aux enchères\n\nActuellement, il n'y a pas de chars disponibles à l'achat dans la vente aux enchères. Si vous pensez qu'il s'agit d'une erreur, veuillez signaler cette anomalie dans [le serveur officiel Discord](https://discord.gg/nDt7AjGJQH)." + }, + "actions": { + "bid": "Enchérir maintenant" + } + }, + "replay": { + "name": "rejouer", + "description": "Afficher les résultats de la relecture" + }, + "research": { + "name": "recherche", + "description": "Liste des dépenses liées à la recherche d'une ligne", + "options": { + "target_tank": { + "name": "char cible", + "description": "Le dernier char à rechercher dans la ligne" + }, + "starting_tank": { + "name": "Char de départ", + "description": "" + } + }, + "body": { + "subtitle": "Research", + "total": "Total", + "research": "Recherche: %s1", + "upgrades": "Améliorations : %s1", + "purchase": "Achat : %s1", + "equipment": "Equipment: %s1", + "estimation": "# Ceci est une estimation !\n\nCette liste peut inclure des chars déjà recherchés en raison de l'information inexacte fournie par Wargaming. Essayez de fournir un `char de départ` depuis la ligne lorsque vous utilisez la commande si vous le pouvez." + }, + "errors": { + "non_tech_tree": "# %s1 n'est pas dans l'arbre technologique\n\nVous ne pouvez pas rechercher un char prenium/de collection.", + "tier_1": "# %s1 est un char de tier I\n\nLe char de tier 1 sont déverrouillé gratuitement pour tous les joueurs.", + "already_researched": "# %s1 est déjà recherché\n\nEssayez de fournir un `char de départ` en utilisant la commande pour voir la progression d'une lignée.", + "start_end_equal": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.", + "tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.", + "unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank." + } + }, + "about": { + "name": "à propos", + "description": "Toutes les informations dont vous avez besoin sur le bot", + "subcommands": { + "commands": { + "name": "commandes", + "description": "Liste de toutes les commandes et leurs descriptions", + "body": "# Commandes\n\nBlitzKit offre les commandes suivantes :\n\n" + }, + "embeds": { + "name": "embeds", + "description": "How embeds work" + }, + "introduction": { + "name": "introduction", + "description": "À propos du bot" + }, + "invite": { + "name": "inviter", + "description": "Comment inviter le bot" + }, + "timezones": { + "name": "fuseau horaire", + "description": "Comment BlitzKit déduit et utilise votre fuseau horaire" + }, + "credits": { + "name": "crédits", + "description": "Personnes qui ont aidé BlitzKit" + } + } + }, + "breakdown": { + "name": "décomposition", + "description": "A period's breakdown by tanks played", + "body": { + "total": "Total", + "battles": "Batailles", + "wn8": "WN8", + "winrate": "Taux de victoires", + "damage": "Dégâts", + "unknown_tank": "Char %s1 inconnu" + } + }, + "debug": { + "name": "déboguer", + "description": "Déboguer les informations sur le bot", + "body": { + "title": "# Debog\n\n", + "version": "Version", + "shard": { + "label": "Fragment", + "default": "par défaut" + }, + "uptime": "Temps de fonctionnement" + } + }, + "evolution": { + "name": "évolution", + "description": "Évolution des statistiques", + "groups": { + "player": { + "name": "joueur", + "description": "Statistiques du joueur" + }, + "tank": { + "name": "char", + "description": "Statistiques du char" + } + } + }, + "inactive": { + "name": "inactif", + "description": "Liste tous les joueurs inactifs dans un clan au-dessus d'un seuil", + "options": { + "threshold": { + "name": "seuil", + "description": "Le nombre de jours inactifs (par défaut: %s1 jours)" + } + }, + "body": { + "subtitle": "Inactif depuis %s1+ de jours", + "listing": "%s1 jour" + } + }, + "full_stats": { + "name": "statistiques complètes", + "description": "Statistiques complètes en jeu", + "body": { + "cards": { + "games": { + "title": "Games", + "winrate": "Taux de victoires", + "battles": "Batailles", + "wins_losses": "Victoires/Défaites" + }, + "efficiency": { + "title": "Efficacité", + "wn8": "WN8", + "survival": "Survie", + "accuracy": "Précision" + }, + "lethality": { + "title": "Lethality", + "average_damage": "Dégâts moyens", + "damage_per_tier": "Dégâts par tier", + "damage_ratio": "Rapport de dégâts", + "average_kills": "Destruction moyenne" + }, + "miscellaneous": { + "title": "Divers", + "average_tier": "Tier moyen", + "average_spots": "Repérage moyen", + "average_xp": "Exp. Moyenne par bataille", + "kills_to_death_ratio": "Rapport tués/morts" + } + } + } + }, + "link": { + "name": "lien", + "description": "Lier votre compte Blitz et Discord", + "body": { + "accounts_linked": "Comptes liés", + "no_role_permissions": { + "title": "La vérification de %s1 a échoué", + "description": "Je n'ai pas l'autorisation de gérer les rôles." + } + }, + "embed": { + "title": "Compte invalide ?", + "description": "Use the command again and select a username from the search results." + } + }, + "search_clans": { + "name": "search-clans", + "description": "Search clans", + "options": { + "limit": { + "name": "limit", + "description": "The max number of clan results (default: %s1)" + } + }, + "body": { + "title": "Clan search for %s1 in region %s2", + "no_results": "No clans found" + } + }, + "search_players": { + "name": "search-players", + "description": "Search players", + "options": { + "limit": { + "name": "limit", + "description": "The max number of player results (default: %s1)" + } + }, + "body": { + "title": "Player search for %s1 in region %s2", + "no_results": "No players found" + } + }, + "rating_leaderboard": { + "name": "rating-leaderboard", + "description": "Rating battles statistics", + "subcommands": { + "neighbors": { + "name": "neighbors", + "description": "Neighboring players on the leaderboard" + }, + "league": { + "name": "league", + "description": "Top players of a league" + } + }, + "options": { + "limit": { + "name": "limit", + "description": "How many neighbors to display (default: %s1)" + }, + "season": { + "name": "season", + "description": "Season number (default: current)", + "choices": { + "current": "%s1 (current)" + } + }, + "league": { + "name": "league", + "description": "The league to display" + } + }, + "body": { + "top_players": "Top %s1 players", + "no_participation": "# %s1 didn't play rating in season %s2\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 %s1", + "neighbors": "Rating neighbors", + "more": "+ %s1 more" + } + }, + "today": { + "name": "aujourd'hui", + "description": "Today's played tanks and statistics" + }, + "stats": { + "name": "stats", + "description": "Quick statistics for a period" + }, + "owned_tanks": { + "name": "owned-tanks", + "description": "Shows all own tanks in a tier", + "body": { + "title": "# %s1's owned tier %s2 tanks\n\n" + } + }, + "permissions": { + "name": "permissions", + "description": "Checks wether the bot has the required permissions to function", + "body": { + "title": "# Permissions\n\n%s1", + "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": { + "name": "ping", + "description": "Check wether the bot is alive.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "player-info", + "description": "Basic information about a player", + "body": { + "title": "%s1's information", + "nickname": "Nickname", + "battles": "Batailles", + "winrate": "Taux de victoires", + "account_id": "Account ID", + "created": "Created", + "last_battle": "Last battle" + } + }, + "rating": { + "name": "rating", + "description": "Rating player's statistics", + "subcommands": { + "today": { + "name": "aujourd'hui", + "description": "Today's rating statistics" + }, + "season": { + "name": "season", + "description": "This season's rating statistics" + } + }, + "body": { + "subtitle": { + "today": "Today's rating statistics", + "season": "This season's rating statistics" + } + }, + "errors": { + "no_participation": "# %s1 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": { + "settings": { + "dev_mode": "Development mode", + "language": "Language" + }, + "common": { + "player_search": { + "hint": "Search players...", + "no_results": "No results", + "searching": "Searching..." + }, + "copy_button": { + "copied": "Copied!" + }, + "tank_search": { + "search_bar_hint": "Search tanks...", + "recent": "Recently viewed", + "select_all": "Select all", + "count_singular": "1 tank", + "count_plural": "%s1 tanks", + "sorting_by": "Sorting by %s1", + "no_tanks_found": { + "body": "No tanks found.", + "search_again": "Try searching again.", + "clear_filters": "Try clearing filters." + }, + "sort": { + "meta.none": "none", + "survivability.health": "health", + "survivability.viewRange": "view range", + "survivability.camouflageStill": "camouflage still", + "survivability.camouflageMoving": "camouflage moving", + "survivability.camouflageShooting": "camouflage shooting", + "survivability.volume": "volume", + "survivability.length": "length", + "fire.dpm": "standard DPM", + "fire.dpmPremium": "premium DPM", + "fire.reload": "reload", + "fire.caliber": "caliber", + "fire.standardPenetration": "standard penetration", + "fire.premiumPenetration": "premium penetration", + "fire.damage": "damage", + "fire.moduleDamage": "module damage", + "fire.shellVelocity": "shell velocity", + "fire.shellRange": "shell range", + "fire.shellCapacity": "shell capacity", + "fire.aimTime": "aim time", + "fire.dispersionStill": "dispersion still", + "fire.dispersionMoving": "dispersion moving", + "fire.gunDepression": "gun depression", + "fire.gunElevation": "gun elevation", + "maneuverability.forwardsSpeed": "forwards speed", + "maneuverability.backwardsSpeed": "backwards speed", + "maneuverability.power": "power", + "maneuverability.powerToWeight": "power to weight", + "maneuverability.weight": "weight", + "maneuverability.traverseSpeed": "traverse speed" + }, + "sort_dropdown": { + "label": "Sort", + "groups": { + "sort": { + "no_sort": "No sort", + "label": "Sort by", + "fire": "Fire", + "maneuverability": "Maneuverability", + "survivability": "Survivability" + }, + "direction": { + "label": "Direction", + "ascending": "Ascending", + "descending": "Descending" + } + } + } + }, + "warnings": { + "test": "Tanks in testing are subject to change and may not represent the final product." + } + }, + "home": { + "hero": { + "credit": "By %s1 ❤️" + }, + "hot_tanks": { + "title": "Popular tanks", + "subtitle": "Last %s1 days" + }, + "other_projects": { + "title": "Other cool projects" + }, + "help_translate": "Please help translate %s1 to %s2!", + "seo_description": "Tools for everything World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Tankopedia", + "button": "Find tanks", + "description": "Blitz tank encyclopedia", + "seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz", + "tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.", + "sandbox": { + "poses": { + "hull_down": "Hull down", + "face_hug": "Face hug", + "default": "Default" + }, + "settings": { + "armor": "Armor", + "green_penetration": "Green penetration", + "hide_model_under_armor": "Hide model under armor", + "opaque": "Opaque", + "dev_wireframe": "DEV: wireframe", + "environment": "Environment", + "show_grid": "Show grid" + }, + "screenshot": { + "download": "Download", + "copy": "Copy" + }, + "model": { + "name": "Model" + }, + "dynamic": { + "name": "Dynamic armor", + "custom_shell": { + "type": "Type", + "caliber": "Caliber", + "penetration": "Penetration", + "normalization": "Normalization", + "ricochet": "Ricochet", + "explosion_radius": "Explosion radius", + "exit": "Exit", + "reset": "Reset" + }, + "advanced": "Advanced highlighting", + "shooter": "Shooter:", + "shot_card": { + "element": { + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "gap": "Gap", + "gun": "Gun", + "track": "Tracks" + }, + "status": { + "penetration": "%s1hp penetration", + "blocked": "Blocked", + "ricochet": "Ricochet", + "splash": "%s1hp splash" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetration" + } + } + }, + "static": { + "name": "Static armor", + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "edit": { + "label": "Edit", + "reset": "Reset" + } + } + }, + "meta": { + "back": "Back", + "compare": "Compare", + "swap": { + "button": "Swap", + "title": "Swap tanks" + }, + "vanilla": "Vanilla", + "opentest": "OpenTest", + "full_name": "Full name", + "nation": "Pays", + "class": "Classe", + "tier": "Rang", + "type": "Type", + "dev_id": "DEV: ID", + "purchase": "Purchase price", + "restoration": "Restoration price", + "sale": "Sale price", + "research": "Research XP" + }, + "tech_tree": { + "title": "Arbre technologique", + "battle": "1 battle", + "battles": "%s1 battles", + "route": "Route %s1 of %s2" + }, + "game_modes": { + "title": "Game mode abilities" + }, + "review": { + "title": "Review videos", + "disclaimer": "BlitzKit does not endorse any review videos.", + "join": "Apply to %s1 YouTuber program" + }, + "configuration": { + "title": "Configuration", + "modules": { + "title": "Modules", + "stock": "Stock", + "upgrade": "Upgrade" + }, + "equipment": { + "title": "Equipment", + "clear": "Clear" + }, + "provisions": { + "title": "Provisions", + "clear": "Clear" + }, + "consumables": { + "title": "Consumables", + "clear": "Clear", + "info": "Yellow consumables do not affect the statistics." + }, + "skills": { + "title": "Crew skills", + "clear": "Clear", + "maximize": "Maximize", + "info": "Yellow skills only apply under special circumstances. Hold Space to quickly toggle between level 0 and 7." + }, + "miscellaneous": { + "title": "Divers" + } + }, + "characteristics": { + "title": "Characteristics", + "compare": { + "label": "Compare against", + "class": "Tier %s1 %s2", + "tier": "All of tier %s1", + "all": "All tanks" + }, + "values": { + "shell_index": "Shell %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "Distance", + "dispersion_at_distance": "Dispersion at %s1m", + "gun_flexibility": "Gun flexibility", + "speed": "Speed", + "terrain_coefficients": "Terrain coefficients", + "raw_terrain_coefficients": "Raw terrain coefficients", + "power_to_weight_ratio": "Power to weight ratio", + "hull_traverse_speed": "Hull traverse speed", + "camouflage": "Camouflage", + "reload": "Reload", + "crewCount": "Crew count", + "shellNormalization": "Normalization", + "shellRicochet": "Ricochet", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Shells", + "mostOptimalShellIndex": "Most optimal shell", + "shellReloads": "Shell reloads", + "shellReload": "Shell reload", + "intraClip": "Intra-clip", + "caliber": "Caliber", + "penetration": "Penetration", + "damage": "Dégâts", + "clipDamage": "Clipping potential", + "moduleDamage": "Module damage", + "explosionRadius": "Explosion radius", + "shellVelocity": "Shell velocity", + "aimTime": "Aim time", + "dispersion": "Still", + "dispersionMoving": "Moving", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Shooting", + "dispersionGunDamaged": "Gun damaged", + "gunDepression": "Depression", + "gunElevation": "Elevation", + "gunFrontalDepression": "Frontal depression", + "gunFrontalElevation": "Frontal elevation", + "gunRearDepression": "Rear depression", + "gunRearElevation": "Rear elevation", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Forwards", + "speedBackwards": "Backwards", + "hardTerrainCoefficient": "Hard", + "hardTerrainCoefficientRaw": "Hard", + "mediumTerrainCoefficient": "Moyen", + "mediumTerrainCoefficientRaw": "Moyen", + "softTerrainCoefficient": "Soft", + "softTerrainCoefficientRaw": "Soft", + "enginePower": "Engine power", + "powerToWeightRatioHardTerrain": "Hard", + "powerToWeightRatioMediumTerrain": "Moyen", + "powerToWeightRatioSoftTerrain": "Soft", + "weight": "Weight", + "turretTraverseSpeed": "Turret traverse speed", + "gunTraverseSpeed": "Gun traverse speed", + "hullTraverseHardTerrain": "Hard", + "hullTraverseMediumTerrain": "Moyen", + "hullTraverseSoftTerrain": "Soft", + "health": "Health", + "fireChance": "Fire chance", + "viewRange": "View range", + "camouflageStill": "Still", + "camouflageMoving": "Moving", + "camouflageShootingStill": "Still", + "camouflageShootingMoving": "Moving", + "camouflageCaughtOnFire": "Caught on fire", + "width": "Width", + "height": "Height", + "length": "Length", + "volume": "Volume", + "shellRange": "Shell range", + "shellCapacity": "Shell capacity", + "gunType": "Gun type", + "penetrationAt250m": "Penetration at 250m" + } + }, + "firepower": { + "title": "Firepower" + }, + "crew": { + "title": "Crew training", + "info": "represents the other roles a crew member can take.", + "count": "Crew count", + "commander": "Commander", + "gunner": "Gunner", + "driver": "Driver", + "radioman": "Radioman", + "loader": "Loader" + }, + "maneuverability": { + "title": "Maneuverability" + }, + "survivability": { + "title": "Survivability" + } + }, + "compare": { + "name": "Compare", + "button": "Compare tanks", + "description": "Compare tank statistics", + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + "actions": { + "add": { + "button": "Add", + "title": "Add tanks", + "description": "Select tanks to add to comparison" + }, + "clear": "Clear", + "deltas": { + "none": "No deltas", + "percentage": "Percentage", + "absolute": "Absolute" + } + }, + "no_tanks": { + "title": "No tanks selected", + "body": "Press the Add button to get started" + }, + "table": { + "equipment": { + "clear": "Clear", + "apply_to_all": "Apply to all" + }, + "skills": { + "clear": "Clear", + "maximize": "Maximize" + }, + "modules": { + "stock": "Stock", + "upgrade": "Upgrade" + }, + "provisions": { + "clear": "Clear" + }, + "consumables": { + "clear": "Clear" + } + } + }, + "performance": { + "name": "Performance", + "button": "View tanks", + "description": "Tank performance statistics", + "seo_description": "Best tanks and their performances in World of Tanks Blitz", + "estimation": { + "body": "Estimated stats based on %s1 players. Updated %s2 ago.", + "hours": "%s1 hours", + "minutes": "%s1 minutes" + }, + "player_count": { + "label": "Player count" + }, + "table": { + "tanks": { + "header": "Tank", + "total": "Total" + }, + "stats": { + "winrate": "Taux de victoires", + "players": "Players", + "damage": "Dégâts", + "survival": "Survie", + "xp": "XP", + "kills": "Tués", + "spots": "Détections", + "accuracy": "Précision", + "shots": "Tirs", + "hits": "Coups", + "damage_ratio": "Dmg. ratio", + "damage_taken": "Dmg. taken", + "capture_points": "Cap points" + } + } + }, + "gallery": { + "name": "Gallery", + "button": "Browse all", + "description": "Blitz player avatars", + "seo_description": "All World of Tanks Blitz player avatars", + "last_updated": "Last updated on %s1", + "search": { + "hint": "Search avatars...", + "results": "%s1 avatars" + } + }, + "session": { + "name": "Session", + "button": "Start tracking", + "description": "Live session stats", + "seo_description": "Track your stats in real time as you play World of Tanks Blitz", + "search": { + "lookup": "Look up a player to get started", + "hint": "Search players...", + "searching": "Searching...", + "no_results": "No players found" + }, + "controls": { + "title": "Tracking %s1", + "subtitle": "Since %s1", + "reset": "Reset", + "change": "Change" + }, + "no_cc": { + "title": "You're attempting to track a CC account", + "body": "CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned.", + "alright": "Alright" + }, + "no_battles": { + "title": "No battles", + "hint": "Go ahead and play a game" + }, + "table": { + "tank": "Tank", + "total": "Total", + "remove": "Remove" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Rank tanks", + "description": "Rank Blitz tanks", + "seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz", + "buttons": { + "reset": "Reset", + "share": "Share" + } + }, + "embed": { + "name": "Embed", + "button": "Create now", + "description": "Embeds for streaming", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "More embeds coming soon.", + "suggest": "Suggest ideas.", + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + "configuration": { + "import": { + "title": "Import", + "description": "Paste your current URL if you have one already to import your settings", + "hint": "Paste URL...", + "apply": "Apply", + "idiot_title": "Invalid URL", + "idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!", + "idiot_button": "Okay, my bad" + }, + "export": { + "title": "Export", + "description": "Copy all properties for you streaming software", + "generate": { + "button": "Generate URL", + "title": "Generate URL", + "subtitle": "Search an account to track", + "cancel": "Cancel", + "success": { + "title": "Copy URL", + "info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".", + "back": "Back", + "copy": "Copy URL" + } + }, + "css": "Copy CSS", + "width": "Copy Width", + "height": "Copy Height", + "framerate": "Copy Framerate" + }, + "customize": { + "title": "Customize", + "reset": "Reset", + "description": "Bells and whistles of your embed" + } + }, + "types": { + "breakdown": { + "name": "Breakdown", + "options": { + "width": "Width", + "height": "Height", + "showTotal": "Show total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Column gap", + "columnValue": "Column value", + "columnLabel": "Column label", + "column1": "Column 1", + "column2": "Column 2", + "column3": "Column 3", + "column4": "Column 4", + "column1CustomLabel": "Column 1 label", + "column2CustomLabel": "Column 2 label", + "column3CustomLabel": "Column 3 label" + } + } + } + }, + "charts": { + "name": "Charts", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "Install now", + "description": "Stats right in Discord", + "seo_description": "Get stats right in Discord with BlitzKit's Discord bot" + }, + "more": { + "name": "More coming soon", + "button": "Join Discord", + "description": "What we're making", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "Players", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Home", + "button": "Get started", + "description": "Get started with BlitzKit", + "seo_description": "Get started with BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analyze now", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "In-game session stats", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + "footer": { + "motto": "Everything World of Tanks Blitz", + "copyright": "Unaffiliated with Wargaming or any service", + "about": { + "title": "About", + "credits": "Credits", + "discord": "Discord", + "github": "GitHub", + "patches": "Patches" + }, + "legal": { + "title": "Legal", + "privacy": "Privacy policy", + "terms": "Terms of service" + } + } + } +} diff --git a/packages/i18n/strings/ja.json b/packages/i18n/strings/ja.json new file mode 100644 index 000000000..f5bb1c4e9 --- /dev/null +++ b/packages/i18n/strings/ja.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "test", + "long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.", + "fox": "The quick brown fox jumps over the lazy dog." + }, + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "自動装填砲", + "auto_reloader": "自動再装填砲", + "regular": "単発砲" + }, + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "HEAT", + "he": "HE" + }, + "player_count_period": { + "total": "トータル", + "d_120": "過去 120 日間", + "d_90": "過去 90 日間", + "d_60": "過去 60 日間", + "d_30": "過去 1 ヶ月", + "d_7": "過去 1 週間", + "d_1": "昨日" + }, + "branches": { + "local": "dev", + "dev": "beta", + "opentest": "opentest", + "preview": "preview" + }, + "locales": { + "en": "英語", + "es": "スペイン語", + "pt": "ポルトガル語", + "ru": "ロシア語", + "uk": "ウクライナ語", + "zh": "中国語", + "ja": "日本語", + "fr": "フランス語", + "pl": "ポーランド語" + }, + "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": "ソ連", + "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": "LT", + "1": "MT", + "2": "HT", + "3": "TD" + }, + "tank_class_medium": { + "-1": "不明", + "0": "軽戦車", + "1": "中戦車", + "2": "重戦車", + "3": "駆逐戦車" + }, + "tree_type": { + "-1": "不明", + "0": "技術ツリー", + "1": "プレミアム", + "2": "コレクター" + }, + "regions": { + "normal": { + "com": "North America", + "eu": "Europe", + "asia": "Asia-Pacific" + }, + "short": { + "com": "NA", + "eu": "EU", + "asia": "APAC" + } + }, + "leagues": { + "diamond": "ダイヤモンド", + "platinum": "プラチナ", + "gold": "ゴールド", + "silver": "シルバー", + "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 \"%s1\". Try selecting a clan from the search result.", + "player_not_found": "# Could not find user\nI couldn't find user \"%s1\". 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 \"%s1\".", + "tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s1\". 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": "Discordサーバーに参加する", + "uncaught_error": "# BlitzKit がエラーに遭遇しました!\n申し訳ありません。お気軽に[Discordの公式サーバー](https://discord.gg/nDt7AjGJQH) をご利用ください。", + "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": { + "name": "WN8", + "types": { + "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": { + "name": "today", + "description": "Today" + }, + "30": { + "name": "30", + "description": "30 days" + }, + "60": { + "name": "60", + "description": "60 days" + }, + "90": { + "name": "90", + "description": "90日" + }, + "career": { + "name": "総合戦績", + "description": "Career" + }, + "custom": { + "name": "custom", + "description": "Custom range", + "options": { + "start": { + "name": "start", + "description": "Start in number of days ago from today" + }, + "end": { + "name": "end", + "description": "End in number of days ago from today" + } + } + } + }, + "options": { + "clan": { + "name": "clan", + "description": "The clan name or tag" + }, + "tank": { + "name": "tank", + "description": "The tank to get stats for" + }, + "username": { + "name": "username", + "description": "The Blitz username of the player" + }, + "filters": { + "nation": { + "name": "nation", + "description": "Nation" + }, + "class": { + "name": "", + "description": "Class" + }, + "type": { + "name": "type", + "description": "Type" + } + }, + "region": { + "name": "region", + "description": "Region" + }, + "tier": { + "name": "tier", + "description": "Tier", + "choices": { + "tier": "Tier %s1 - %s2" + } + } + }, + "periods": { + "career": "Career", + "today": "Today", + "days": "%s1 days", + "custom": "%s1 - %s2 days" + }, + "filters": { + "none": "No filters", + "tier": "Tier %s1" + }, + "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": { + "name": "auction", + "description": "Tanks available right now in the auction", + "body": { + "title": "Blitz Auction", + "subtitle": "%s1 available • next prices %s2\nBid now: %s3", + "next": "Next price: %s1", + "available": "Available: %s1 / %s2" + }, + "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": { + "name": "replay", + "description": "Display replay results" + }, + "research": { + "name": "research", + "description": "Lists the expenses in researching a line", + "options": { + "target_tank": { + "name": "target-tank", + "description": "The final tank to research in the line" + }, + "starting_tank": { + "name": "starting-tank", + "description": "The starting tank in the line (inferred automatically if not provided)" + } + }, + "body": { + "subtitle": "Research", + "total": "トータル", + "research": "Research: %s1", + "upgrades": "Upgrades: %s1", + "purchase": "Purchase: %s1", + "equipment": "Equipment: %s1", + "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": "# %s1 is not on the tech tree\n\nYou cannot research a premium/collector tank.", + "tier_1": "# %s1 is a tier I\n\nTier I tanks are always unlocked for free for all players.", + "already_researched": "# %s1 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": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.", + "tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.", + "unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank." + } + }, + "about": { + "name": "about", + "description": "All the info you need about the bot", + "subcommands": { + "commands": { + "name": "commands", + "description": "List of all commands and their descriptions", + "body": "# Commands\n\nBlitzKit offers the following commands:\n\n" + }, + "embeds": { + "name": "embeds", + "description": "How embeds work" + }, + "introduction": { + "name": "introduction", + "description": "About the bot" + }, + "invite": { + "name": "invite", + "description": "How to invite the bot" + }, + "timezones": { + "name": "timezones", + "description": "How BlitzKit infers and uses your timezone" + }, + "credits": { + "name": "credits", + "description": "People who have helped BlitzKit" + } + } + }, + "breakdown": { + "name": "breakdown", + "description": "A period's breakdown by tanks played", + "body": { + "total": "トータル", + "battles": "Battles", + "wn8": "WN8", + "winrate": "Winrate", + "damage": "Damage", + "unknown_tank": "Unknown tank %s1" + } + }, + "debug": { + "name": "debug", + "description": "Debug information about the bot", + "body": { + "title": "# Debug\n\n", + "version": "Version", + "shard": { + "label": "Shard", + "default": "default" + }, + "uptime": "Uptime" + } + }, + "evolution": { + "name": "evolution", + "description": "Evolution of statistics", + "groups": { + "player": { + "name": "player", + "description": "Player statistics" + }, + "tank": { + "name": "tank", + "description": "Tank statistics" + } + } + }, + "inactive": { + "name": "inactive", + "description": "Lists all inactive players in a clan above a threshold", + "options": { + "threshold": { + "name": "threshold", + "description": "The number of days inactive (default: %s1 days)" + } + }, + "body": { + "subtitle": "Inactive for %s1+ days", + "listing": "%s1 days" + } + }, + "full_stats": { + "name": "full-stats", + "description": "Full in-game statistics", + "body": { + "cards": { + "games": { + "title": "Games", + "winrate": "Winrate", + "battles": "Battles", + "wins_losses": "Wins / losses" + }, + "efficiency": { + "title": "Efficiency", + "wn8": "WN8", + "survival": "Survival", + "accuracy": "Accuracy" + }, + "lethality": { + "title": "Lethality", + "average_damage": "Average damage", + "damage_per_tier": "Damage per tier", + "damage_ratio": "Damage ratio", + "average_kills": "Average kills" + }, + "miscellaneous": { + "title": "Miscellaneous", + "average_tier": "Average tier", + "average_spots": "Average spots", + "average_xp": "Average XP", + "kills_to_death_ratio": "Kills to death ratio" + } + } + } + }, + "link": { + "name": "link", + "description": "Links your Blitz and Discord account", + "body": { + "accounts_linked": "Accounts linked", + "no_role_permissions": { + "title": "%s1 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": { + "name": "search-clans", + "description": "Search clans", + "options": { + "limit": { + "name": "limit", + "description": "The max number of clan results (default: %s1)" + } + }, + "body": { + "title": "Clan search for %s1 in region %s2", + "no_results": "No clans found" + } + }, + "search_players": { + "name": "search-players", + "description": "Search players", + "options": { + "limit": { + "name": "limit", + "description": "The max number of player results (default: %s1)" + } + }, + "body": { + "title": "Player search for %s1 in region %s2", + "no_results": "No players found" + } + }, + "rating_leaderboard": { + "name": "rating-leaderboard", + "description": "Rating battles statistics", + "subcommands": { + "neighbors": { + "name": "neighbors", + "description": "Neighboring players on the leaderboard" + }, + "league": { + "name": "league", + "description": "Top players of a league" + } + }, + "options": { + "limit": { + "name": "limit", + "description": "How many neighbors to display (default: %s1)" + }, + "season": { + "name": "season", + "description": "Season number (default: current)", + "choices": { + "current": "%s1 (current)" + } + }, + "league": { + "name": "league", + "description": "The league to display" + } + }, + "body": { + "top_players": "Top %s1 players", + "no_participation": "# %s1 didn't play rating in season %s2\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 %s1", + "neighbors": "Rating neighbors", + "more": "+ %s1 more" + } + }, + "today": { + "name": "today", + "description": "Today's played tanks and statistics" + }, + "stats": { + "name": "stats", + "description": "Quick statistics for a period" + }, + "owned_tanks": { + "name": "owned-tanks", + "description": "Shows all own tanks in a tier", + "body": { + "title": "# %s1's owned tier %s2 tanks\n\n" + } + }, + "permissions": { + "name": "permissions", + "description": "Checks wether the bot has the required permissions to function", + "body": { + "title": "# Permissions\n\n%s1", + "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": { + "name": "ping", + "description": "Check wether the bot is alive.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "player-info", + "description": "Basic information about a player", + "body": { + "title": "%s1's information", + "nickname": "Nickname", + "battles": "Battles", + "winrate": "Winrate", + "account_id": "Account ID", + "created": "Created", + "last_battle": "Last battle" + } + }, + "rating": { + "name": "rating", + "description": "Rating player's statistics", + "subcommands": { + "today": { + "name": "today", + "description": "Today's rating statistics" + }, + "season": { + "name": "season", + "description": "This season's rating statistics" + } + }, + "body": { + "subtitle": { + "today": "Today's rating statistics", + "season": "This season's rating statistics" + } + }, + "errors": { + "no_participation": "# %s1 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": { + "settings": { + "dev_mode": "開発者モード", + "language": "言語 / Language" + }, + "common": { + "player_search": { + "hint": "プレイヤーを検索中...", + "no_results": "該当なし", + "searching": "検索中..." + }, + "copy_button": { + "copied": "コピーしました!" + }, + "tank_search": { + "search_bar_hint": "車輌名を入力…", + "recent": "最近の閲覧", + "select_all": "全て選択", + "count_singular": "1 輌", + "count_plural": "%s1 輌", + "sorting_by": "並べ替え:%s1", + "no_tanks_found": { + "body": "該当する車輌が存在しません。", + "search_again": "もう一度探してください。", + "clear_filters": "フィルターをクリアしてください" + }, + "sort": { + "meta.none": "なし", + "survivability.health": "耐久値", + "survivability.viewRange": "視認範囲", + "survivability.camouflageStill": "静止時隠蔽率", + "survivability.camouflageMoving": "移動時隠蔽率", + "survivability.camouflageShooting": "射撃時隠蔽率", + "survivability.volume": "体積", + "survivability.length": "全長", + "fire.dpm": "通常弾DPM", + "fire.dpmPremium": "課金弾DPM", + "fire.reload": "装填時間", + "fire.caliber": "砲口径", + "fire.standardPenetration": "通常弾貫通力", + "fire.premiumPenetration": "課金弾貫通力", + "fire.damage": "ダメージ", + "fire.moduleDamage": "モジュールダメージ", + "fire.shellVelocity": "弾速", + "fire.shellRange": "射程範囲", + "fire.shellCapacity": "総弾数", + "fire.aimTime": "照準時間", + "fire.dispersionStill": "静止時散布界", + "fire.dispersionMoving": "移動時散布界", + "fire.gunDepression": "主砲俯角", + "fire.gunElevation": "主砲仰角", + "maneuverability.forwardsSpeed": "前進速度", + "maneuverability.backwardsSpeed": "後退速度", + "maneuverability.power": "馬力", + "maneuverability.powerToWeight": "出力重量比", + "maneuverability.weight": "重量", + "maneuverability.traverseSpeed": "車体旋回速度" + }, + "sort_dropdown": { + "label": "ソート", + "groups": { + "sort": { + "no_sort": "ソートなし", + "label": "並べ替え項目", + "fire": "火力", + "maneuverability": "機動性", + "survivability": "生存性" + }, + "direction": { + "label": "方法", + "ascending": "昇順", + "descending": "降順" + } + } + } + }, + "warnings": { + "test": "テスト中の車輌は変更が加えられる可能性があり、最終的な性能と異なる場合があります。" + } + }, + "home": { + "hero": { + "credit": "By %s1 ❤️" + }, + "hot_tanks": { + "title": "人気の車輌", + "subtitle": "過去 %s1 日間" + }, + "other_projects": { + "title": "Other cool projects" + }, + "help_translate": "%s1の %s2への翻訳にご協力ください!", + "seo_description": "Tools for everything World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Tankopedia", + "button": "戦車を探す", + "description": "Blitz戦車百科", + "seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz", + "tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.", + "sandbox": { + "poses": { + "hull_down": "ハルダウン", + "face_hug": "密着", + "default": "通常" + }, + "settings": { + "armor": "装甲", + "green_penetration": "貫通可能域を緑色表示", + "hide_model_under_armor": "車輌モデルを非表示", + "opaque": "貫通アシストを不透明にする", + "dev_wireframe": "DEV: wireframe", + "environment": "環境", + "show_grid": "グリッドを表示" + }, + "screenshot": { + "download": "保存する", + "copy": "コピー" + }, + "model": { + "name": "モデル" + }, + "dynamic": { + "name": "貫通テスト", + "custom_shell": { + "type": "弾種", + "caliber": "口径", + "penetration": "貫通力", + "normalization": "標準化", + "ricochet": "跳弾角度", + "explosion_radius": "爆風半径", + "exit": "決定", + "reset": "リセット" + }, + "advanced": "高度なハイライト", + "shooter": "自車両", + "shot_card": { + "element": { + "primary": "主装甲", + "spaced": "空間装甲", + "external": "外部モジュール", + "gap": "空間", + "gun": "主砲", + "track": "履帯" + }, + "status": { + "penetration": "%s1hp 貫通", + "blocked": "非貫通", + "ricochet": "跳弾", + "splash": "%s1hp 爆風ダメージ" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm 装甲", + "ricochet_loss": "%s1% penetration" + } + } + }, + "static": { + "name": "装甲モデル", + "primary": "主装甲", + "spaced": "空間装甲", + "external": "外部モジュール", + "edit": { + "label": "編集", + "reset": "リセット" + } + } + }, + "meta": { + "back": "戻る", + "compare": "性能比較", + "swap": { + "button": "入れ替え", + "title": "車輌を入れ替える" + }, + "vanilla": "Vanilla", + "opentest": "OpenTest", + "full_name": "正式名称", + "nation": "国籍", + "class": "車種", + "tier": "Tier", + "type": "タイプ", + "dev_id": "DEV: ID", + "purchase": "購入価格", + "restoration": "再購入価格", + "sale": "売却価格", + "research": "研究費用" + }, + "tech_tree": { + "title": "技術ツリー", + "battle": "1 戦", + "battles": "%s1 戦", + "route": "ルート %s1 / %s2" + }, + "game_modes": { + "title": "ゲームモードアビリティ" + }, + "review": { + "title": "Review videos", + "disclaimer": "BlitzKitはいかなるレビュー動画も後援していません。", + "join": "%s1 YouTuberプログラムに応募する" + }, + "configuration": { + "title": "構成", + "modules": { + "title": "モジュール", + "stock": "初期装備", + "upgrade": "最終装備" + }, + "equipment": { + "title": "拡張パーツ", + "clear": "クリア" + }, + "provisions": { + "title": "常備品", + "clear": "クリア" + }, + "consumables": { + "title": "消耗品", + "clear": "クリア", + "info": "黄色の消耗品は統計には影響しません。" + }, + "skills": { + "title": "搭乗員スキル", + "clear": "クリア", + "maximize": "最大", + "info": "黄色のスキルは特殊な状況下でのみ適用されます。スペースキーを長押しすると、レベル0とレベル7を素早く切り替えられます。" + }, + "miscellaneous": { + "title": "その他" + } + }, + "characteristics": { + "title": "性能値", + "compare": { + "label": "比較対象", + "class": "Tier %s1 %s2", + "tier": "Tier %s1 全車輌", + "all": "全車輌" + }, + "values": { + "shell_index": "Shell %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "距離", + "dispersion_at_distance": "%s1m での散布界", + "gun_flexibility": "主砲旋回上限", + "speed": "最高速度", + "terrain_coefficients": "地形走破能力", + "raw_terrain_coefficients": "地形係数", + "power_to_weight_ratio": "出力重量比", + "hull_traverse_speed": "車体旋回速度", + "camouflage": "隠蔽率", + "reload": "装填時間", + "crewCount": "搭乗員数", + "shellNormalization": "標準化", + "shellRicochet": "跳弾角度", + "dpm": "DPM", + "dpmEffective": "60秒での有効値", + "shells": "砲弾", + "mostOptimalShellIndex": "最適な弾数", + "shellReloads": "自動装填時間", + "shellReload": "装填時間", + "intraClip": "砲弾再装填時間", + "caliber": "口径", + "penetration": "貫通力", + "damage": "ダメージ", + "clipDamage": "弾倉火力", + "moduleDamage": "モジュールダメージ", + "explosionRadius": "爆風半径", + "shellVelocity": "弾速", + "aimTime": "照準時間", + "dispersion": "静止時", + "dispersionMoving": "移動時", + "dispersionHullTraversing": "車体旋回時", + "dispersionTurretTraversing": "砲塔旋回時", + "dispersionShooting": "射撃時", + "dispersionGunDamaged": "主砲損傷時", + "gunDepression": "俯角", + "gunElevation": "仰角", + "gunFrontalDepression": "正面俯角", + "gunFrontalElevation": "正面仰角", + "gunRearDepression": "背面俯角", + "gunRearElevation": "背面仰角", + "azimuthLeft": "左射界", + "azimuthRight": "右射界", + "speedForwards": "前進", + "speedBackwards": "後退", + "hardTerrainCoefficient": "道", + "hardTerrainCoefficientRaw": "道", + "mediumTerrainCoefficient": "地面", + "mediumTerrainCoefficientRaw": "地面", + "softTerrainCoefficient": "水辺", + "softTerrainCoefficientRaw": "水辺", + "enginePower": "エンジン出力", + "powerToWeightRatioHardTerrain": "道", + "powerToWeightRatioMediumTerrain": "地面", + "powerToWeightRatioSoftTerrain": "水辺", + "weight": "重量", + "turretTraverseSpeed": "砲塔旋回速度", + "gunTraverseSpeed": "主砲旋回速度", + "hullTraverseHardTerrain": "道", + "hullTraverseMediumTerrain": "地面", + "hullTraverseSoftTerrain": "水辺", + "health": "耐久値", + "fireChance": "火災発生率", + "viewRange": "視認範囲", + "camouflageStill": "静止時", + "camouflageMoving": "移動時", + "camouflageShootingStill": "静止時", + "camouflageShootingMoving": "移動時", + "camouflageCaughtOnFire": "Caught on fire", + "width": "全幅", + "height": "全高", + "length": "車体長", + "volume": "体積", + "shellRange": "射程範囲", + "shellCapacity": "総弾数", + "gunType": "主砲タイプ", + "penetrationAt250m": "250m での貫通力" + } + }, + "firepower": { + "title": "火力" + }, + "crew": { + "title": "搭乗員熟練度", + "info": "搭乗員が担うことができるその他の役割を表します", + "count": "搭乗員数", + "commander": "車長", + "gunner": "砲手", + "driver": "操縦手", + "radioman": "通信手", + "loader": "装填手" + }, + "maneuverability": { + "title": "機動性" + }, + "survivability": { + "title": "生存性" + } + }, + "compare": { + "name": "比較", + "button": "戦車を比較", + "description": "車輌の特性を比較", + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + "actions": { + "add": { + "button": "追加", + "title": "車輌を追加", + "description": "比較に追加する車輌を選択" + }, + "clear": "クリア", + "deltas": { + "none": "差分なし", + "percentage": "割合", + "absolute": "絶対値" + } + }, + "no_tanks": { + "title": "車輌が選択されていません", + "body": "「追加」ボタンを押して開始" + }, + "table": { + "equipment": { + "clear": "クリア", + "apply_to_all": "すべて選択" + }, + "skills": { + "clear": "クリア", + "maximize": "最大" + }, + "modules": { + "stock": "初期装備", + "upgrade": "最終装備" + }, + "provisions": { + "clear": "クリア" + }, + "consumables": { + "clear": "クリア" + } + } + }, + "performance": { + "name": "Performance", + "button": "戦車を見る", + "description": "車輌の戦闘統計", + "seo_description": "Best tanks and their performances in World of Tanks Blitz", + "estimation": { + "body": "%s1 人のプレイヤーの推定統計。 %s2前に更新されました。", + "hours": "%s1 時間", + "minutes": "%s1 分" + }, + "player_count": { + "label": "統計期間:" + }, + "table": { + "tanks": { + "header": "車輌名", + "total": "トータル" + }, + "stats": { + "winrate": "勝率", + "players": "プレイヤー数", + "damage": "Dmg", + "survival": "生還率", + "xp": "EXP", + "kills": "撃破数", + "spots": "発見数", + "accuracy": "命中率", + "shots": "射撃数", + "hits": "命中数", + "damage_ratio": "ダメ比", + "damage_taken": "被ダメ", + "capture_points": "占領ポイント" + } + } + }, + "gallery": { + "name": "Gallery", + "button": "全て見る", + "description": "Blitzアバター図鑑", + "seo_description": "World of Tanks Blitz の 全ての プレイヤーアバター", + "last_updated": "最終更新: %s1", + "search": { + "hint": "アバターを検索...", + "results": "検索結果: %s1 個" + } + }, + "session": { + "name": "Session", + "button": "追跡開始", + "description": "セッション中プレイヤーの追跡", + "seo_description": "World of Tanks Blitz をプレイ中、統計をリアルタイムで追跡します", + "search": { + "lookup": "プレイヤーを検索して開始", + "hint": "プレイヤー名を入力...", + "searching": "検索中...", + "no_results": "プレイヤーが見つかりませんでした" + }, + "controls": { + "title": "%s1 を追跡中", + "subtitle": "開始日時: %s1", + "reset": "リセット", + "change": "変更" + }, + "no_cc": { + "title": "CCアカウントを追跡しようとしています", + "body": "CC (Community Contributor) アカウントは、統計が公開されていません。CC アカウントの埋め込みを追加する作業を進めていますので、お待ちください。", + "alright": "Alright" + }, + "no_battles": { + "title": "まだ戦闘未参加です", + "hint": "ゲームをプレイしてください" + }, + "table": { + "tank": "車輌名", + "total": "トータル", + "remove": "削除" + } + }, + "tier_list": { + "name": "Tier list", + "button": "ランク付け", + "description": "Blitzの戦車ランキング", + "seo_description": "World of Tanks Blitz のプレイヤー評価とパフォーマンス統計に基づいて、最良から最悪までのランキングを作成しましょう", + "buttons": { + "reset": "リセット", + "share": "共有" + } + }, + "embed": { + "name": "Embed", + "button": "今すぐ作成", + "description": "ストリーミング用の埋め込み", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "埋め込みは近日中さらに追加される予定です。", + "suggest": "アイデアを提案してください。", + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + "configuration": { + "import": { + "title": "Import", + "description": "Paste your current URL if you have one already to import your settings", + "hint": "Paste URL...", + "apply": "Apply", + "idiot_title": "Invalid URL", + "idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!", + "idiot_button": "Okay, my bad" + }, + "export": { + "title": "Export", + "description": "Copy all properties for you streaming software", + "generate": { + "button": "Generate URL", + "title": "Generate URL", + "subtitle": "Search an account to track", + "cancel": "Cancel", + "success": { + "title": "Copy URL", + "info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".", + "back": "戻る", + "copy": "Copy URL" + } + }, + "css": "Copy CSS", + "width": "Copy Width", + "height": "Copy Height", + "framerate": "Copy Framerate" + }, + "customize": { + "title": "Customize", + "reset": "リセット", + "description": "Bells and whistles of your embed" + } + }, + "types": { + "breakdown": { + "name": "Breakdown", + "options": { + "width": "全幅", + "height": "全高", + "showTotal": "Show total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Column gap", + "columnValue": "Column value", + "columnLabel": "Column label", + "column1": "Column 1", + "column2": "Column 2", + "column3": "Column 3", + "column4": "Column 4", + "column1CustomLabel": "Column 1 label", + "column2CustomLabel": "Column 2 label", + "column3CustomLabel": "Column 3 label" + } + } + } + }, + "charts": { + "name": "Charts", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "インストール", + "description": "Discordで統計情報を確認", + "seo_description": "Get stats right in Discord with BlitzKit's Discord bot" + }, + "more": { + "name": "More coming soon", + "button": "Discord に参加", + "description": "近日追加予定 - 私たちが作っているもの", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "プレイヤー数", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Home", + "button": "Get started", + "description": "さあ、BlitzKit を始めよう!", + "seo_description": "さあ、BlitzKit を始めよう!" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analyze now", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "ゲーム内のセッション統計", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + "footer": { + "motto": "Everything World of Tanks Blitz", + "copyright": "Wargamingやその他のサービスとは関係ありません", + "about": { + "title": "概要", + "credits": "クレジット", + "discord": "Discord", + "github": "GitHub", + "patches": "パッチノート" + }, + "legal": { + "title": "法的事項", + "privacy": "プライバシーポリシー", + "terms": "利用規約" + } + } + } +} diff --git a/packages/i18n/strings/pl.json b/packages/i18n/strings/pl.json new file mode 100644 index 000000000..13d0b6cc2 --- /dev/null +++ b/packages/i18n/strings/pl.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "", + "long": "To jest długi ciąg testowy. NIGDY NIE ZOBACZYSZ tego w interfejsie użytkownika. Mój ulubiony owoc to %s1, ponieważ ma kolor %s2. Tak bardzo je lubię, że powiem ich nazwę 3 razy! %s1! %s1! %s1! Po raz kolejny to %s2, a nie %s3.", + "fox": "Szybki brązowy lis przeskoczył nad leniwym psem." + }, + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Auto loader", + "auto_reloader": "Automatyczne przeładowanie", + "regular": "" + }, + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "HEAT", + "he": "OB" + }, + "player_count_period": { + "total": "Total", + "d_120": "Past 120 days", + "d_90": "Past 90 days", + "d_60": "Past 60 days", + "d_30": "Past month", + "d_7": "W minionym tygodniu", + "d_1": "Yesterday" + }, + "branches": { + "local": "dev", + "dev": "beta", + "opentest": "opentest", + "preview": "podgląd" + }, + "locales": { + "en": "Angielski", + "es": "Hiszpański", + "pt": "Portugalski", + "ru": "Rosyjski", + "uk": "Ukraiński", + "zh": "Chiński", + "ja": "Japoński", + "fr": "Francuski", + "pl": "Polski" + }, + "wn8_percentile": { + "0": "Bardzo złe", + "1": "Złe", + "2": "Poniżej średniej", + "3": "Średnie", + "4": "Powyżej średniej", + "5": "Dobre", + "6": "Bardzo dobre", + "7": "Świetne", + "8": "Unikalny", + "9": "Super unikalny" + }, + "bkni_percentile": { + "0": "Super źle", + "1": "Źle", + "2": "Bardzo złe", + "3": "Złe", + "4": "Poniżej średniej", + "5": "Średnie", + "6": "Powyżej średniej", + "7": "Dobre", + "8": "Bardzo dobre", + "9": "Unikalny", + "10": "Super unikalny" + }, + "nations": { + "ussr": "ZSRR", + "germany": "Germany", + "usa": "U.S.A.", + "china": "Chiny", + "uk": "Wielka Brytania", + "france": "France", + "japan": "Japan", + "european": "Nacja Europejska", + "other": "Nacja Hybrydowa" + }, + "nations_adjectives": { + "ussr": "ZSRR", + "germany": "German", + "usa": "Amerykański", + "china": "Chiński", + "uk": "Wielkiej Brytanii", + "france": "Francuski", + "japan": "Japoński", + "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": "Lekki", + "1": "Medium", + "2": "Heavy", + "3": "Tank destroyer" + }, + "tree_type": { + "-1": "unrecognized", + "0": "Tech tree", + "1": "Premium", + "2": "Kolekcjonerski" + }, + "regions": { + "normal": { + "com": "Ameryka Północna", + "eu": "Europa", + "asia": "Azja-Pacyfik" + }, + "short": { + "com": "NA", + "eu": "EU", + "asia": "APAC" + } + }, + "leagues": { + "diamond": "Diament", + "platinum": "Platyna", + "gold": "Złoty", + "silver": "Srebro", + "bronze": "Brąz" + }, + "composite_stats": { + "normalized_damage_dealt": "Uszkodzenia", + "normalized_damage_received": "Otrzymane uszkodzenia", + "normalized_deaths": "Wskaźnik śmierci", + "normalized_frags": "Zniszczenia", + "normalized_hits": "Trafienia", + "normalized_losses": "Wskaźnik przegranych", + "normalized_misses": "Nietrafione", + "normalized_shots": "Strzały", + "normalized_spotted": "Zwiad", + "normalized_survived_battles": "Współczynnik przeżycia", + "normalized_win_and_survived": "Wskaźnik wygranych i przeżywalności", + "normalized_wins": "Śr. wygranych", + "normalized_xp": "XP", + "normalized_battle_life_time": "Czas bitwy", + "cumulative_accuracy": "Skuteczność", + "cumulative_battles": "Bitwy", + "cumulative_capture_points": "Całkowita liczba punktów bazy", + "cumulative_damage_dealt": "Łączne obrażenia", + "cumulative_damage_ratio": "Współczynnik obrażeń", + "cumulative_damage_received": "Całkowita liczba otrzymanych obrażeń", + "cumulative_deaths": "Łączna liczba zgonów", + "cumulative_dropped_capture_points": "Całkowita liczba zdjętych punktów bazy", + "cumulative_frags": "Łączna liczba zabójstw", + "cumulative_hits": "Łączna liczba trafień", + "cumulative_inaccuracy": "Niedokładność", + "cumulative_kills_to_death_ratio": "Stosunek zabójstw do śmierci", + "cumulative_losses": "Przegrane", + "cumulative_misses": "Łącznie nietrafionych", + "cumulative_shots": "Strzały ogółem", + "cumulative_spotted": "Suma wykryć", + "cumulative_survived_battles": "Przetrwane bitwy", + "cumulative_win_and_survived": "Wygrana i przetrwana bitwa", + "cumulative_wins": "Zwycięstwa", + "cumulative_xp": "Łączne PD", + "cumulative_wn8": "WN8", + "cumulative_battle_life_time": "Całkowity czas bitwy" + } + }, + "bot": { + "common": { + "errors": { + "clan_not_found": "# Nie można znaleźć klanu\nNie można znaleźć \"%s1\". Spróbuj wybrać klan z wyników wyszukiwania.", + "player_not_found": "# Nie można znaleźć użytkownika\nNie mogłem znaleźć użytkownika \"%s1\". Spróbuj wybrać użytkownika z wyniku wyszukiwania, wpisując poprawną nazwę użytkownika lub używając polecenia `/link`.", + "player_not_linked": "# Użyj `/link` aby zacząć\nNie wiem, kim jesteś, ale możesz mi dziś powiedzieć!", + "tank_name_not_found": "# Czołg nie został znaleziony\nNie można znaleźć czołgu o nazwie \"%s1\".", + "tank_id_not_found": "# Czołg nie został znaleziony\nNie można znaleźć czołgu o ID \"%s1\". Spróbuj wpisać nazwę czołgu zamiast liczby.", + "no_tank_stats": "# Brak dostępnych statystyk czołgów\nTen gracz nie ma żadnych dostępnych statystyk. Może to nie być gracz, którego szukasz.", + "get_help": "Pomóż serwerowi Discord", + "uncaught_error": "# BlitzKit wpadł w błąd!\nPrzepraszamy za to. Możesz [dołączyć do oficjalnego serwera Discord](https://discord.gg/nDt7AjGJQH), aby uzyskać pomoc.", + "preview": "# Ta komenda jest w podglądzie\n\nMożesz uzyskać dostęp do tej komendy tylko na [oficjalnym serwerze Discorda](https://discord.gg/nDt7AjGJQH). Polecenie będzie ogólnie dostępne wkrótce.", + "query_too_small": "# Twoje wyszukiwanie jest zbyt krótkie\nTwoje wyszukiwanie musi mieć co najmniej 3 znaki.", + "query_too_long": "# Twoje wyszukiwanie jest zbyt długie\nTwoje wyszukiwanie musi mieć maksymalnie 100 znaków." + }, + "wn8": { + "name": "WN8", + "types": { + "very_bad": "Bardzo złe", + "bad": "Złe", + "below_average": "Poniżej średniej", + "average": "Średnia", + "above_average": "Powyżej średniej", + "good": "Dobrze", + "very_good": "Bardzo dobrze", + "great": "Świetne", + "unicum": "Unikalny ", + "super_unicum": "Bardzo unikalny" + } + }, + "subcommands": { + "today": { + "name": "dziś", + "description": "Dziś" + }, + "30": { + "name": "30", + "description": "30 dni" + }, + "60": { + "name": "60", + "description": "60 dni" + }, + "90": { + "name": "90", + "description": "90 dni" + }, + "career": { + "name": "kariera", + "description": "Kariera" + }, + "custom": { + "name": "Niestandardowy", + "description": "Zakres niestandardowy", + "options": { + "start": { + "name": "start", + "description": "Rozpocznij od dnia dzisiejszego" + }, + "end": { + "name": "zakończ", + "description": "Zakończ od dnia dzisiejszego" + } + } + } + }, + "options": { + "clan": { + "name": "klan", + "description": "Wpisz nazwę klanu lub tag" + }, + "tank": { + "name": "czołg", + "description": "Czołg do pobrania statystyk" + }, + "username": { + "name": "nazwa użytkownika", + "description": "Nazwa użytkownika Blitz gracza" + }, + "filters": { + "nation": { + "name": "naród", + "description": "Naród" + }, + "class": { + "name": "klasa", + "description": "Klasa" + }, + "type": { + "name": "rodzaj", + "description": "Rodzaj" + } + }, + "region": { + "name": "region", + "description": "Region" + }, + "tier": { + "name": "poziom", + "description": "Poziom", + "choices": { + "tier": "Poziom %s1 - %s2" + } + } + }, + "periods": { + "career": "Kariera", + "today": "Dzisiaj", + "days": "%s1 dzień", + "custom": "%s1 - %s2 dni" + }, + "filters": { + "none": "Brak filtrów", + "tier": "Poziom %s1" + }, + "no_data": { + "battles_in_period": "Brak bitew w tym okresie", + "battles_in_season": "Brak bitew w tym sezonie", + "players_in_period": "Brak graczy w tym okresie", + "tanks_found": "Nie znaleziono czołgu" + }, + "buttons": { + "refresh": "Odśwież" + }, + "hero_stats": { + "battles": "Bitwy", + "winrate": "Zwycięstwa", + "damage": "Obrażenia", + "damage_ratio": "Współczynnik obrażeń", + "tier": "Poziom", + "survival": "Przeżycie", + "accuracy": "Celność", + "kills": "Zabójstwa" + } + }, + "commands": { + "auction": { + "name": "aukcja", + "description": "Czołgi dostępne na aukcji", + "body": { + "title": "Aukcja Blitz", + "subtitle": "%s1 dostępne • następne ceny %s2\nOferta teraz: %s3", + "next": "Następna cena: %s1", + "available": "Dostępne: %s1 / %s2" + }, + "errors": { + "no_auction": "# Brak bieżącej aukcji\n\nObecnie nie ma dostępnych czołgów do zakupu na aukcji. Jeśli uważasz, że to błąd, zgłoś go na [oficjalnym serwerze Discord](https://discord.gg/nDt7AjGJQH)." + }, + "actions": { + "bid": "Oferta teraz" + } + }, + "replay": { + "name": "powtórz", + "description": "Wyświetl wyniki powtórki" + }, + "research": { + "name": "badanie", + "description": "Wyświetla wydatki na badanie linii", + "options": { + "target_tank": { + "name": "docelowy czołg", + "description": "Ostatni czołg do badań w linii" + }, + "starting_tank": { + "name": "czołg rozpoczynający", + "description": "Czołg początkowy w linii (automatycznie, jeżeli nie jest dostarczony)" + } + }, + "body": { + "subtitle": "Rozpocznij badania", + "total": "Całkowity", + "research": "Badanie: %s1", + "upgrades": "Ulepszenie: %s1", + "purchase": "Zakup: %s1", + "equipment": "Wyposażenie: %s1", + "estimation": "# To jest szacunek!\n\nTa lista może zawierać już zbadane czołgi z powodu niedokładnych informacji dostarczanych przez Wargaming. Spróbuj podać `starting-tank` z wiersza podczas używania polecenia, jeśli możesz." + }, + "errors": { + "non_tech_tree": "# %s1 nie jest na drzewie technologii\n\nNie możesz zbadać czołg premium/kolekcjonerski.", + "tier_1": "# %s1 jest czołgami poziomu I\n\nPoziom I są zawsze odblokowane za darmo dla wszystkich graczy.", + "already_researched": "# %s1 jest już zbadany\n\nSpróbuj podać `starting-tank` gdy użyjesz komendy aby zobaczyć sumy dla linii, którą już zbadałeś.", + "start_end_equal": "# %s1 został dostarczony dwa razy\n\nUpewnij się, że `starting-tank` i `target-tank` są inne.", + "tanks_not_on_line": "# %s i %s nie znajdują się w tej samej linii\n\nUpewnij się, że `starting-tank` i `target-tank` znajdują się na tej samej linii drzewa technicznego.", + "unordered": "# %s1 wyższy stopień %s2\n\nUpewnij się, że zamiast tego %s3 jest `starting-tank`, ponieważ jest tierowo niższym czołgiem." + } + }, + "about": { + "name": "opis", + "description": "Wszystkie informacje na temat bota", + "subcommands": { + "commands": { + "name": "komendy", + "description": "Lista wszystkich poleceń i ich opisów", + "body": "# Polecenia\n\nBlitzKit oferuje następujące polecenia:\n\n" + }, + "embeds": { + "name": "osadzenie", + "description": "Jak działa osadzanie" + }, + "introduction": { + "name": "wprowadzenie", + "description": "O bocie" + }, + "invite": { + "name": "zaproszenie", + "description": "Jak zaprosić bota" + }, + "timezones": { + "name": "strefy czasowe", + "description": "W jaki sposób BlitzKit wnioskuje i wykorzystuje Twoją strefę czasową" + }, + "credits": { + "name": "uznania", + "description": "Ludzie, którzy pomogli BlitzKit" + } + } + }, + "breakdown": { + "name": "podział", + "description": "Podział przez okres według rozgrywanych czołgów", + "body": { + "total": "Ogółem", + "battles": "Bitwy", + "wn8": "WN8", + "winrate": "Śr. wygranych", + "damage": "Uszkodzenia", + "unknown_tank": "Nieznany czołg %s1" + } + }, + "debug": { + "name": "debugowanie", + "description": "Debugowanie informacji o botu", + "body": { + "title": "# Debug\n\n", + "version": "Wersja", + "shard": { + "label": "Fragment", + "default": "domyślne" + }, + "uptime": "Czas działania" + } + }, + "evolution": { + "name": "rozwój", + "description": "Rozwój statystyk", + "groups": { + "player": { + "name": "gracz", + "description": "Statystyki gracza" + }, + "tank": { + "name": "czołg", + "description": "Statystyki czołgu" + } + } + }, + "inactive": { + "name": "inactive", + "description": "Lists all inactive players in a clan above a threshold", + "options": { + "threshold": { + "name": "threshold", + "description": "Liczba dni nieaktywnych (domyślnie: %s dni)" + } + }, + "body": { + "subtitle": "Inactive for %s1+ days", + "listing": "%s1 dzień" + } + }, + "full_stats": { + "name": "full-stats", + "description": "Full in-game statistics", + "body": { + "cards": { + "games": { + "title": "Games", + "winrate": "Śr. wygranych", + "battles": "Bitwy", + "wins_losses": "Zwycięstwa / porażki" + }, + "efficiency": { + "title": "Efficiency", + "wn8": "WN8", + "survival": "Przeżycie", + "accuracy": "Skuteczność" + }, + "lethality": { + "title": "Lethality", + "average_damage": "Średnie uszkodzenia", + "damage_per_tier": "Damage per tier", + "damage_ratio": "Współczynnik obrażeń", + "average_kills": "Average kills" + }, + "miscellaneous": { + "title": "Miscellaneous", + "average_tier": "Średni poziom", + "average_spots": "Average spots", + "average_xp": "Average XP", + "kills_to_death_ratio": "Stosunek zabójstw do śmierci" + } + } + } + }, + "link": { + "name": "link", + "description": "Links your Blitz and Discord account", + "body": { + "accounts_linked": "Accounts linked", + "no_role_permissions": { + "title": "%s1 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": { + "name": "search-clans", + "description": "Search clans", + "options": { + "limit": { + "name": "limit", + "description": "The max number of clan results (default: %s1)" + } + }, + "body": { + "title": "Clan search for %s1 in region %s2", + "no_results": "No clans found" + } + }, + "search_players": { + "name": "search-players", + "description": "Search players", + "options": { + "limit": { + "name": "limit", + "description": "Maksymalna liczba wyników gracza (domyślnie: %s)" + } + }, + "body": { + "title": "Player search for %s1 in region %s2", + "no_results": "No players found" + } + }, + "rating_leaderboard": { + "name": "rating-leaderboard", + "description": "Rating battles statistics", + "subcommands": { + "neighbors": { + "name": "neighbors", + "description": "Neighboring players on the leaderboard" + }, + "league": { + "name": "league", + "description": "Najlepsi gracze ligi" + } + }, + "options": { + "limit": { + "name": "limit", + "description": "How many neighbors to display (default: %s1)" + }, + "season": { + "name": "season", + "description": "Season number (default: current)", + "choices": { + "current": "%s1 (current)" + } + }, + "league": { + "name": "league", + "description": "The league to display" + } + }, + "body": { + "top_players": "Top %s1 players", + "no_participation": "# %s1 didn't play rating in season %s2\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": "Usunięto %s1", + "neighbors": "Rating neighbors", + "more": "+ %s1 więcej" + } + }, + "today": { + "name": "dzisiaj", + "description": "Dzisiejsze zagrane czołgami i statystyki" + }, + "stats": { + "name": "stats", + "description": "Szybkie statystyki na okres" + }, + "owned_tanks": { + "name": "posiadane czołgi", + "description": "Pokazuje wszystkie własne czołgi na poziomie", + "body": { + "title": "# %s1 posiadany poziom %s2 czołgi\n\n" + } + }, + "permissions": { + "name": "uprawnienia", + "description": "Sprawdza, czy bot ma wymagane uprawnienia do funkcji", + "body": { + "title": "# Uprawnienia\n\n%s1", + "view_channels": "Wyświetl kanały: potrzebne do znalezienia wiadomości użyty przycisk odświeżania", + "read_message_history": "Przeczytaj historię wiadomości: potrzebne do znalezienia wiadomości do odświeżenia", + "attach_files": "Dołącz pliki: potrzebne do dodawania obrazów do komend opartych na obrazach", + "external_emojis": "Zewnętrzne emotikony: potrzebne do dodania flagi krajowych i emotikonów klasy zbiorników" + } + }, + "ping": { + "name": "ping", + "description": "Sprawdź, czy bot żyje.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "informacje o graczu", + "description": "Podstawowe informacje o graczu", + "body": { + "title": "Informacje o %s1", + "nickname": "Nick", + "battles": "Bitwy", + "winrate": "Śr. wygranych", + "account_id": "ID konta", + "created": "Stworzony", + "last_battle": "Ostatnia bitwa" + } + }, + "rating": { + "name": "klasyfikacja", + "description": "Statystyki klasyfikacji gracza", + "subcommands": { + "today": { + "name": "dzisiaj", + "description": "Dzisiejsze statystyki klasyfikacji" + }, + "season": { + "name": "sezon", + "description": "Statystyki klasyfikacji w tym sezonie" + } + }, + "body": { + "subtitle": { + "today": "Dzisiejsze statystyki klasyfikacji", + "season": "Statystyki klasyfikacji w tym sezonie" + } + }, + "errors": { + "no_participation": "# %s1 nie grał w \n\nTen gracz nie uczestniczył w grze oceniającej w tym sezonie.", + "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": { + "settings": { + "dev_mode": "Development mode", + "language": "Language" + }, + "common": { + "player_search": { + "hint": "Search players...", + "no_results": "No results", + "searching": "Searching..." + }, + "copy_button": { + "copied": "Copied!" + }, + "tank_search": { + "search_bar_hint": "Search tanks...", + "recent": "Recently viewed", + "select_all": "Select all", + "count_singular": "1 tank", + "count_plural": "%s1 tanks", + "sorting_by": "Sorting by %s1", + "no_tanks_found": { + "body": "No tanks found.", + "search_again": "Try searching again.", + "clear_filters": "Try clearing filters." + }, + "sort": { + "meta.none": "none", + "survivability.health": "health", + "survivability.viewRange": "view range", + "survivability.camouflageStill": "camouflage still", + "survivability.camouflageMoving": "camouflage moving", + "survivability.camouflageShooting": "camouflage shooting", + "survivability.volume": "volume", + "survivability.length": "length", + "fire.dpm": "standard DPM", + "fire.dpmPremium": "premium DPM", + "fire.reload": "reload", + "fire.caliber": "caliber", + "fire.standardPenetration": "standard penetration", + "fire.premiumPenetration": "premium penetration", + "fire.damage": "damage", + "fire.moduleDamage": "module damage", + "fire.shellVelocity": "shell velocity", + "fire.shellRange": "shell range", + "fire.shellCapacity": "shell capacity", + "fire.aimTime": "aim time", + "fire.dispersionStill": "dispersion still", + "fire.dispersionMoving": "dispersion moving", + "fire.gunDepression": "gun depression", + "fire.gunElevation": "gun elevation", + "maneuverability.forwardsSpeed": "forwards speed", + "maneuverability.backwardsSpeed": "backwards speed", + "maneuverability.power": "power", + "maneuverability.powerToWeight": "power to weight", + "maneuverability.weight": "weight", + "maneuverability.traverseSpeed": "traverse speed" + }, + "sort_dropdown": { + "label": "Sort", + "groups": { + "sort": { + "no_sort": "No sort", + "label": "Sort by", + "fire": "Fire", + "maneuverability": "Maneuverability", + "survivability": "Survivability" + }, + "direction": { + "label": "Direction", + "ascending": "Ascending", + "descending": "Descending" + } + } + } + }, + "warnings": { + "test": "Tanks in testing are subject to change and may not represent the final product." + } + }, + "home": { + "hero": { + "credit": "By %s1 ❤️" + }, + "hot_tanks": { + "title": "Popular tanks", + "subtitle": "Last %s1 days" + }, + "other_projects": { + "title": "Other cool projects" + }, + "help_translate": "Please help translate %s1 to %s2!", + "seo_description": "Tools for everything World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Tankopedia", + "button": "Find tanks", + "description": "Blitz tank encyclopedia", + "seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz", + "tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.", + "sandbox": { + "poses": { + "hull_down": "Hull down", + "face_hug": "Face hug", + "default": "Default" + }, + "settings": { + "armor": "Armor", + "green_penetration": "Green penetration", + "hide_model_under_armor": "Hide model under armor", + "opaque": "Opaque", + "dev_wireframe": "DEV: wireframe", + "environment": "Environment", + "show_grid": "Show grid" + }, + "screenshot": { + "download": "Download", + "copy": "Copy" + }, + "model": { + "name": "Model" + }, + "dynamic": { + "name": "Dynamic armor", + "custom_shell": { + "type": "Rodzaj", + "caliber": "Caliber", + "penetration": "Penetration", + "normalization": "Normalization", + "ricochet": "Ricochet", + "explosion_radius": "Explosion radius", + "exit": "Exit", + "reset": "Reset" + }, + "advanced": "Advanced highlighting", + "shooter": "Shooter:", + "shot_card": { + "element": { + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "gap": "Gap", + "gun": "Gun", + "track": "Tracks" + }, + "status": { + "penetration": "%s1hp penetration", + "blocked": "Blocked", + "ricochet": "Ricochet", + "splash": "%s1hp splash" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetration" + } + } + }, + "static": { + "name": "Static armor", + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "edit": { + "label": "Edit", + "reset": "Reset" + } + } + }, + "meta": { + "back": "Back", + "compare": "Compare", + "swap": { + "button": "Swap", + "title": "Swap tanks" + }, + "vanilla": "Vanilla", + "opentest": "OpenTest", + "full_name": "Full name", + "nation": "Naród", + "class": "Klasa", + "tier": "Poziom", + "type": "Rodzaj", + "dev_id": "DEV: ID", + "purchase": "Purchase price", + "restoration": "Restoration price", + "sale": "Sale price", + "research": "Research XP" + }, + "tech_tree": { + "title": "Tech tree", + "battle": "1 battle", + "battles": "%s1 battles", + "route": "Route %s1 of %s2" + }, + "game_modes": { + "title": "Game mode abilities" + }, + "review": { + "title": "Review videos", + "disclaimer": "BlitzKit does not endorse any review videos.", + "join": "Apply to %s1 YouTuber program" + }, + "configuration": { + "title": "Configuration", + "modules": { + "title": "Modules", + "stock": "Stock", + "upgrade": "Upgrade" + }, + "equipment": { + "title": "Equipment", + "clear": "Clear" + }, + "provisions": { + "title": "Provisions", + "clear": "Clear" + }, + "consumables": { + "title": "Consumables", + "clear": "Clear", + "info": "Yellow consumables do not affect the statistics." + }, + "skills": { + "title": "Crew skills", + "clear": "Clear", + "maximize": "Maximize", + "info": "Yellow skills only apply under special circumstances. Hold Space to quickly toggle between level 0 and 7." + }, + "miscellaneous": { + "title": "Miscellaneous" + } + }, + "characteristics": { + "title": "Characteristics", + "compare": { + "label": "Compare against", + "class": "Tier %s1 %s2", + "tier": "All of tier %s1", + "all": "All tanks" + }, + "values": { + "shell_index": "Shell %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "Distance", + "dispersion_at_distance": "Dispersion at %s1m", + "gun_flexibility": "Gun flexibility", + "speed": "Speed", + "terrain_coefficients": "Terrain coefficients", + "raw_terrain_coefficients": "Raw terrain coefficients", + "power_to_weight_ratio": "Power to weight ratio", + "hull_traverse_speed": "Hull traverse speed", + "camouflage": "Camouflage", + "reload": "Reload", + "crewCount": "Crew count", + "shellNormalization": "Normalization", + "shellRicochet": "Ricochet", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Shells", + "mostOptimalShellIndex": "Most optimal shell", + "shellReloads": "Shell reloads", + "shellReload": "Shell reload", + "intraClip": "Intra-clip", + "caliber": "Caliber", + "penetration": "Penetration", + "damage": "Uszkodzenia", + "clipDamage": "Clipping potential", + "moduleDamage": "Module damage", + "explosionRadius": "Explosion radius", + "shellVelocity": "Shell velocity", + "aimTime": "Aim time", + "dispersion": "Still", + "dispersionMoving": "Moving", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Shooting", + "dispersionGunDamaged": "Gun damaged", + "gunDepression": "Depression", + "gunElevation": "Elevation", + "gunFrontalDepression": "Frontal depression", + "gunFrontalElevation": "Frontal elevation", + "gunRearDepression": "Rear depression", + "gunRearElevation": "Rear elevation", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Forwards", + "speedBackwards": "Backwards", + "hardTerrainCoefficient": "Hard", + "hardTerrainCoefficientRaw": "Hard", + "mediumTerrainCoefficient": "Medium", + "mediumTerrainCoefficientRaw": "Medium", + "softTerrainCoefficient": "Soft", + "softTerrainCoefficientRaw": "Soft", + "enginePower": "Engine power", + "powerToWeightRatioHardTerrain": "Hard", + "powerToWeightRatioMediumTerrain": "Medium", + "powerToWeightRatioSoftTerrain": "Soft", + "weight": "Weight", + "turretTraverseSpeed": "Turret traverse speed", + "gunTraverseSpeed": "Gun traverse speed", + "hullTraverseHardTerrain": "Hard", + "hullTraverseMediumTerrain": "Medium", + "hullTraverseSoftTerrain": "Soft", + "health": "Health", + "fireChance": "Fire chance", + "viewRange": "View range", + "camouflageStill": "Still", + "camouflageMoving": "Moving", + "camouflageShootingStill": "Still", + "camouflageShootingMoving": "Moving", + "camouflageCaughtOnFire": "Caught on fire", + "width": "Width", + "height": "Height", + "length": "Length", + "volume": "Volume", + "shellRange": "Shell range", + "shellCapacity": "Shell capacity", + "gunType": "Gun type", + "penetrationAt250m": "Penetration at 250m" + } + }, + "firepower": { + "title": "Firepower" + }, + "crew": { + "title": "Crew training", + "info": "represents the other roles a crew member can take.", + "count": "Crew count", + "commander": "Commander", + "gunner": "Gunner", + "driver": "Driver", + "radioman": "Radioman", + "loader": "Loader" + }, + "maneuverability": { + "title": "Maneuverability" + }, + "survivability": { + "title": "Survivability" + } + }, + "compare": { + "name": "Compare", + "button": "Compare tanks", + "description": "Compare tank statistics", + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + "actions": { + "add": { + "button": "Add", + "title": "Add tanks", + "description": "Select tanks to add to comparison" + }, + "clear": "Clear", + "deltas": { + "none": "No deltas", + "percentage": "Percentage", + "absolute": "Absolute" + } + }, + "no_tanks": { + "title": "No tanks selected", + "body": "Press the Add button to get started" + }, + "table": { + "equipment": { + "clear": "Clear", + "apply_to_all": "Apply to all" + }, + "skills": { + "clear": "Clear", + "maximize": "Maximize" + }, + "modules": { + "stock": "Stock", + "upgrade": "Upgrade" + }, + "provisions": { + "clear": "Clear" + }, + "consumables": { + "clear": "Clear" + } + } + }, + "performance": { + "name": "Performance", + "button": "View tanks", + "description": "Tank performance statistics", + "seo_description": "Best tanks and their performances in World of Tanks Blitz", + "estimation": { + "body": "Estimated stats based on %s1 players. Updated %s2 ago.", + "hours": "%s1 hours", + "minutes": "%s1 minutes" + }, + "player_count": { + "label": "Player count" + }, + "table": { + "tanks": { + "header": "Tank", + "total": "Total" + }, + "stats": { + "winrate": "Śr. wygranych", + "players": "Players", + "damage": "Uszkodzenia", + "survival": "Przeżycie", + "xp": "XP", + "kills": "Zniszczenia", + "spots": "Zwiad", + "accuracy": "Skuteczność", + "shots": "Strzały", + "hits": "Trafienia", + "damage_ratio": "Dmg. ratio", + "damage_taken": "Dmg. taken", + "capture_points": "Cap points" + } + } + }, + "gallery": { + "name": "Gallery", + "button": "Browse all", + "description": "Blitz player avatars", + "seo_description": "All World of Tanks Blitz player avatars", + "last_updated": "Last updated on %s1", + "search": { + "hint": "Search avatars...", + "results": "%s1 avatars" + } + }, + "session": { + "name": "Session", + "button": "Start tracking", + "description": "Live session stats", + "seo_description": "Track your stats in real time as you play World of Tanks Blitz", + "search": { + "lookup": "Look up a player to get started", + "hint": "Search players...", + "searching": "Searching...", + "no_results": "No players found" + }, + "controls": { + "title": "Tracking %s1", + "subtitle": "Since %s1", + "reset": "Reset", + "change": "Change" + }, + "no_cc": { + "title": "You're attempting to track a CC account", + "body": "CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned.", + "alright": "Alright" + }, + "no_battles": { + "title": "No battles", + "hint": "Go ahead and play a game" + }, + "table": { + "tank": "Tank", + "total": "Total", + "remove": "Remove" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Rank tanks", + "description": "Rank Blitz tanks", + "seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz", + "buttons": { + "reset": "Reset", + "share": "Share" + } + }, + "embed": { + "name": "Embed", + "button": "Create now", + "description": "Embeds for streaming", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "More embeds coming soon.", + "suggest": "Suggest ideas.", + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + "configuration": { + "import": { + "title": "Import", + "description": "Paste your current URL if you have one already to import your settings", + "hint": "Paste URL...", + "apply": "Apply", + "idiot_title": "Invalid URL", + "idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!", + "idiot_button": "Okay, my bad" + }, + "export": { + "title": "Export", + "description": "Copy all properties for you streaming software", + "generate": { + "button": "Generate URL", + "title": "Generate URL", + "subtitle": "Search an account to track", + "cancel": "Cancel", + "success": { + "title": "Copy URL", + "info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".", + "back": "Back", + "copy": "Copy URL" + } + }, + "css": "Copy CSS", + "width": "Copy Width", + "height": "Copy Height", + "framerate": "Copy Framerate" + }, + "customize": { + "title": "Customize", + "reset": "Reset", + "description": "Bells and whistles of your embed" + } + }, + "types": { + "breakdown": { + "name": "Breakdown", + "options": { + "width": "Width", + "height": "Height", + "showTotal": "Show total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Column gap", + "columnValue": "Column value", + "columnLabel": "Column label", + "column1": "Column 1", + "column2": "Column 2", + "column3": "Column 3", + "column4": "Column 4", + "column1CustomLabel": "Column 1 label", + "column2CustomLabel": "Column 2 label", + "column3CustomLabel": "Column 3 label" + } + } + } + }, + "charts": { + "name": "Charts", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "Install now", + "description": "Stats right in Discord", + "seo_description": "Get stats right in Discord with BlitzKit's Discord bot" + }, + "more": { + "name": "More coming soon", + "button": "Join Discord", + "description": "What we're making", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "Players", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Home", + "button": "Get started", + "description": "Get started with BlitzKit", + "seo_description": "Get started with BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analyze now", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "In-game session stats", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + "footer": { + "motto": "Everything World of Tanks Blitz", + "copyright": "Unaffiliated with Wargaming or any service", + "about": { + "title": "About", + "credits": "Credits", + "discord": "Discord", + "github": "GitHub", + "patches": "Patches" + }, + "legal": { + "title": "Legal", + "privacy": "Privacy policy", + "terms": "Terms of service" + } + } + } +} diff --git a/packages/i18n/strings/pt.json b/packages/i18n/strings/pt.json new file mode 100644 index 000000000..4c8f84c5e --- /dev/null +++ b/packages/i18n/strings/pt.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "teste", + "long": "Esta é uma sequência de testes longa. Você NUNCA DEVE ver isto na UI. Minha fruta favorita é %s1 porque tem a cor de %s2. Eu gosto tanto dela que vou dizer o nome dela 3 vezes! %s1! %s1! %s1! Mais uma vez, é %s2, não %s3.", + "fox": "A ágil raposa castanha salta por cima do cão preguiçoso." + }, + "common": { + "units": { + "m": "%s1m", + "mm": "%s1mm", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Autocarregador", + "auto_reloader": "Autorrecarregador", + "regular": "Regular" + }, + "shells": { + "ap": "AP", + "apcr": "APCR", + "heat": "HEAT", + "he": "HE" + }, + "player_count_period": { + "total": "Total", + "d_120": "Últimos 120 dias", + "d_90": "Últimos 90 dias", + "d_60": "Últimos 60 dias", + "d_30": "Último mês", + "d_7": "Última semana", + "d_1": "Ontem" + }, + "branches": { + "local": "dev", + "dev": "beta", + "opentest": "testeaberto", + "preview": "visualização" + }, + "locales": { + "en": "Inglês", + "es": "Espanhol", + "pt": "Português", + "ru": "Russo", + "uk": "Ucraniano", + "zh": "Chinês", + "ja": "Japonês", + "fr": "Francês", + "pl": "Polonês" + }, + "wn8_percentile": { + "0": "Muito ruim", + "1": "Ruim", + "2": "Abaixo da média", + "3": "Na média", + "4": "Acima da média", + "5": "Bom", + "6": "Muito bom", + "7": "Ótimo", + "8": "Unicum", + "9": "Super unicum" + }, + "bkni_percentile": { + "0": "Super malum", + "1": "Malum", + "2": "Muito ruim", + "3": "Ruim", + "4": "Abaixo da média", + "5": "Na média", + "6": "Acima da média", + "7": "Bom", + "8": "Muito bom", + "9": "Excelente", + "10": "Super unicum" + }, + "nations": { + "ussr": "U.R.S.S.", + "germany": "Alemanha", + "usa": "E.U.A.", + "china": "China", + "uk": "Reino Unido", + "france": "França", + "japan": "Japão", + "european": "Nação Europeia", + "other": "Nação Híbrida" + }, + "nations_adjectives": { + "ussr": "U.R.S.S.", + "germany": "Alemão", + "usa": "E.U.A.", + "china": "Chinês", + "uk": "Reino Unido", + "france": "Francês", + "japan": "Japonês", + "european": "Europeu", + "other": "Híbrido" + }, + "tank_class_short": { + "-1": "Não reconhecido", + "0": "Leve", + "1": "Médio", + "2": "Pesado", + "3": "Caça-tanques" + }, + "tank_class_short_plural_lowercase": { + "0": "leves", + "1": "médios", + "2": "pesados", + "3": "Caça-tanques" + }, + "tank_class_medium": { + "-1": "não reconhecido", + "0": "Leve", + "1": "Médio", + "2": "Pesado", + "3": "Caça-tanque" + }, + "tree_type": { + "-1": "não reconhecido", + "0": "Árvore Tecnológica", + "1": "Premium", + "2": "Colecionador" + }, + "regions": { + "normal": { + "com": "América do Norte", + "eu": "Europa", + "asia": "Ásia-Pacífico" + }, + "short": { + "com": "NA", + "eu": "EU", + "asia": "APAC" + } + }, + "leagues": { + "diamond": "Diamante", + "platinum": "Platina", + "gold": "Ouro", + "silver": "Prata", + "bronze": "Bronze" + }, + "composite_stats": { + "normalized_damage_dealt": "Dano", + "normalized_damage_received": "Dano recebido", + "normalized_deaths": "Taxa de mortes", + "normalized_frags": "Abates", + "normalized_hits": "Acertos", + "normalized_losses": "Taxa de derrota", + "normalized_misses": "Tiros errados", + "normalized_shots": "Disparos", + "normalized_spotted": "Detecções", + "normalized_survived_battles": "Taxa de sobrevivência", + "normalized_win_and_survived": "Taxa de sobrevivência e vitórias", + "normalized_wins": "Taxa de vitória", + "normalized_xp": "EXP", + "normalized_battle_life_time": "Tempo em batalha", + "cumulative_accuracy": "Precisão", + "cumulative_battles": "Batalhas", + "cumulative_capture_points": "Total de pontos de captura", + "cumulative_damage_dealt": "Dano total", + "cumulative_damage_ratio": "Razão entre dano dado e recebido", + "cumulative_damage_received": "Dano total recebido", + "cumulative_deaths": "Total de mortes", + "cumulative_dropped_capture_points": "Total de pontos de captura perdidos", + "cumulative_frags": "Total de abates", + "cumulative_hits": "Total de acertos", + "cumulative_inaccuracy": "Imprecisão", + "cumulative_kills_to_death_ratio": "Razão entre abates e mortes", + "cumulative_losses": "Derrotas", + "cumulative_misses": "Total de tiros errados", + "cumulative_shots": "Total de disparos", + "cumulative_spotted": "Total de detecções", + "cumulative_survived_battles": "Batalhas sobrevividas", + "cumulative_win_and_survived": "Batalhas ganhas e sobrevividas", + "cumulative_wins": "Vitórias", + "cumulative_xp": "EXP total", + "cumulative_wn8": "WN8", + "cumulative_battle_life_time": "Tempo de batalha total" + } + }, + "bot": { + "common": { + "errors": { + "clan_not_found": "# Não foi possível encontrar o clã\nNão foi possível encontrar \"%s1\". Tente selecionar um clã do resultado de pesquisa.", + "player_not_found": "# Não foi possível encontrar o usuário\nEu não consegui encontrar o usuário \"%s1\". Tente escolher um usuário do resultado de pesquisa, digitando um nome de usuário válido, ou usando o comando `/link`.", + "player_not_linked": "Digite \"/link\" para começar.\nEu posso não saber quem é você, mas pode-me dizer hoje sobre isso!", + "tank_name_not_found": "# Tanque não encontrado\nNão foi possível encontrar um tanque pelo nome \"%s1\".", + "tank_id_not_found": "# Tanque não encontrado\nNão foi possível encontrar o tanque pelo ID \"%s1\". Tente digitar o nome do tanque ao invés de um número.", + "no_tank_stats": "# Nenhuma estatística de tanques disponível\nEsse jogador não tem as suas estatísticas disponíveis. Talvez esse pode não ser o jogador que procura.", + "get_help": "Servidor de Ajuda no Discord", + "uncaught_error": "# O BlitzKit apresentou um erro!\nSentimos muito por isso. Sinta-se a vontade para [entrar no servidor oficial no Discord]\n(https://discord.gg/nDt7AjGJQH) para conseguir ajuda.", + "preview": "# Este comando está na pré-visualização\n\nVocê só pode acessar este comando no [servidor oficial do Discord](https://discord.gg/nDt7AjGJQH). O comando estará geralmente disponível em breve.", + "query_too_small": "# Sua pesquisa é muito curta\nSua pesquisa deve ter pelo menos 3 caracteres.", + "query_too_long": "# Sua pesquisa é muito longa\nSua pesquisa deve ter no máximo 100 caracteres." + }, + "wn8": { + "name": "WN8", + "types": { + "very_bad": "Muito ruim", + "bad": "Ruim", + "below_average": "Abaixo da média", + "average": "Na média", + "above_average": "Acima da média", + "good": "Bom", + "very_good": "Muito bom", + "great": "Ótimo", + "unicum": "Unicum", + "super_unicum": "Super unicum" + } + }, + "subcommands": { + "today": { + "name": "hoje", + "description": "Hoje" + }, + "30": { + "name": "30", + "description": "30 dias" + }, + "60": { + "name": "60", + "description": "60 dias" + }, + "90": { + "name": "90", + "description": "90 dias" + }, + "career": { + "name": "carreira", + "description": "Carreira" + }, + "custom": { + "name": "personalizado", + "description": "Intervalo personalizado", + "options": { + "start": { + "name": "iniciar", + "description": "Iniciar em número de dias atrás a partir de hoje" + }, + "end": { + "name": "terminar", + "description": "Termina em número de dias atrás a partir de hoje" + } + } + } + }, + "options": { + "clan": { + "name": "clã", + "description": "O nome ou tag do clã" + }, + "tank": { + "name": "tanque", + "description": "O tanque para obter estatísticas para" + }, + "username": { + "name": "nome de usuário", + "description": "O nome de usuário Blitz do jogador" + }, + "filters": { + "nation": { + "name": "nação", + "description": "Nação" + }, + "class": { + "name": "classe", + "description": "Classe" + }, + "type": { + "name": "tipo", + "description": "Tipo" + } + }, + "region": { + "name": "região", + "description": "Região" + }, + "tier": { + "name": "nível", + "description": "Nível", + "choices": { + "tier": "Nível %s1 - %s2" + } + } + }, + "periods": { + "career": "Carreira", + "today": "Hoje", + "days": "%s1 dias", + "custom": "%s1 - %s2 dias" + }, + "filters": { + "none": "Sem filtros", + "tier": "Nível %s1" + }, + "no_data": { + "battles_in_period": "Não há batalhas jogadas neste período", + "battles_in_season": "Não há batalhas jogadas nesta temporada", + "players_in_period": "Nenhum jogador neste período", + "tanks_found": "Nenhum tanque encontrado" + }, + "buttons": { + "refresh": "Atualizar" + }, + "hero_stats": { + "battles": "Batalhas", + "winrate": "Taxa de vitória", + "damage": "Dano", + "damage_ratio": "Razão entre dano dado e recebido", + "tier": "Nível", + "survival": "Sobrevivência", + "accuracy": "Precisão", + "kills": "Abates" + } + }, + "commands": { + "auction": { + "name": "leilão", + "description": "Tanques disponíveis agora mesmo no leilão", + "body": { + "title": "Leilão Blitz", + "subtitle": "%s1 disponível • próximos preços %s2 oferta agora: %s3", + "next": "Próximo preço: %s1", + "available": "Disponível: %s1 / %s2" + }, + "errors": { + "no_auction": "# Nenhum leilão em curso\n\nNo momento, não há tanques disponíveis para compra no leilão. Se você acha que isso é um erro, relate esta discrepância no [servidor oficial do Discord](https://discord.gg/nDt7AjGJQH)." + }, + "actions": { + "bid": "Ofertar agora" + } + }, + "replay": { + "name": "replay", + "description": "Exibir resultados de replays" + }, + "research": { + "name": "pesquisa", + "description": "Liste as despesas na pesquisa de uma linha", + "options": { + "target_tank": { + "name": "tanque-alvo", + "description": "O último tanque para pesquisar na linha" + }, + "starting_tank": { + "name": "tanque-inicial", + "description": "O tanque inicial na linha (inferido automaticamente se não fornecido)" + } + }, + "body": { + "subtitle": "Pesquisa", + "total": "Total", + "research": "Pesquisa: %s1", + "upgrades": "Melhorias: %s1", + "purchase": "Compra: %s1", + "equipment": "Equipamento: %s1", + "estimation": "# Esta é uma estimativa!\n\nEsta lista pode incluir tanques já pesquisados devido à informação imprecisa que a Wargaming fornece. Tente fornecer um `tanque-inicial` da linha ao usar o comando, se puder." + }, + "errors": { + "non_tech_tree": "# %s1 não está na árvore tecnológica\n\nVocê não pode pesquisar um tanque premium/colecionador.", + "tier_1": "# %s1 é um nível I\n\nOs tanques de nível I sempre são desbloqueados gratuitamente para todos os jogadores.", + "already_researched": "# %s1 já foi pesquisado\n\nTente fornecer um `tanque-inicial` ao usar o comando para ver os totais de uma linha que você já pesquisou.", + "start_end_equal": "# %s1 foi fornecido duas vezes\n\nCertifique-se de que `tanque-inicial` e `tanque-alvo` são diferentes.", + "tanks_not_on_line": "# %s1 e %s2 não estão na mesma linha\n\nCertifique-se de que `tanque-inicial` e `tanque-alvo` estão na mesma linha tecnológica.", + "unordered": "# %s1 de nível mais alto para %s2\n\nCertifique-se de fornecer %s3 como o `tanque-inicial` em vez disso, pois é o tanque de nível inferior." + } + }, + "about": { + "name": "sobre", + "description": "Todas as informações que você precisa sobre o bot", + "subcommands": { + "commands": { + "name": "comandos", + "description": "Lista de todos os comandos e suas descrições", + "body": "# Comandos\n\nBlitzKit oferece os seguintes comandos:\n" + }, + "embeds": { + "name": "incorporar", + "description": "Como incorporações funcionam" + }, + "introduction": { + "name": "Introdução", + "description": "Sobre o bot" + }, + "invite": { + "name": "convite", + "description": "Como convidar o bot" + }, + "timezones": { + "name": "fusos horários", + "description": "Como o BlitzKit inferi e usa seu fuso horário" + }, + "credits": { + "name": "créditos", + "description": "Pessoas que ajudaram BlitzKit" + } + } + }, + "breakdown": { + "name": "divisão", + "description": "Análise de um período por tanques jogados", + "body": { + "total": "Total", + "battles": "Batalhas", + "wn8": "WN8", + "winrate": "Taxa de vitória", + "damage": "Dano", + "unknown_tank": "Tanque desconhecido %s1" + } + }, + "debug": { + "name": "depuração", + "description": "Depurar informações sobre o bot", + "body": { + "title": "# Depuração\n", + "version": "Versão", + "shard": { + "label": "Fragmento", + "default": "padrão" + }, + "uptime": "Tempo de atividade" + } + }, + "evolution": { + "name": "evolução", + "description": "Evolução das estatísticas", + "groups": { + "player": { + "name": "jogador", + "description": "Estatísticas do Jogador" + }, + "tank": { + "name": "tanque", + "description": "Estatísticas do tanque" + } + } + }, + "inactive": { + "name": "inativo", + "description": "Liste todos os jogadores inativos em um clã acima de um limite", + "options": { + "threshold": { + "name": "limite", + "description": "O número de dias inativos (padrão: %s1 dias)" + } + }, + "body": { + "subtitle": "Inativo por %s1+ dias", + "listing": "%s1 dias" + } + }, + "full_stats": { + "name": "estatísticas-completas", + "description": "Estatísticas completas dentro do jogo", + "body": { + "cards": { + "games": { + "title": "Jogos", + "winrate": "Taxa de vitória", + "battles": "Batalhas", + "wins_losses": "Vitórias / Derrotas" + }, + "efficiency": { + "title": "Eficiência", + "wn8": "WN8", + "survival": "Sobrevivência", + "accuracy": "Precisão" + }, + "lethality": { + "title": "Letalidade", + "average_damage": "Dano médio", + "damage_per_tier": "Dano por nível", + "damage_ratio": "Razão entre dano dado e recebido", + "average_kills": "Média de abates" + }, + "miscellaneous": { + "title": "Diversos", + "average_tier": "Nível médio", + "average_spots": "Média de detecções", + "average_xp": "EXP Médio", + "kills_to_death_ratio": "Razão entre abates e mortes" + } + } + } + }, + "link": { + "name": "link", + "description": "Conecte sua conta Blitz e Discord", + "body": { + "accounts_linked": "Contas vinculadas", + "no_role_permissions": { + "title": "%s1 falhou ao verificar", + "description": "Eu não tenho permissão para gerenciar funções." + } + }, + "embed": { + "title": "Conta incorreta?", + "description": "Use o comando novamente e selecione um nome de usuário dos resultados da pesquisa." + } + }, + "search_clans": { + "name": "procurar-clãs", + "description": "Procurar clãs", + "options": { + "limit": { + "name": "limite", + "description": "O número máximo de resultados do clã (padrão: %s1)" + } + }, + "body": { + "title": "Pesquisa do clã para %s1 na região %s2", + "no_results": "Nenhum clã encontrado" + } + }, + "search_players": { + "name": "pesquisa-jogadores", + "description": "Procurar jogadores", + "options": { + "limit": { + "name": "limite", + "description": "O número máximo de resultados do jogador (padrão: %s1)" + } + }, + "body": { + "title": "Busca de jogador para %s1 na região %s2", + "no_results": "Nenhum jogador encontrado" + } + }, + "rating_leaderboard": { + "name": "classificação-tabela de classificação", + "description": "Estatísticas de batalhas de classificação", + "subcommands": { + "neighbors": { + "name": "vizinhos", + "description": "Jogadores vizinhos na tabela de classificação" + }, + "league": { + "name": "Liga", + "description": "Melhores jogadores de uma liga" + } + }, + "options": { + "limit": { + "name": "limite", + "description": "Quantos vizinhos exibir (padrão: %s1)" + }, + "season": { + "name": "temporada", + "description": "Número da temporada (padrão: atual)", + "choices": { + "current": "%s1 (atual)" + } + }, + "league": { + "name": "Liga", + "description": "A liga para exibir" + } + }, + "body": { + "top_players": "Melhores %s1 jogadores", + "no_participation": "# %s1 não jogou classificação na temporada %s2\n\nEste jogador não participou nesta temporada ou não passou da calibração.", + "no_ongoing_season": "# Nenhuma temporada em andamento\n\nNão há uma temporada de classificação em execução no momento.", + "deleted_player": "Excluído %s1", + "neighbors": "Classificação de vizinhos", + "more": "+ %s1 mais" + } + }, + "today": { + "name": "hoje", + "description": "Tanques jogados hoje e estatísticas" + }, + "stats": { + "name": "estatísticas", + "description": "Estatísticas rápidas para um período" + }, + "owned_tanks": { + "name": "tanques-possuídos", + "description": "Mostra todos os tanques possuídos em um nível", + "body": { + "title": "# %s1's possuía nível %s2 tanques\n\n" + } + }, + "permissions": { + "name": "Permissões", + "description": "Verifica se o bot tem as permissões necessárias para funcionar", + "body": { + "title": "# Permissões\n\n%s1", + "view_channels": "Ver canais: necessário para encontrar a mensagem em que o botão atualizar foi usado", + "read_message_history": "Histórico de mensagens lidas: necessário para encontrar a mensagem para atualizar", + "attach_files": "Anexar arquivos: necessário para adicionar imagens a comandos baseados em imagens", + "external_emojis": "Emojis externos: necessário para adicionar bandeiras nacionais e emojis de classe de tanque" + } + }, + "ping": { + "name": "ping", + "description": "Verifique se o bot está vivo.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "informação do jogador", + "description": "Informações básicas sobre um jogador", + "body": { + "title": "%s1's informações", + "nickname": "Apelido", + "battles": "Batalhas", + "winrate": "Taxa de vitória", + "account_id": "ID da conta", + "created": "Criado", + "last_battle": "Última batalha" + } + }, + "rating": { + "name": "classificação", + "description": "Estatísticas de jogador de classificação", + "subcommands": { + "today": { + "name": "hoje", + "description": "Estatísticas de classificação hoje" + }, + "season": { + "name": "temporada", + "description": "Estatísticas de classificação desta temporada" + } + }, + "body": { + "subtitle": { + "today": "Estatísticas de classificação hoje", + "season": "Estatísticas de classificação desta temporada" + } + }, + "errors": { + "no_participation": "# %s1 não jogou classificação\n\nEste jogador não participou do modo de classificação nesta temporada.", + "unrecorded_stats": "# Estatísticas atualmente indisponível\n\nWargaming pode estar no momento, enfrentando tempo de inatividade do servidor. Se houver uma temporada em curso, as estatísticas deverão ser disponibilizadas dentro de 24 horas." + } + } + } + }, + "website": { + "settings": { + "dev_mode": "Modo de desenvolvimento", + "language": "Idioma" + }, + "common": { + "player_search": { + "hint": "Procurar jogadores...", + "no_results": "Sem resultados", + "searching": "Pesquisando..." + }, + "copy_button": { + "copied": "Copiado!" + }, + "tank_search": { + "search_bar_hint": "Procurar tanques...", + "recent": "Visto recentemente", + "select_all": "Selecionar todos", + "count_singular": "1 tanque", + "count_plural": "%s1 tanques", + "sorting_by": "Ordenando por %s1", + "no_tanks_found": { + "body": "Nenhum tanque encontrado.", + "search_again": "Tente pesquisar novamente.", + "clear_filters": "Tente limpar os filtros." + }, + "sort": { + "meta.none": "nenhum", + "survivability.health": "vida", + "survivability.viewRange": "alcance de visão", + "survivability.camouflageStill": "camuflagem quando estacionário", + "survivability.camouflageMoving": "camuflagem em movimento", + "survivability.camouflageShooting": "camuflagem ao disparar", + "survivability.volume": "volume", + "survivability.length": "comprimento", + "fire.dpm": "DPM padrão", + "fire.dpmPremium": "DPM premium", + "fire.reload": "recarga", + "fire.caliber": "calibre", + "fire.standardPenetration": "penetração padrão", + "fire.premiumPenetration": "penetração premium", + "fire.damage": "dano", + "fire.moduleDamage": "dano a módulos", + "fire.shellVelocity": "velocidade do projétil", + "fire.shellRange": "alcance de projétil", + "fire.shellCapacity": "capacidade de projéteis", + "fire.aimTime": "tempo de mira", + "fire.dispersionStill": "dispersão quando estacionário", + "fire.dispersionMoving": "dispersão em movimento", + "fire.gunDepression": "depressão da arma", + "fire.gunElevation": "elevação da arma", + "maneuverability.forwardsSpeed": "velocidade frontal", + "maneuverability.backwardsSpeed": "velocidade de ré", + "maneuverability.power": "potência", + "maneuverability.powerToWeight": "relação peso/potência", + "maneuverability.weight": "peso", + "maneuverability.traverseSpeed": "taxa de rotação" + }, + "sort_dropdown": { + "label": "Ordenar", + "groups": { + "sort": { + "no_sort": "Sem ordenação", + "label": "Ordenar por", + "fire": "Fogo", + "maneuverability": "Manobrabilidade", + "survivability": "Capacidade de sobrevivência" + }, + "direction": { + "label": "Direção", + "ascending": "Ascendente", + "descending": "Descendente" + } + } + } + }, + "warnings": { + "test": "Os tanques em testes estão sujeitos a alterações e não podem representar o produto final." + } + }, + "home": { + "hero": { + "credit": "Por %s1 ❤️" + }, + "hot_tanks": { + "title": "Tanques populares", + "subtitle": "Últimos %s1 dias" + }, + "other_projects": { + "title": "Outros projetos legais" + }, + "help_translate": "Por favor, ajude a traduzir %s1 para %s2!", + "seo_description": "Ferramentas para tudo World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Tanquepedia", + "button": "Encontrar tanques", + "description": "Blitz enciclopédia de tanques", + "seo_description": "Estatísticas, blindagem, e mais para todos os tanques de Wolrd of Tanks Blitz", + "tank_seo_title": "%s1 - Nível %s2 %s3 %s4 | %s5 Tanquepedia | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 blindagem, estatísticas, equipamentos, provisões e mais. %s2 DPM, %s3hp alfa, %s4mm de penetração, %s5km/h velocidade máxima, %s6hp vida.", + "sandbox": { + "poses": { + "hull_down": "Casco para baixo", + "face_hug": "Abraço facial", + "default": "Padrão" + }, + "settings": { + "armor": "Blindagem", + "green_penetration": "Penetração verde", + "hide_model_under_armor": "Ocultar modelo sob blindagem", + "opaque": "Opaco", + "dev_wireframe": "DEV: wireframe", + "environment": "Ambiente", + "show_grid": "Exibir grade" + }, + "screenshot": { + "download": "Download", + "copy": "Copiar" + }, + "model": { + "name": "Modelo" + }, + "dynamic": { + "name": "Blindagem dinâmica", + "custom_shell": { + "type": "Tipo", + "caliber": "Calibre", + "penetration": "Penetração", + "normalization": "Normalização", + "ricochet": "Ricochete", + "explosion_radius": "Raio de Explosão", + "exit": "Sair", + "reset": "Restaurar" + }, + "advanced": "Realce avançado", + "shooter": "Atirador:", + "shot_card": { + "element": { + "primary": "Primário", + "spaced": "Espaçado", + "external": "Externo", + "gap": "Lacuna", + "gun": "Arma", + "track": "Esteiras" + }, + "status": { + "penetration": "%s1HP penetração", + "blocked": "Bloqueado", + "ricochet": "Ricochete", + "splash": "%s1hp respingo" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetração" + } + } + }, + "static": { + "name": "Blindagem estática", + "primary": "Primário", + "spaced": "Espaçada", + "external": "Externa", + "edit": { + "label": "Editar", + "reset": "Redefinir" + } + } + }, + "meta": { + "back": "Voltar", + "compare": "Comparar", + "swap": { + "button": "Trocar", + "title": "Trocar tanques" + }, + "vanilla": "Padrão", + "opentest": "TesteAberto", + "full_name": "Nome completo", + "nation": "Nação", + "class": "Classe", + "tier": "Nível", + "type": "Tipo", + "dev_id": "DEV: ID", + "purchase": "Preço de compra", + "restoration": "Preço de recuperação", + "sale": "Preço de venda", + "research": "EXP de pesquisa" + }, + "tech_tree": { + "title": "Árvore tecnológica", + "battle": "1 batalha", + "battles": "%s1 batalhas", + "route": "Rota %s1 de %s2" + }, + "game_modes": { + "title": "Habilidades do modo de jogo" + }, + "review": { + "title": "Vídeos de análise", + "disclaimer": "BlitzKit não aprova nenhum vídeo de análise.", + "join": "Candidate-se para %s1 um programa YouTuber" + }, + "configuration": { + "title": "Configuração", + "modules": { + "title": "Módulos", + "stock": "Padrão", + "upgrade": "Melhorar" + }, + "equipment": { + "title": "Equipamento", + "clear": "Limpar" + }, + "provisions": { + "title": "Provisões", + "clear": "Limpar" + }, + "consumables": { + "title": "Consumíveis", + "clear": "Limpar", + "info": "Os consumíveis amarelos não afetam as estatísticas." + }, + "skills": { + "title": "Habilidades da tripulação", + "clear": "Limpar", + "maximize": "Maximizar", + "info": "As habilidades amarelas só se aplicam em circunstâncias especiais. Segure Espaço para alternar rapidamente entre o nível 0 e 7." + }, + "miscellaneous": { + "title": "Diversos" + } + }, + "characteristics": { + "title": "Características", + "compare": { + "label": "Comparar com", + "class": "Nível %s1 %s2", + "tier": "Tudo de nível %s1", + "all": "Todos os tanques" + }, + "values": { + "shell_index": "Projétil %s1", + "at_distance": "À %s1m", + "at_distance_slider_label": "Distância", + "dispersion_at_distance": "Dispersão à %s1m", + "gun_flexibility": "Flexibilidade da arma", + "speed": "Velocidade", + "terrain_coefficients": "Coeficientes do terreno", + "raw_terrain_coefficients": "Coeficientes brutos do terreno", + "power_to_weight_ratio": "Taxa de peso para potência", + "hull_traverse_speed": "Taxa de rotação do casco", + "camouflage": "Camuflagem", + "reload": "Recarga", + "crewCount": "Quantidade de tripulantes", + "shellNormalization": "Normalização", + "shellRicochet": "Ricochete", + "dpm": "DPM", + "dpmEffective": "Efetivo aos 60s", + "shells": "Projéteis", + "mostOptimalShellIndex": "Projétil mais ideal", + "shellReloads": "Recarga de projétil", + "shellReload": "Recarregamento de projétil", + "intraClip": "Intra-clipe", + "caliber": "Calibre", + "penetration": "Penetração", + "damage": "Dano", + "clipDamage": "Potencial de clipe", + "moduleDamage": "Dano a módulos", + "explosionRadius": "Raio de Explosão", + "shellVelocity": "Velocidade do projétil", + "aimTime": "Tempo de mira", + "dispersion": "Parado", + "dispersionMoving": "Em movimento", + "dispersionHullTraversing": "Deslocamento de casco", + "dispersionTurretTraversing": "Deslocamento de torre", + "dispersionShooting": "Disparando", + "dispersionGunDamaged": "Arma danificada", + "gunDepression": "Depressão", + "gunElevation": "Elevação", + "gunFrontalDepression": "Depressão Frontal", + "gunFrontalElevation": "Elevação frontal", + "gunRearDepression": "Depressão traseira", + "gunRearElevation": "Elevação traseira", + "azimuthLeft": "Azimute à esquerda", + "azimuthRight": "Azimute à direita", + "speedForwards": "Para frente", + "speedBackwards": "Para trás", + "hardTerrainCoefficient": "Difícil", + "hardTerrainCoefficientRaw": "Difícil", + "mediumTerrainCoefficient": "Médio", + "mediumTerrainCoefficientRaw": "Médio", + "softTerrainCoefficient": "Macio", + "softTerrainCoefficientRaw": "Macio", + "enginePower": "Potência do motor", + "powerToWeightRatioHardTerrain": "Difícil", + "powerToWeightRatioMediumTerrain": "Médio", + "powerToWeightRatioSoftTerrain": "Macio", + "weight": "Peso", + "turretTraverseSpeed": "Taxa de rotação da torreta", + "gunTraverseSpeed": "Taxa de rotação da arma", + "hullTraverseHardTerrain": "Difícil", + "hullTraverseMediumTerrain": "Médio", + "hullTraverseSoftTerrain": "Macio", + "health": "Vida", + "fireChance": "Chance de incêndio", + "viewRange": "Alcance de visão", + "camouflageStill": "Parado", + "camouflageMoving": "Em movimento", + "camouflageShootingStill": "Parado", + "camouflageShootingMoving": "Em movimento", + "camouflageCaughtOnFire": "Preso em chamas", + "width": "Largura", + "height": "Altura", + "length": "Comprimento", + "volume": "Volume", + "shellRange": "Alcance de projétil", + "shellCapacity": "Capacidade de projéteis", + "gunType": "Tipo de arma", + "penetrationAt250m": "Penetração à 250m" + } + }, + "firepower": { + "title": "Poder de fogo" + }, + "crew": { + "title": "Treinamento da tripulação", + "info": "representa os outros papéis que um membro da tripulação pode assumir.", + "count": "Quantidade de tripulantes", + "commander": "Comandante", + "gunner": "Artilheiro", + "driver": "Motorista", + "radioman": "Radialista", + "loader": "Carregador" + }, + "maneuverability": { + "title": "Manobrabilidade" + }, + "survivability": { + "title": "Capacidade de sobrevivência" + } + }, + "compare": { + "name": "Comparar", + "button": "Comparar tanques", + "description": "Comparar estatísticas de tanques", + "seo_description": "Compare estatísticas dos tanques e arsenais no World of Tanks Blitz", + "actions": { + "add": { + "button": "Adicionar", + "title": "Adicionar tanques", + "description": "Selecione tanques para adicionar à comparação" + }, + "clear": "Limpar", + "deltas": { + "none": "Sem deltas", + "percentage": "Porcentagem", + "absolute": "Absoluto" + } + }, + "no_tanks": { + "title": "Nenhum tanque selecionado", + "body": "Pressione o botão Adicionar para começar" + }, + "table": { + "equipment": { + "clear": "Limpar", + "apply_to_all": "Aplicar a todos" + }, + "skills": { + "clear": "Limpar", + "maximize": "Maximizar" + }, + "modules": { + "stock": "Padrão", + "upgrade": "Melhorar" + }, + "provisions": { + "clear": "Limpar" + }, + "consumables": { + "clear": "Limpar" + } + } + }, + "performance": { + "name": "Desempenho", + "button": "Ver tanques", + "description": "Estatísticas de desempenho do tanque", + "seo_description": "Melhores tanques e suas performances no World of Tanks Blitz", + "estimation": { + "body": "Estatísticas estimadas baseadas em %s1 jogadores. Atualizado %s2 atrás.", + "hours": "%s1 horas", + "minutes": "%s1 minutos" + }, + "player_count": { + "label": "Número de jogadores" + }, + "table": { + "tanks": { + "header": "Tanque", + "total": "Total" + }, + "stats": { + "winrate": "Taxa de vitória", + "players": "Jogadores", + "damage": "Dano", + "survival": "Sobrevivência", + "xp": "EXP", + "kills": "Abates", + "spots": "Detecções", + "accuracy": "Precisão", + "shots": "Disparos", + "hits": "Acertos", + "damage_ratio": "Razão de dano", + "damage_taken": "Dano tomado", + "capture_points": "Pontos de captura" + } + } + }, + "gallery": { + "name": "Galeria", + "button": "Procurar todos", + "description": "Avatares do jogador Blitz", + "seo_description": "Todos os avatares do jogador World of Tanks Blitz", + "last_updated": "Última atualização em %s1", + "search": { + "hint": "Procurar avatares...", + "results": "%s1 avatares" + } + }, + "session": { + "name": "Sessão", + "button": "Iniciar rastreamento", + "description": "Estatísticas da temporada ao vivo", + "seo_description": "Registre suas estatísticas em tempo real enquanto você joga World of Tanks Blitz", + "search": { + "lookup": "Procure um jogador para começar", + "hint": "Procurar jogadores...", + "searching": "Pesquisando...", + "no_results": "Nenhum jogador encontrado" + }, + "controls": { + "title": "Rastreando %s1", + "subtitle": "Desde %s1", + "reset": "Restaurar", + "change": "Mudança" + }, + "no_cc": { + "title": "Não é possível acompanhar uma conta de Contribuidor da Comunidade (CC)", + "body": "Contas CC (colaborador da comunidade) não possuem estatísticas públicas de tanques. Estamos no processo de adicionar incorporações para contas CC, fique ligado.", + "alright": "Tudo bem" + }, + "no_battles": { + "title": "Sem Batalhas", + "hint": "Vá em frente e jogue uma partida" + }, + "table": { + "tank": "Tanque", + "total": "Total", + "remove": "Remover" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Ranque de tanques", + "description": "Ranque de tanques Blitz", + "seo_description": "Explore listas de classificação de tanques do melhor para o pior, baseado nas classificações dos jogadores e no desempenho estatístico em World of Tanks Blitz", + "buttons": { + "reset": "Restaurar", + "share": "Compartilhar" + } + }, + "embed": { + "name": "Incorporar", + "button": "Crie agora", + "description": "Incorporações para streaming", + "seo_description": "Crie incorporações para streaming do World of Tanks Blitz com extrema personalização", + "coming_soon": "Mais incorporações vindo em breve.", + "suggest": "Sugira ideias.", + "preview": { + "title": "Pré-visualização de exemplo", + "upload": "Enviar fundo de teste" + }, + "configuration": { + "import": { + "title": "Importar", + "description": "Cole sua URL atual se você já tiver uma para importar suas configurações", + "hint": "Colar URL...", + "apply": "Aplicar", + "idiot_title": "URL inválida", + "idiot_description": "Tente colar a URL que BlitzKit gerou anteriormente, seu ganso idiota!", + "idiot_button": "Okay, foi mal" + }, + "export": { + "title": "Exportar", + "description": "Copiar todas as propriedades para seu software de streaming", + "generate": { + "button": "Gerar URL", + "title": "Gerar URL", + "subtitle": "Procure uma conta para acompanhar", + "cancel": "Cancelar", + "success": { + "title": "Copiar URL", + "info": "Gerou a URL para %s1. A incorporação começará a rastrear assim que a URL for colada. Redefina a qualquer momento clicando com o botão direito no PC ou pressionando longamente no celular e, em seguida, selecionando \"Redefinir\".", + "back": "Voltar", + "copy": "Copiar URL" + } + }, + "css": "Copiar CSS", + "width": "Copiar Largura", + "height": "Copiar Altura", + "framerate": "Copiar Framerate" + }, + "customize": { + "title": "Personalizar", + "reset": "Restaurar", + "description": "Sinos e assobios de sua incorporação" + } + }, + "types": { + "breakdown": { + "name": "Divisão", + "options": { + "width": "Largura", + "height": "Altura", + "showTotal": "Exibir total", + "listGap": "Listar lacuna", + "listMaxTanks": "Máximo de tanques", + "cardRadius": "Raio do cartão", + "cardHeaderBackgroundColor": "Cor de fundo do cabeçalho do cartão", + "cardHeaderPadding": "Preenchimento do cabeçalho do cartão\n", + "cardTitle": "Título do cartão", + "cardTitleTypeColor": "Tipo de cor do título do cartão", + "cardTitleClassIcon": "Ícone de classe do título do cartão", + "cardBodyBackgroundColor": "Cor de fundo do corpo do cartão", + "cardBodyPadding": "Preenchimento do corpo do cartão", + "columnGap": "Lacuna da coluna", + "columnValue": "Valor da coluna", + "columnLabel": "Rótulo da coluna", + "column1": "Coluna 1", + "column2": "Coluna 2", + "column3": "Coluna 3", + "column4": "Coluna 4", + "column1CustomLabel": "Rótulo da coluna 1", + "column2CustomLabel": "Rótulo da coluna 2", + "column3CustomLabel": "Rótulo da coluna 3" + } + } + } + }, + "charts": { + "name": "Gráfico", + "button": "Nerdice para fora", + "description": "Muitos gráficos", + "seo_description": "Tabelas e gráficos sobre qualquer coisa e tudo sobre tanques, jogadores e eventos em World of Tanks Blitz" + }, + "rating": { + "name": "Classificação", + "button": "Ver tabela de classificação", + "description": "Classificação completa da tabela de classificação", + "seo_description": "Classificação completa ao vivo e arquivada para World of Tanks Blitz" + }, + "discord": { + "name": "Bot do discord", + "button": "Instale agora", + "description": "Estatísticas diretamente no Discord", + "seo_description": "Obtenha estatísticas diretamente no Discord com o bot do Discord de BlitzKit" + }, + "more": { + "name": "Mais em breve", + "button": "Juntar-se ao Discord", + "description": "O que estamos fazendo", + "seo_description": "O que estamos fazendo com BlitzKit" + }, + "players": { + "name": "Jogadores", + "button": "Buscar jogador", + "description": "Estatísticas periódicas de jogadores", + "seo_description": "Estatísticas periódicas de jogadores em World of Tanks Blitz" + }, + "home": { + "name": "Página Principal", + "button": "Introdução", + "description": "Comece com BlitzKit", + "seo_description": "Comece com BlitzKit" + }, + "blitz_analysis": { + "name": "AnaliseBlitz[]", + "button": "Analise agora", + "description": "Estatísticas médias históricas ricas", + "seo_description": "Estatísticas médias históricas ricas no World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Assistente Blitz", + "button": "Descubra hoje", + "description": "Estatísticas da temporada dentro do jogo", + "seo_description": "Estatísticas da sessão no jogo World of Tanks Blitz" + } + }, + "footer": { + "motto": "Tudo World of Tanks Blitz", + "copyright": "Não afiliado à Wargaming ou qualquer serviço", + "about": { + "title": "Sobre", + "credits": "Créditos", + "discord": "Discord", + "github": "GitHub", + "patches": "Atualizações" + }, + "legal": { + "title": "Legal", + "privacy": "Política de privacidade", + "terms": "Termos de serviço" + } + } + } +} diff --git a/packages/i18n/strings/ru.json b/packages/i18n/strings/ru.json new file mode 100644 index 000000000..0b9f1ec59 --- /dev/null +++ b/packages/i18n/strings/ru.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "тест", + "long": "Это длинная тестовая строка. Вы НИКОГДА не должны видеть её в пользовательском интерфейсе. Мой любимый фрукт - %s1, потому что его цвет %s2. Он мне так нравится, что я произнесу его имя 3 раза! %s1! %s1! %s1! И ещё раз, это %s2, а не %s3.", + "fox": "Это длинная тестовая строка. Вы НИКОГДА не должны видеть её в пользовательском интерфейсе. Мой любимый фрукт - %s1, потому что его цвет %s2. Он мне так нравится, что я произнесу его имя 3 раза! %s1! %s1! %s1! И ещё раз, это %s2, а не %s3." + }, + "common": { + "units": { + "m": "%s1м", + "mm": "%s1мм", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Барабан", + "auto_reloader": "Барабан с дозарядкой", + "regular": "Цикличный" + }, + "shells": { + "ap": "ББ", + "apcr": "БП", + "heat": "КС", + "he": "ОФ" + }, + "player_count_period": { + "total": "Всего", + "d_120": "За последние 120 дней", + "d_90": "За последние 90 дней", + "d_60": "За последние 60 дней", + "d_30": "За последний месяц", + "d_7": "За неделю", + "d_1": "Вчера" + }, + "branches": { + "local": "dev", + "dev": "бета", + "opentest": "открытый тест", + "preview": "предварительный просмотр" + }, + "locales": { + "en": "Английский", + "es": "Испанский", + "pt": "Португальский", + "ru": "Русский", + "uk": "Украинский", + "zh": "Китайский", + "ja": "Японский", + "fr": "Французский", + "pl": "Польский" + }, + "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Не найден «%s1». Попробуйте выбрать клан из результатов поиска.", + "player_not_found": "# Не удалось найти пользователя\nНе удалось найти пользователя «%s1». Попробуйте выбрать пользователя из результатов поиска, введя правильное имя пользователя или используя команду «/link».", + "player_not_linked": "# Используйте «/link», чтобы начать\nЯ не знаю, кто ты, но давай познакомимся сегодня!", + "tank_name_not_found": "# Танк не найден\nНе удалось найти танк по названию «%s1».", + "tank_id_not_found": "# Танк не найден\nНе удалось найти танк по ID «%s1». Попробуйте ввести название танка вместо числа.", + "no_tank_stats": "# Статистика танка недоступна\nУ этого игрока нет доступной статистики. Возможно, это не тот игрок, которого вы ищете.", + "get_help": "Получить помощь", + "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": { + "name": "WN8", + "types": { + "very_bad": "Очень плохо", + "bad": "Плохо", + "below_average": "Ниже среднего", + "average": "Средне", + "above_average": "Выше среднего", + "good": "Хорошо", + "very_good": "Очень хорошо", + "great": "Отлично", + "unicum": "Уникально", + "super_unicum": "Крайне уникально" + } + }, + "subcommands": { + "today": { + "name": "cегодня", + "description": "Cегодня" + }, + "30": { + "name": "30", + "description": "30 дней" + }, + "60": { + "name": "60", + "description": "60 дней" + }, + "90": { + "name": "90", + "description": "90 дней" + }, + "career": { + "name": "карьера", + "description": "Карьера" + }, + "custom": { + "name": "пользовательский", + "description": "Собственный диапазон", + "options": { + "start": { + "name": "начать", + "description": "Начать через N дней с сегодняшнего дня" + }, + "end": { + "name": "конец", + "description": "Закончить через N дней с сегодняшнего дня" + } + } + } + }, + "options": { + "clan": { + "name": "клан", + "description": "Название или тег клана" + }, + "tank": { + "name": "танк", + "description": "Танк для получения статистики для" + }, + "username": { + "name": "ник", + "description": "Ник игрока в Blitz" + }, + "filters": { + "nation": { + "name": "нация", + "description": "Нация" + }, + "class": { + "name": "класс", + "description": "Класс" + }, + "type": { + "name": "тип", + "description": "Тип" + } + }, + "region": { + "name": "регион", + "description": "Регион" + }, + "tier": { + "name": "уровень", + "description": "Уровень", + "choices": { + "tier": "Уровень %s - %s" + } + } + }, + "periods": { + "career": "Карьера", + "today": "Cегодня", + "days": "%s дней", + "custom": "%s - %s дней" + }, + "filters": { + "none": "Без фильтров", + "tier": "Уровень %s1" + }, + "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": { + "name": "аукцион", + "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": { + "name": "повтор", + "description": "Показать результаты повтора" + }, + "research": { + "name": "исследование", + "description": "Список ресурсов для исследования ветки", + "options": { + "target_tank": { + "name": "желаемый танк", + "description": "Желаемый танк ветки исследования" + }, + "starting_tank": { + "name": "стартовый-танк", + "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": { + "name": "о-боте", + "description": "Вся необходимая информация о боте", + "subcommands": { + "commands": { + "name": "команды", + "description": "Список всех команд и их описаний", + "body": "# Команды\n\nBlitzKit предлагает следующие команды:\n\n" + }, + "embeds": { + "name": "встраиваемые-элементы", + "description": "Как работают встраиваемые элементы" + }, + "introduction": { + "name": "введение", + "description": "О боте" + }, + "invite": { + "name": "пригласить", + "description": "Как пригласить бота" + }, + "timezones": { + "name": "часовой-пояс", + "description": "Как BlitzKit определяет и использует ваш часовой пояс" + }, + "credits": { + "name": "авторы", + "description": "Люди, которые помогли BlitzKit" + } + } + }, + "breakdown": { + "name": "распределение", + "description": "Статистика игр за период с распределением по танкам", + "body": { + "total": "Всего", + "battles": "Боёв", + "wn8": "WN8", + "winrate": "Коэффициент побед", + "damage": "Урон", + "unknown_tank": "Неизвестный танк %s" + } + }, + "debug": { + "name": "отладка", + "description": "Сведения для отладки", + "body": { + "title": "# Отладка", + "version": "Версия", + "shard": { + "label": "Фрагмент", + "default": "по умолчанию" + }, + "uptime": "Время работы" + } + }, + "evolution": { + "name": "изменения", + "description": "Изменения статистики", + "groups": { + "player": { + "name": "игрок", + "description": "Статистика игрока" + }, + "tank": { + "name": "танк", + "description": "Статистика танков" + } + } + }, + "inactive": { + "name": "неактивно", + "description": "Список всех неактивных игроков в клане выше порога", + "options": { + "threshold": { + "name": "порог", + "description": "Количество дней не активности (по умолчанию: %s дней)" + } + }, + "body": { + "subtitle": "Неактивен %s+ дней", + "listing": "%s дней" + } + }, + "full_stats": { + "name": "полная-статистика", + "description": "Полная игровая статистика", + "body": { + "cards": { + "games": { + "title": "Игры", + "winrate": "Коэффициент побед", + "battles": "Боёв", + "wins_losses": "Побед / поражений" + }, + "efficiency": { + "title": "Эффективность", + "wn8": "WN8", + "survival": "Выжил", + "accuracy": "Точность стрельбы" + }, + "lethality": { + "title": "Смертность", + "average_damage": "Средний урон", + "damage_per_tier": "Урон по уровню", + "damage_ratio": "Коэффициент урона", + "average_kills": "Среднее число уничтоженных" + }, + "miscellaneous": { + "title": "Прочее", + "average_tier": "Средний уровень", + "average_spots": "Среднее число попаданий", + "average_xp": "Средний опыт", + "kills_to_death_ratio": "Коэффициент убийств и смертей" + } + } + } + }, + "link": { + "name": "ссылка", + "description": "Привяжите учётную запись Blitz и Discord", + "body": { + "accounts_linked": "Аккаунты привязаны", + "no_role_permissions": { + "title": "%s1 не удалось проверить", + "description": "У меня нет разрешения на управление ролями." + } + }, + "embed": { + "title": "Неверный аккаунт?", + "description": "Используйте команду снова и выберите имя пользователя из результатов поиска." + } + }, + "search_clans": { + "name": "поиск-кланов", + "description": "Поиск кланов", + "options": { + "limit": { + "name": "лимит", + "description": "Максимальное количество результатов клана (по умолчанию: %s)" + } + }, + "body": { + "title": "Поиск клана %s в регионе %s", + "no_results": "Кланов не найдено" + } + }, + "search_players": { + "name": "поиск-игроков", + "description": "Поиск игроков", + "options": { + "limit": { + "name": "лимит", + "description": "Максимальное число результатов игрока (по умолчанию: %s)" + } + }, + "body": { + "title": "Поиск игрока %s в регионе %s", + "no_results": "Игроков не найдено" + } + }, + "rating_leaderboard": { + "name": "таблица-лидеров", + "description": "Статистика рейтингов боёв", + "subcommands": { + "neighbors": { + "name": "ближайшие", + "description": "Ближайшие игроки в таблице лидеров" + }, + "league": { + "name": "лига", + "description": "Топ игроков лиги" + } + }, + "options": { + "limit": { + "name": "лимит", + "description": "Количество отображаемых ближайших игроков (по умолчанию: %s)" + }, + "season": { + "name": "сезон", + "description": "Номер сезона (по умолчанию: текущий)", + "choices": { + "current": "%s (текущая)" + } + }, + "league": { + "name": "лига", + "description": "Отображаемая лига" + } + }, + "body": { + "top_players": "Топ %s игроков", + "no_participation": "# %s не играл в рейтинг в сезоне %s\n\nЭтот игрок не участвовал в этом сезоне или не получил прошлую калибровку.", + "no_ongoing_season": "# Нет текущего сезона\n\nВ данный момент нет рейтингового сезона.", + "deleted_player": "Удалено %s", + "neighbors": "Рейтинг ближайших игроков", + "more": "+ ещё %s" + } + }, + "today": { + "name": "cегодня", + "description": "Танки, на которых сыграли сегодня и статистика" + }, + "stats": { + "name": "статистика", + "description": "Быстрая статистика за период" + }, + "owned_tanks": { + "name": "cобственные-танки", + "description": "Показывает все принадлежащие танки на уровне", + "body": { + "title": "# %s принадлежал уровень %s танков\n\n" + } + }, + "permissions": { + "name": "разрешения", + "description": "Проверяет, есть ли у бота необходимые разрешения для работы", + "body": { + "title": "# Разрешения\n\n%s", + "view_channels": "Просмотр каналов: необходимо найти сообщение, на котором была использована кнопка обновления", + "read_message_history": "Просмотр истории сообщений: необходимо найти сообщение для обновления", + "attach_files": "Прикрепить файлы: необходимо для добавления изображений в команды на основе изображения", + "external_emojis": "Внешние эмоции: необходимы для добавления национальных флагов и эмодзи класса танков" + } + }, + "ping": { + "name": "пинг", + "description": "Проверьте, что бот жив.", + "body": { + "pong": "Понг 🏓", + "pong_with_time": "Понг 🏓 - %sмс" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Пинг BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Пинг World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "информация-об-игроке", + "description": "Основная информация о игроке", + "body": { + "title": "Информация о %s", + "nickname": "Никнейм", + "battles": "Боёв", + "winrate": "Коэффициент побед", + "account_id": "ID учётной записи", + "created": "Создано", + "last_battle": "Последний бой" + } + }, + "rating": { + "name": "рейтинг", + "description": "Статистика рейтинга игрока", + "subcommands": { + "today": { + "name": "cегодня", + "description": "Статистика рейтинга за сегодня" + }, + "season": { + "name": "сезон", + "description": "Статистика рейтинга в этом сезоне" + } + }, + "body": { + "subtitle": { + "today": "Статистика рейтинга за сегодня", + "season": "Статистика рейтинга в этом сезоне" + } + }, + "errors": { + "no_participation": "# %s не получил рейтинга\n\nЭтот игрок в этом сезоне не участвовал в рейтинговом режиме.", + "unrecorded_stats": "# Статистика недоступна в настоящее время\n\nВ настоящее время Wargaming, возможно, сталкивается с простоями сервера. Если сезон продолжается, статистика должна быть доступна в течение 24 часов." + } + } + } + }, + "website": { + "settings": { + "dev_mode": "Режим разработчика", + "language": "Язык" + }, + "common": { + "player_search": { + "hint": "Поиск игроков...", + "no_results": "Ничего не найденo", + "searching": "Поиск..." + }, + "copy_button": { + "copied": "Скопировано!" + }, + "tank_search": { + "search_bar_hint": "Поиск танков...", + "recent": "Недавние", + "select_all": "Выбрать всё", + "count_singular": "1 танк", + "count_plural": "%s1 танк", + "sorting_by": "Сортировать по %s1", + "no_tanks_found": { + "body": "Танков не найдено.", + "search_again": "Попробуйте повторить поиск.", + "clear_filters": "Попробуйте очистить фильтры." + }, + "sort": { + "meta.none": "Никак", + "survivability.health": "прочность", + "survivability.viewRange": "обзор", + "survivability.camouflageStill": "маскировка когда неподвижен", + "survivability.camouflageMoving": "маскировка в движении", + "survivability.camouflageShooting": "маскировка при выстреле", + "survivability.volume": "объём", + "survivability.length": "длина", + "fire.dpm": "базовый ДПМ", + "fire.dpmPremium": "улучшенный ДПМ", + "fire.reload": "перезарядка", + "fire.caliber": "калибр", + "fire.standardPenetration": "базовое пробитие", + "fire.premiumPenetration": "улучшенное пробитие ", + "fire.damage": "урон", + "fire.moduleDamage": "урон по модулям", + "fire.shellVelocity": "скорость снаряда", + "fire.shellRange": "дальность снаряда", + "fire.shellCapacity": "боезапас", + "fire.aimTime": "время сведения", + "fire.dispersionStill": "разброс когда неподвижен", + "fire.dispersionMoving": "разброс в движении", + "fire.gunDepression": "углы вниз", + "fire.gunElevation": "углы вверх", + "maneuverability.forwardsSpeed": "скорость вперед", + "maneuverability.backwardsSpeed": "скорость назад", + "maneuverability.power": "мощность", + "maneuverability.powerToWeight": "тяговооруженность", + "maneuverability.weight": "вес", + "maneuverability.traverseSpeed": "скорость поворота" + }, + "sort_dropdown": { + "label": "Сортировка", + "groups": { + "sort": { + "no_sort": "Без сортировки", + "label": "Сортировка", + "fire": "Стрельба", + "maneuverability": "Манёвренность", + "survivability": "Выживаемость" + }, + "direction": { + "label": "Направление", + "ascending": "По возрастанию", + "descending": "По убыванию" + } + } + } + }, + "warnings": { + "test": "Танки на тестировании могут быть изменены и могут сильно отличатся при появлении в игре." + } + }, + "home": { + "hero": { + "credit": "От %s1 ❤️" + }, + "hot_tanks": { + "title": "Популярные танки", + "subtitle": "За последнее %s1 дней" + }, + "other_projects": { + "title": "Другие классные проекты" + }, + "help_translate": "Пожалуйста, помогите перевести %s1 на %s2!", + "seo_description": "Инструменты для World of Tanks Blitz" + }, + "tools": { + "tankopedia": { + "name": "Танкопедия", + "button": "Поиск танков", + "description": "Танковая энциклопедия Blitz", + "seo_description": "Статистика, броня и многое другое по всем танкам World of Tanks Blitz", + "tank_seo_title": "%s1 - %s4 %s3 %s2 уровня | Танкопедия %s5 | World of Tanks Blitz (WoTB)", + "tank_seo_description": "%s1 броня, статистика, оборудование, снабжение и многое другое. %s2 УВМ, %s3hp альфа, %s4mm пробитие, %s5км/ч лучшая скорость, %s6hp здоровье.", + "sandbox": { + "poses": { + "hull_down": "За холмом", + "face_hug": "В упор", + "default": "По умолчанию" + }, + "settings": { + "armor": "Броня", + "green_penetration": "Зелёное пробитие", + "hide_model_under_armor": "Скрыть модель под броней", + "opaque": "Прозрачность", + "dev_wireframe": "DEV: wireframe", + "environment": "Окружение", + "show_grid": "Показать сетку" + }, + "screenshot": { + "download": "Загрузить", + "copy": "Скопировать" + }, + "model": { + "name": "Модель" + }, + "dynamic": { + "name": "Динамическая броня", + "custom_shell": { + "type": "Тип", + "caliber": "Калибр", + "penetration": "Пробитие", + "normalization": "Нормализация", + "ricochet": "Рикошет", + "explosion_radius": "Разлет осколков", + "exit": "Выход", + "reset": "Сброс" + }, + "advanced": "Продвинутая подсветка", + "shooter": "Стреляет:", + "shot_card": { + "element": { + "primary": "Броня", + "spaced": "Экран", + "external": "Вн. модуль", + "gap": "Зазор", + "gun": "Орудие", + "track": "Гусеницы" + }, + "status": { + "penetration": "Пробитие на %s1оп", + "blocked": "Не пробил", + "ricochet": "Рикошет", + "splash": "Урон осколками на %s1оп" + }, + "stats": { + "thickness_and_angle": "%s1мм %s2°", + "nominal": "%s1мм номинала", + "ricochet_loss": "%s1% пробития" + } + } + }, + "static": { + "name": "Статичная броня", + "primary": "Броня", + "spaced": "Экраны", + "external": "Вн. модули", + "edit": { + "label": "Изменить", + "reset": "Сброс" + } + } + }, + "meta": { + "back": "Назад", + "compare": "Сравнение", + "swap": { + "button": "Поменять", + "title": "Поменять танк" + }, + "vanilla": "Без модов", + "opentest": "Открытый тест", + "full_name": "Полное название", + "nation": "Нация", + "class": "Класс", + "tier": "Уровень", + "type": "Тип", + "dev_id": "DEV: ID", + "purchase": "Стоимость покупки", + "restoration": "Цена восстановления", + "sale": "Цена продажи", + "research": "Необходимо опыта" + }, + "tech_tree": { + "title": "Дерево танков", + "battle": "1 бой", + "battles": "%s1 боёв", + "route": "Ветка %s1 из %s2" + }, + "game_modes": { + "title": "Способности в игровых режимах" + }, + "review": { + "title": "Видеообзоры", + "disclaimer": "BlitzKit не несёт ответственности за видеообзоры.", + "join": "Предложить ютубера для программы %s1" + }, + "configuration": { + "title": "Конфигурация", + "modules": { + "title": "Модули", + "stock": "Сток", + "upgrade": "Топ" + }, + "equipment": { + "title": "Оборудование", + "clear": "Очистить" + }, + "provisions": { + "title": "Амуниция", + "clear": "Очистить" + }, + "consumables": { + "title": "Снаряжение", + "clear": "Очистить", + "info": "Жёлтое снаряжение не влияет на характеристики." + }, + "skills": { + "title": "Навыки экипажа", + "clear": "Очистить", + "maximize": "Макс.", + "info": "Желтые навыки применяются только при особых условиях. Удерживайте пробел, чтобы быстро переключаться между уровнями 0 и 7." + }, + "miscellaneous": { + "title": "Прочее" + } + }, + "characteristics": { + "title": "Характеристики", + "compare": { + "label": "Сравнивать с", + "class": "%s2 %s1 уровня", + "tier": "Все %s1 уровня", + "all": "Все танки" + }, + "values": { + "shell_index": "Снаряд %s1", + "at_distance": "На %s1м", + "at_distance_slider_label": "Дистанция", + "dispersion_at_distance": "Разброс на %s1м", + "gun_flexibility": "Углы наводки", + "speed": "Скорость", + "terrain_coefficients": "Сопротивление грунтам", + "raw_terrain_coefficients": "Сырое сопротивление грунтам", + "power_to_weight_ratio": "Тяговооружённость", + "hull_traverse_speed": "Скорость поворота корпуса", + "camouflage": "Маскировка", + "reload": "Перезарядка", + "crewCount": "Количество экипажа", + "shellNormalization": "Нормализация", + "shellRicochet": "Рикошет", + "dpm": "ДПМ", + "dpmEffective": "Эффективность за 60с", + "shells": "Снарядов в барабане", + "mostOptimalShellIndex": "Самый эффективный снаряд", + "shellReloads": "Перезарядка снарядов", + "shellReload": "Перезарядка", + "intraClip": "Между снарядами", + "caliber": "Калибр", + "penetration": "Пробитие", + "damage": "Урон", + "clipDamage": "Урон с барабана", + "moduleDamage": "Урон по модулям", + "explosionRadius": "Разлет осколков", + "shellVelocity": "Скорость снаряда", + "aimTime": "Базовое время сведения", + "dispersion": "Когда неподвижен", + "dispersionMoving": "В движении", + "dispersionHullTraversing": "При повороте шасси", + "dispersionTurretTraversing": "При повороте башни", + "dispersionShooting": "После выстрела", + "dispersionGunDamaged": "Повреждено орудие", + "gunDepression": "Вниз", + "gunElevation": "Вверх", + "gunFrontalDepression": "Вниз спереди", + "gunFrontalElevation": "Вверх спереди", + "gunRearDepression": "Вниз сзади", + "gunRearElevation": "Вверх сзади", + "azimuthLeft": "Влево", + "azimuthRight": "Вправо", + "speedForwards": "Вперёд", + "speedBackwards": "Назад", + "hardTerrainCoefficient": "На жёстком грунте", + "hardTerrainCoefficientRaw": "На жёстком грунте", + "mediumTerrainCoefficient": "На среднем грунте", + "mediumTerrainCoefficientRaw": "На среднем грунте", + "softTerrainCoefficient": "На мягком грунте", + "softTerrainCoefficientRaw": "На мягком грунте", + "enginePower": "Мощность двигателя", + "powerToWeightRatioHardTerrain": "На жёстком грунте", + "powerToWeightRatioMediumTerrain": "На среднем грунте", + "powerToWeightRatioSoftTerrain": "На мягком грунте", + "weight": "Вес", + "turretTraverseSpeed": "Скорость поворота башни", + "gunTraverseSpeed": "Скорость поворота орудия", + "hullTraverseHardTerrain": "На жёстком грунте", + "hullTraverseMediumTerrain": "На среднем грунте", + "hullTraverseSoftTerrain": "На мягком грунте", + "health": "Прочность", + "fireChance": "Вероятность возгорания", + "viewRange": "Радиус обзора", + "camouflageStill": "Когда неподвижен", + "camouflageMoving": "В движении", + "camouflageShootingStill": "После выстрела, если неподвижен", + "camouflageShootingMoving": "После выстрела в движении", + "camouflageCaughtOnFire": "При пожаре", + "width": "Ширина", + "height": "Высота", + "length": "Длина", + "volume": "Объём", + "shellRange": "Дальность снаряда", + "shellCapacity": "Боезапас", + "gunType": "Тип орудия", + "penetrationAt250m": "Пробитие на 250м" + } + }, + "firepower": { + "title": "Стрельба" + }, + "crew": { + "title": "Мастерство экипажа", + "info": "обозначает другие роли, которые может взять член экипажа.", + "count": "Количество экипажа", + "commander": "Командир", + "gunner": "Наводчик", + "driver": "Механик-водитель", + "radioman": "Радист", + "loader": "Заряжающий" + }, + "maneuverability": { + "title": "Манёвренность" + }, + "survivability": { + "title": "Выживаемость" + } + }, + "compare": { + "name": "Сравнение", + "button": "Сравнить", + "description": "Сравнить статистику танков", + "seo_description": "Сравните статистику танков и снаряжения в World of Tanks Blitz", + "actions": { + "add": { + "button": "Добавить", + "title": "Добавить танки", + "description": "Выберите танки для добавления к сравнению" + }, + "clear": "Очистить", + "deltas": { + "none": "Без дельты", + "percentage": "Проценты", + "absolute": "Абсолютные" + } + }, + "no_tanks": { + "title": "Нет выбранных танков", + "body": "Нажмите кнопку Добавить, чтобы начать" + }, + "table": { + "equipment": { + "clear": "Очистить", + "apply_to_all": "Применить ко всем" + }, + "skills": { + "clear": "Очистить", + "maximize": "Максимум" + }, + "modules": { + "stock": "Сток", + "upgrade": "Топ" + }, + "provisions": { + "clear": "Очистить" + }, + "consumables": { + "clear": "Очистить" + } + } + }, + "performance": { + "name": "Эффективность", + "button": "Изучить", + "description": "Статистика эффективности танков", + "seo_description": "Лучшие танки и их эффективность в World of Tanks Blitz", + "estimation": { + "body": "Примерная статистика на основе %s1 игроков. Обновлено %s2 назад.", + "hours": "%s1 часов", + "minutes": "%s1 минут" + }, + "player_count": { + "label": "Количество игроков" + }, + "table": { + "tanks": { + "header": "Танк", + "total": "Всего" + }, + "stats": { + "winrate": "Коэф. побед", + "players": "Игроки", + "damage": "Урон", + "survival": "Выжил", + "xp": "Опыт", + "kills": "Уничтожил", + "spots": "Попаданий", + "accuracy": "Точность", + "shots": "Выстрелов", + "hits": "Уничтожен", + "damage_ratio": "Коэф. урона", + "damage_taken": "Получено урона", + "capture_points": "Ограничительные точки" + } + } + }, + "gallery": { + "name": "Галерея", + "button": "Просмотреть", + "description": "Аватары игрока Blitz", + "seo_description": "Все аватары игрока World of Tanks Blitz", + "last_updated": "Последнее обновление %s1", + "search": { + "hint": "Поиск аватаров...", + "results": "%s1 аватар" + } + }, + "session": { + "name": "Сессия", + "button": "Найти", + "description": "Статистика сеанса игры", + "seo_description": "Отслеживайте свою статистику в реальном времени во время игры World of Tanks Blitz", + "search": { + "lookup": "Искать игрока, чтобы начать", + "hint": "Поиск игроков...", + "searching": "Поиск...", + "no_results": "Игроков не найдено" + }, + "controls": { + "title": "Отслеживание %s1", + "subtitle": "Начиная с %s1", + "reset": "Сброс", + "change": "Изменить" + }, + "no_cc": { + "title": "Вы пытаетесь отследить аккаунт контребьютера", + "body": "Учетные записи контрибьютеров не имеют публичной статистики танков. Мы находимся в процессе добавления вставки для аккаунтов контрибьютеров, оставайтесь с нами.", + "alright": "Принять" + }, + "no_battles": { + "title": "Нет боёв", + "hint": "Идите вперед и поиграйте в игру" + }, + "table": { + "tank": "Танк", + "total": "Всего", + "remove": "Удалить" + } + }, + "tier_list": { + "name": "Тир-лист", + "button": "Ранжировать", + "description": "Ранжирование танков Blitz", + "seo_description": "Изучите списки уровней, в которых танки ранжируются от лучших к худшим на основе рейтингов игроков и статистических показателей в World of Tanks Blitz", + "buttons": { + "reset": "Сброс", + "share": "Поделиться" + } + }, + "embed": { + "name": "Встройки", + "button": "Создать", + "description": "Встраиваемые элементы для стримов", + "seo_description": "Создать вставки для стримов по World of Tanks Blitz с невероятной кастомизации", + "coming_soon": "Скоро появятся новые встраиваемые элементы.", + "suggest": "Предложить идеи.", + "preview": { + "title": "Предпросмотр", + "upload": "Загрузить фон для теста" + }, + "configuration": { + "import": { + "title": "Импорт", + "description": "Вставьте свой текущий URL, если он у вас уже есть, чтобы импортировать свои настройки", + "hint": "Вставить URL...", + "apply": "Применить", + "idiot_title": "Недопустимый URL", + "idiot_description": "Попробуй вставить URL, который ранее сгенерировал BlitzKit, глупый ты гусь!", + "idiot_button": "Ладно, виноват" + }, + "export": { + "title": "Экспорт", + "description": "Скопируйте все свойства для вашего приложения для стриминга", + "generate": { + "button": "Сгенерировать URL", + "title": "Сгенерировать URL", + "subtitle": "Поиск учетной записи для отслеживания", + "cancel": "Отмена", + "success": { + "title": "Скопировать URL", + "info": "Сгенерирован URL для %s1. Встройка начнет отслеживание, как только URL будет вставлен. Можно сбросить, щелкнув правой кнопкой мыши на ПК или долгим нажатием на мобильном телефоне и выбором «Сброс».", + "back": "Назад", + "copy": "Скопировать URL" + } + }, + "css": "Скопировать CSS", + "width": "Скопировать ширину", + "height": "Скопировать высоту", + "framerate": "Скопировать частоту кадров" + }, + "customize": { + "title": "Настроить", + "reset": "Сброс", + "description": "Навороты для вашей встройки" + } + }, + "types": { + "breakdown": { + "name": "Разбивка", + "options": { + "width": "Ширина", + "height": "Высота", + "showTotal": "Показать «Общая»", + "listGap": "Разрыв", + "listMaxTanks": "Число танков", + "cardRadius": "Радиус карточки", + "cardHeaderBackgroundColor": "Цвет фона заголовка карточки", + "cardHeaderPadding": "Отступ в заголовке", + "cardTitle": "Заголовок карточки", + "cardTitleTypeColor": "Цвет по типу в заголовке", + "cardTitleClassIcon": "Иконка класса в заголовке", + "cardBodyBackgroundColor": "Цвет фона карточки", + "cardBodyPadding": "Отступ в карточке", + "columnGap": "Разрыв столбцов", + "columnValue": "Значение столбца", + "columnLabel": "Название столбца", + "column1": "Столбец 1", + "column2": "Столбец 2", + "column3": "Столбец 3", + "column4": "Столбец 4", + "column1CustomLabel": "Название столбца 1", + "column2CustomLabel": "Название столбца 2", + "column3CustomLabel": "Название столбца 3" + } + } + } + }, + "charts": { + "name": "Таблицы", + "button": "Ботаник вне игры", + "description": "Множество графиков", + "seo_description": "Таблицы и графики обо всем, что касается танков, игроков и событий в World of Tanks Blitz" + }, + "rating": { + "name": "Рейтинг", + "button": "Посмотреть таблицу лидеров", + "description": "Полный таблица лидеров", + "seo_description": "Полная рейтинговая таблица лидеров World of Tanks Blitz в реальном времени и в архиве" + }, + "discord": { + "name": "Discord-бот", + "button": "Установить", + "description": "Статистика в Discord", + "seo_description": "Получите статистику прямо в Discord с помощью бота Discord BlitzKit" + }, + "more": { + "name": "Дальше больше", + "button": "Присоединяйся", + "description": "Что мы готовим", + "seo_description": "Что мы готовим для BlitzKit" + }, + "players": { + "name": "Игроки", + "button": "Поиск игрока", + "description": "Периодическая статистика игроков", + "seo_description": "Периодическая статистика игроков в World of Tanks Blitz" + }, + "home": { + "name": "Главная", + "button": "Начать", + "description": "Начните с BlitzKit", + "seo_description": "Начните с BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Анализируй", + "description": "Богатая статистика", + "seo_description": "Богатые статистика по World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Открой для себя", + "description": "Статистика сеанса игры", + "seo_description": "Статистика сеанса игры в World of Tanks Blitz" + } + }, + "footer": { + "motto": "Всё о World of Tanks Blitz", + "copyright": "Не связан с Wargaming или иным сервисом", + "about": { + "title": "О сайте", + "credits": "Авторы", + "discord": "Discord", + "github": "GitHub", + "patches": "Патчи" + }, + "legal": { + "title": "Правовая информация", + "privacy": "Политика конфиденциальности", + "terms": "Условия использования" + } + } + } +} diff --git a/packages/i18n/strings/uk.json b/packages/i18n/strings/uk.json new file mode 100644 index 000000000..360315c33 --- /dev/null +++ b/packages/i18n/strings/uk.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "тест", + "long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.", + "fox": "The quick brown fox jumps over the lazy dog." + }, + "common": { + "units": { + "m": "%s1м", + "mm": "%s1мм", + "deg": "%s1°", + "hp": "%s1hp", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "Магазинна система заряджання", + "auto_reloader": "Гармата з системою дозаряджання магазину", + "regular": "Циклічна" + }, + "shells": { + "ap": "ББ", + "apcr": "БП", + "heat": "КС", + "he": "ОФ" + }, + "player_count_period": { + "total": "Загалом", + "d_120": "Останні 120 днів", + "d_90": "Останні 90 днів", + "d_60": "Останні 60 днів", + "d_30": "Минулого місяця", + "d_7": "Минулий тиждень", + "d_1": "Вчора" + }, + "branches": { + "local": "розробник", + "dev": "бета", + "opentest": "відкритий тест", + "preview": "попередній перегляд" + }, + "locales": { + "en": "Англійська", + "es": "Іспанська", + "pt": "Португальська", + "ru": "російська", + "uk": "Українська", + "zh": "Китайська", + "ja": "Японська", + "fr": "Французька", + "pl": "Польська" + }, + "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": "NA", + "eu": "EU", + "asia": "APAC" + } + }, + "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Не вдалося знайти клан \"%s1\". Спробуйте вибрати клан з пошукового результату.", + "player_not_found": "# Could not find user\nI couldn't find user \"%s1\". 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 \"%s1\".", + "tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s1\". 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": "Допомогти 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": { + "name": "WN8", + "types": { + "very_bad": "Дуже погано", + "bad": "Погано", + "below_average": "Нижче середнього", + "average": "Середнє", + "above_average": "Вище середнього", + "good": "Добре", + "very_good": "Дуже добре", + "great": "Відмінно", + "unicum": "Винятковий", + "super_unicum": "Супер винятковий" + } + }, + "subcommands": { + "today": { + "name": "сьогодні", + "description": "Сьогодні" + }, + "30": { + "name": "30", + "description": "30 днів" + }, + "60": { + "name": "60", + "description": "60 днів" + }, + "90": { + "name": "90", + "description": "90 днів" + }, + "career": { + "name": "кар'єра", + "description": "Кар'єра" + }, + "custom": { + "name": "користувацьке", + "description": "Власний діапазон", + "options": { + "start": { + "name": "почати", + "description": "Початок за кількістю днів тому від сьогодні" + }, + "end": { + "name": "кінець", + "description": "Кінець за кількістю днів тому від сьогодні" + } + } + } + }, + "options": { + "clan": { + "name": "клан", + "description": "Назва клану або тег" + }, + "tank": { + "name": "танк", + "description": "Танк для якого треба отримати статистику" + }, + "username": { + "name": "ім'я користувача", + "description": "Бліц ім'я гравця" + }, + "filters": { + "nation": { + "name": "нація", + "description": "Нація" + }, + "class": { + "name": "клас", + "description": "Клас" + }, + "type": { + "name": "вид", + "description": "Вид" + } + }, + "region": { + "name": "регіон", + "description": "Регіон" + }, + "tier": { + "name": "рівень", + "description": "Рівень", + "choices": { + "tier": "Рівень %s1 - %s2" + } + } + }, + "periods": { + "career": "Кар'єра", + "today": "Сьогодні", + "days": "%s1 днів", + "custom": "%s1 - %s2 днів" + }, + "filters": { + "none": "Немає фільтрів", + "tier": "Рівень %s1" + }, + "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": { + "name": "аукціон", + "description": "Танки доступні зараз на аукціоні", + "body": { + "title": "Бліц Аукціон", + "subtitle": "%s1 доступно • наступна ціна %s2\nПоточна ставка: %s3", + "next": "Наступна ціна:%s1", + "available": "Доступно: %s1 / %s2" + }, + "errors": { + "no_auction": "# Немає поточного аукціону\n\nНаразі немає танків, доступних для купівлі на аукціоні. Якщо ви вважаєте це помилкою, будь ласка, повідомте про цю невідповідність на [офіційний Дискорд сервер](https://discord.gg/nDt7AjGJQH)." + }, + "actions": { + "bid": "Поточна ставка" + } + }, + "replay": { + "name": "реплей", + "description": "Показати результати реплею" + }, + "research": { + "name": "дослідження", + "description": "Показує витрати в дослідженні гілки", + "options": { + "target_tank": { + "name": "цільовий танк", + "description": "Останній танк для дослідження в гілці" + }, + "starting_tank": { + "name": "початковий танк", + "description": "Початковий танк в гілці (виводиться автоматично, якщо не передбачено)" + } + }, + "body": { + "subtitle": "Дослідження", + "total": "Загалом", + "research": "Дослідження: %s1", + "upgrades": "Покращення: %s1", + "purchase": "Придбання: %s1", + "equipment": "Спорядження: %s1", + "estimation": "# Це оцінка!\n\nЦей список може включати вже досліджені танки через неточну інформацію для вантажу. Спробуйте використати \"початковий танк\" з рядка за допомогою команди." + }, + "errors": { + "non_tech_tree": "# %s1 не в технічному дереві\n\nВи не можете досліджувати преміум/колекційний танк.", + "tier_1": "# %s1 це рівень I\n\nТанки рівню I завжди відкриті для всіх гравців безкоштовно.", + "already_researched": "# %s1 уже досліджено!\n\nСпробуйте забезпечити `starting-tank` коли використовуєте команду щоб подивитись підсумки гілки, яку ви вже дослідили.", + "start_end_equal": "# %s1 було надано двічі\n\nПереконайся, що `starting-tank` та `target-tank` різні.", + "tanks_not_on_line": ".", + "unordered": "" + } + }, + "about": { + "name": "про бота", + "description": "Вся інформація про бота", + "subcommands": { + "commands": { + "name": "команди", + "description": "Список всіх команд та їх описів", + "body": "# Команди\n\nBlitzKit пропонує наступні команди:\n\n" + }, + "embeds": { + "name": "вкладення", + "description": "Як працюють вкладення" + }, + "introduction": { + "name": "ознайомлення", + "description": "Про бота" + }, + "invite": { + "name": "запрошення", + "description": "Як запросити бота" + }, + "timezones": { + "name": "часові пояси", + "description": "Як BlitzKit отримує інформацію про час і використовує ваш часовий пояс" + }, + "credits": { + "name": "кредити", + "description": "Люди, які допомогли BlitzKit" + } + } + }, + "breakdown": { + "name": "breakdown", + "description": "A period's breakdown by tanks played", + "body": { + "total": "Загалом", + "battles": "Бої", + "wn8": "WN8", + "winrate": "Відсоток перемог", + "damage": "Шкода", + "unknown_tank": "Невідомий танк %s1" + } + }, + "debug": { + "name": "debug", + "description": "Debug information about the bot", + "body": { + "title": "# Debug\n\n", + "version": "Версія", + "shard": { + "label": "Уламок", + "default": "за замовчуванням" + }, + "uptime": "Час роботи" + } + }, + "evolution": { + "name": "еволюція", + "description": "Еволюція статистики", + "groups": { + "player": { + "name": "гравець", + "description": "Статистика гравця" + }, + "tank": { + "name": "танк", + "description": "Статистика танків" + } + } + }, + "inactive": { + "name": "неактивний", + "description": "Відображає всіх неактивних гравців у клані вище порогу", + "options": { + "threshold": { + "name": "поріг", + "description": "Кількість днів неактивності (за замовчуванням: %s1 днів)" + } + }, + "body": { + "subtitle": "Неактивний %s1+ днів", + "listing": "%s1 днів" + } + }, + "full_stats": { + "name": "повна статистика", + "description": "Повна ігрова статистика", + "body": { + "cards": { + "games": { + "title": "Ігри", + "winrate": "Відсоток перемог", + "battles": "Бої", + "wins_losses": "Перемоги / поразки" + }, + "efficiency": { + "title": "Ефективність", + "wn8": "WN8", + "survival": "Виживання", + "accuracy": "Точність" + }, + "lethality": { + "title": "Летальність", + "average_damage": "Середня шкода", + "damage_per_tier": "Шкода за рівень", + "damage_ratio": "Коефіцієнт шкоди", + "average_kills": "Середня кількість знищень" + }, + "miscellaneous": { + "title": "Різне", + "average_tier": "Середній рівень", + "average_spots": "Середній засвіт", + "average_xp": "Середній досвід", + "kills_to_death_ratio": "Співвідношення знищень до смертей" + } + } + } + }, + "link": { + "name": "посилання", + "description": "Прив'язує ваш Blitz та Discord акаунт", + "body": { + "accounts_linked": "Прив'язані акаунти", + "no_role_permissions": { + "title": "%s1 не вдалося перевірити", + "description": "У мене немає дозволу керувати ролями." + } + }, + "embed": { + "title": "Невірний обліковий запис?", + "description": "Use the command again and select a username from the search results." + } + }, + "search_clans": { + "name": "пошук кланів", + "description": "Пошук кланів", + "options": { + "limit": { + "name": "ліміт", + "description": "The max number of clan results (default: %s1)" + } + }, + "body": { + "title": "Clan search for %s1 in region %s2", + "no_results": "No clans found" + } + }, + "search_players": { + "name": "пошук гравців", + "description": "Пошук гравців", + "options": { + "limit": { + "name": "ліміт", + "description": "The max number of player results (default: %s1)" + } + }, + "body": { + "title": "Player search for %s1 in region %s2", + "no_results": "Гравця не знайдено" + } + }, + "rating_leaderboard": { + "name": "rating-leaderboard", + "description": "Rating battles statistics", + "subcommands": { + "neighbors": { + "name": "neighbors", + "description": "Neighboring players on the leaderboard" + }, + "league": { + "name": "league", + "description": "Top players of a league" + } + }, + "options": { + "limit": { + "name": "ліміт", + "description": "" + }, + "season": { + "name": "season", + "description": "Season number (default: current)", + "choices": { + "current": "%s1 (current)" + } + }, + "league": { + "name": "ліга", + "description": "The league to display" + } + }, + "body": { + "top_players": "Top %s1 players", + "no_participation": "# %s1 didn't play rating in season %s2\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 %s1", + "neighbors": "Rating neighbors", + "more": "+ %s1 more" + } + }, + "today": { + "name": "сьогодні", + "description": "Today's played tanks and statistics" + }, + "stats": { + "name": "статистика", + "description": "Коротка статистика за період" + }, + "owned_tanks": { + "name": "owned-tanks", + "description": "Shows all own tanks in a tier", + "body": { + "title": "# %s1's owned tier %s2 tanks\n\n" + } + }, + "permissions": { + "name": "permissions", + "description": "Checks wether the bot has the required permissions to function", + "body": { + "title": "# Permissions\n\n%s1", + "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": { + "name": "", + "description": "Check wether the bot is alive.", + "body": { + "pong": "Pong 🏓", + "pong_with_time": "Pong 🏓 - %s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "Ping BlitzKit" + }, + "wotb": { + "name": "wotb", + "description": "Ping World of Tanks Blitz" + } + } + }, + "player_info": { + "name": "player-info", + "description": "Basic information about a player", + "body": { + "title": "%s1's information", + "nickname": "Нік", + "battles": "Бої", + "winrate": "Відсоток перемог", + "account_id": "ID акаунта", + "created": "Створено", + "last_battle": "Остання битва" + } + }, + "rating": { + "name": "рейтинг", + "description": "Rating player's statistics", + "subcommands": { + "today": { + "name": "сьогодні", + "description": "Today's rating statistics" + }, + "season": { + "name": "season", + "description": "This season's rating statistics" + } + }, + "body": { + "subtitle": { + "today": "Today's rating statistics", + "season": "This season's rating statistics" + } + }, + "errors": { + "no_participation": "# %s1 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": { + "settings": { + "dev_mode": "Development mode", + "language": "Мова" + }, + "common": { + "player_search": { + "hint": "Пошук гравців...", + "no_results": "Немає результатів", + "searching": "Пошук..." + }, + "copy_button": { + "copied": "Скопійовано!" + }, + "tank_search": { + "search_bar_hint": "Шукайте техніку...", + "recent": "Недавно переглянуті", + "select_all": "Вибрати все", + "count_singular": "1 танк", + "count_plural": "%s1 танків", + "sorting_by": "Сортувати за %s1", + "no_tanks_found": { + "body": "Нічого не знайдено.", + "search_again": "Спробуйте шукати заново.", + "clear_filters": "Спробуйте очистити фільтри." + }, + "sort": { + "meta.none": "нічого", + "survivability.health": "міцність", + "survivability.viewRange": "радіус огляду", + "survivability.camouflageStill": "непомітність (стоячи)", + "survivability.camouflageMoving": "непомітність (в русі)", + "survivability.camouflageShooting": "непомітність (після пострілу)", + "survivability.volume": "об'єм(?)", + "survivability.length": "довжина", + "fire.dpm": "шкода за хвилину (базова)", + "fire.dpmPremium": "шкода за хвилину (преміум)", + "fire.reload": "швидкість перезаряджання", + "fire.caliber": "калібр", + "fire.standardPenetration": "базове пробиття", + "fire.premiumPenetration": "преміум пробиття", + "fire.damage": "шкода", + "fire.moduleDamage": "шкода по модулях", + "fire.shellVelocity": "швидкість польоту снаряда", + "fire.shellRange": "дальність польоту", + "fire.shellCapacity": "кількість снарядів", + "fire.aimTime": "час зведення", + "fire.dispersionStill": "розкид стоячи", + "fire.dispersionMoving": "розкид в русі", + "fire.gunDepression": "КВН вниз", + "fire.gunElevation": "КВН вгору", + "maneuverability.forwardsSpeed": "максимальна швидкість вперед", + "maneuverability.backwardsSpeed": "максимальна швидкість назад", + "maneuverability.power": "потужність двигуна", + "maneuverability.powerToWeight": "притома потужність", + "maneuverability.weight": "маса", + "maneuverability.traverseSpeed": "швидкість повороту" + }, + "sort_dropdown": { + "label": "Сортувати", + "groups": { + "sort": { + "no_sort": "Не сортувати", + "label": "Сортувати за", + "fire": "Вогнева міць", + "maneuverability": "Маневреність", + "survivability": "Живучість" + }, + "direction": { + "label": "Напрямок", + "ascending": "Зростає", + "descending": "Спадає" + } + } + } + }, + "warnings": { + "test": "Танки, що тестуються, можуть мінятися і можуть не являти собою фінальний." + } + }, + "home": { + "hero": { + "credit": "Зроблено %s1 ❤" + }, + "hot_tanks": { + "title": "Популярні танки", + "subtitle": "Останні %s1 дні(в)" + }, + "other_projects": { + "title": "Інші класні проєкти" + }, + "help_translate": "Будь ласка, допоможіть перекласти %s1 на %s2!", + "seo_description": "Інструменти для всього, пов'язаного з WoT Blitz" + }, + "tools": { + "tankopedia": { + "name": "Танкопедія", + "button": "Знайти танки", + "description": "Танкова енциклопедія", + "seo_description": "Характеристики, броня та інше для всіх танків WoT Blitz", + "tank_seo_title": "%s1 - Рівень %s2 %s3 %s4 | %s5 Танкопедія | WoT Blitz", + "tank_seo_description": "%s1 броня, характеристики, обладнання, амуніція та більше. %s2 шкода на хвилину, %s3 разова шкода, %s4 мм пробиття, %s5 км/год максимальна швидкість, %s6 міцність.", + "sandbox": { + "poses": { + "hull_down": "Від башти", + "face_hug": "Клінч", + "default": "За замовчуванням" + }, + "settings": { + "armor": "Броня", + "green_penetration": "Зелене пробиття", + "hide_model_under_armor": "Hide model under armor", + "opaque": "Opaque", + "dev_wireframe": "DEV: wireframe", + "environment": "Environment", + "show_grid": "Show grid" + }, + "screenshot": { + "download": "Завантажити", + "copy": "Скопіювати" + }, + "model": { + "name": "Модель" + }, + "dynamic": { + "name": "Динамічна броня", + "custom_shell": { + "type": "Вид", + "caliber": "Калібр", + "penetration": "Бронепробиття", + "normalization": "Нормалізація", + "ricochet": "Рикошет", + "explosion_radius": "Радіус розльоту уламків", + "exit": "Exit", + "reset": "Reset" + }, + "advanced": "Advanced highlighting", + "shooter": "Shooter:", + "shot_card": { + "element": { + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "gap": "Gap", + "gun": "Gun", + "track": "Tracks" + }, + "status": { + "penetration": "%s1hp penetration", + "blocked": "Blocked", + "ricochet": "Рикошет", + "splash": "%s1hp splash" + }, + "stats": { + "thickness_and_angle": "%s1mm %s2°", + "nominal": "%s1mm nominal", + "ricochet_loss": "%s1% penetration" + } + } + }, + "static": { + "name": "Static armor", + "primary": "Primary", + "spaced": "Spaced", + "external": "External", + "edit": { + "label": "Редагувати", + "reset": "Reset" + } + } + }, + "meta": { + "back": "Назад", + "compare": "Порівняти", + "swap": { + "button": "Swap", + "title": "Swap tanks" + }, + "vanilla": "Vanilla", + "opentest": "Відкритий тест", + "full_name": "Full name", + "nation": "Нація", + "class": "Клас", + "tier": "Рівень", + "type": "Вид", + "dev_id": "DEV: ID", + "purchase": "Purchase price", + "restoration": "Restoration price", + "sale": "Sale price", + "research": "Research XP" + }, + "tech_tree": { + "title": "Звичайний", + "battle": "1 бій", + "battles": "%s1 battles", + "route": "Route %s1 of %s2" + }, + "game_modes": { + "title": "Game mode abilities" + }, + "review": { + "title": "Review videos", + "disclaimer": "BlitzKit does not endorse any review videos.", + "join": "Apply to %s1 YouTuber program" + }, + "configuration": { + "title": "Configuration", + "modules": { + "title": "Модулі", + "stock": "Stock", + "upgrade": "Upgrade" + }, + "equipment": { + "title": "Обладнання", + "clear": "Clear" + }, + "provisions": { + "title": "Амуніція", + "clear": "Очистити" + }, + "consumables": { + "title": "Спорядження", + "clear": "Очистити", + "info": "Yellow consumables do not affect the statistics." + }, + "skills": { + "title": "Навички екіпажу", + "clear": "Очистити", + "maximize": "Збільшити все", + "info": "Жовті навички застосовуються лише за особливих обставин. Тримайте [Space] для швидкого рівня 0 та 7." + }, + "miscellaneous": { + "title": "Різне" + } + }, + "characteristics": { + "title": "Characteristics", + "compare": { + "label": "Compare against", + "class": "Tier %s1 %s2", + "tier": "All of tier %s1", + "all": "Усі танки" + }, + "values": { + "shell_index": "Снаряд %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "Відстань", + "dispersion_at_distance": "Dispersion at %s1m", + "gun_flexibility": "Gun flexibility", + "speed": "Швидкість", + "terrain_coefficients": "Terrain coefficients", + "raw_terrain_coefficients": "Raw terrain coefficients", + "power_to_weight_ratio": "Power to weight ratio", + "hull_traverse_speed": "Hull traverse speed", + "camouflage": "Камуфляж", + "reload": "Час перезаряджання", + "crewCount": "Crew count", + "shellNormalization": "Нормалізація", + "shellRicochet": "Рикошет", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Снаряди", + "mostOptimalShellIndex": "Most optimal shell", + "shellReloads": "Shell reloads", + "shellReload": "Shell reload", + "intraClip": "Intra-clip", + "caliber": "Калібр", + "penetration": "Бронепробиття", + "damage": "Шкода", + "clipDamage": "Clipping potential", + "moduleDamage": "Шкода по модулях", + "explosionRadius": "Радіус розльоту уламків", + "shellVelocity": "Швидкість польоту снаряда", + "aimTime": "Час зведення", + "dispersion": "Нерухомо", + "dispersionMoving": "У русі", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Shooting", + "dispersionGunDamaged": "Gun damaged", + "gunDepression": "Depression", + "gunElevation": "Elevation", + "gunFrontalDepression": "Frontal depression", + "gunFrontalElevation": "Frontal elevation", + "gunRearDepression": "Rear depression", + "gunRearElevation": "Rear elevation", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Forwards", + "speedBackwards": "Backwards", + "hardTerrainCoefficient": "Hard", + "hardTerrainCoefficientRaw": "Hard", + "mediumTerrainCoefficient": "Середній", + "mediumTerrainCoefficientRaw": "Середній", + "softTerrainCoefficient": "Soft", + "softTerrainCoefficientRaw": "Soft", + "enginePower": "Engine power", + "powerToWeightRatioHardTerrain": "Hard", + "powerToWeightRatioMediumTerrain": "Середній", + "powerToWeightRatioSoftTerrain": "Soft", + "weight": "Маса", + "turretTraverseSpeed": "Turret traverse speed", + "gunTraverseSpeed": "Gun traverse speed", + "hullTraverseHardTerrain": "Hard", + "hullTraverseMediumTerrain": "Середній", + "hullTraverseSoftTerrain": "Soft", + "health": "Health", + "fireChance": "Fire chance", + "viewRange": "View range", + "camouflageStill": "Нерухомо", + "camouflageMoving": "У русі", + "camouflageShootingStill": "Нерухомо", + "camouflageShootingMoving": "У русі", + "camouflageCaughtOnFire": "Caught on fire", + "width": "Width", + "height": "Height", + "length": "Length", + "volume": "Volume", + "shellRange": "Shell range", + "shellCapacity": "Shell capacity", + "gunType": "Тип гармати", + "penetrationAt250m": "Penetration at 250m" + } + }, + "firepower": { + "title": "Firepower" + }, + "crew": { + "title": "Crew training", + "info": "represents the other roles a crew member can take.", + "count": "Crew count", + "commander": "Командир", + "gunner": "Навідник", + "driver": "Водій", + "radioman": "Radioman", + "loader": "Заряджаючий" + }, + "maneuverability": { + "title": "Маневреність" + }, + "survivability": { + "title": "Живучість" + } + }, + "compare": { + "name": "Порівняти", + "button": "Порівняти танки", + "description": "Compare tank statistics", + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + "actions": { + "add": { + "button": "Додати", + "title": "Add tanks", + "description": "Select tanks to add to comparison" + }, + "clear": "Clear", + "deltas": { + "none": "No deltas", + "percentage": "Відсоток", + "absolute": "Absolute" + } + }, + "no_tanks": { + "title": "Не вибрано жодного танку", + "body": "Натисніть кнопку Додати, щоб розпочати" + }, + "table": { + "equipment": { + "clear": "Clear", + "apply_to_all": "Apply to all" + }, + "skills": { + "clear": "Очистити", + "maximize": "Збільшити все" + }, + "modules": { + "stock": "Stock", + "upgrade": "Upgrade" + }, + "provisions": { + "clear": "Clear" + }, + "consumables": { + "clear": "Clear" + } + } + }, + "performance": { + "name": "Performance", + "button": "View tanks", + "description": "Tank performance statistics", + "seo_description": "Best tanks and their performances in World of Tanks Blitz", + "estimation": { + "body": "Estimated stats based on %s1 players. Updated %s2 ago.", + "hours": "%s1 hours", + "minutes": "%s1 minutes" + }, + "player_count": { + "label": "Player count" + }, + "table": { + "tanks": { + "header": "Танк", + "total": "Загалом" + }, + "stats": { + "winrate": "Відсоток перемог", + "players": "Гравці", + "damage": "Шкода", + "survival": "Виживання", + "xp": "Досвід", + "kills": "Знищено", + "spots": "Засвіт", + "accuracy": "Точність", + "shots": "Постріли", + "hits": "Пробиття", + "damage_ratio": "Dmg. ratio", + "damage_taken": "Dmg. taken", + "capture_points": "Cap points" + } + } + }, + "gallery": { + "name": "Gallery", + "button": "Browse all", + "description": "Blitz player avatars", + "seo_description": "All World of Tanks Blitz player avatars", + "last_updated": "Last updated on %s1", + "search": { + "hint": "Search avatars...", + "results": "%s1 avatars" + } + }, + "session": { + "name": "Сесія", + "button": "Розпочати відстеження", + "description": "Статистика сесії в реальному часі", + "seo_description": "Відстежуйте свою статистику в реальному часі як ви граєте World of Tanks Blitz", + "search": { + "lookup": "Знайди гравця щоб розпочати", + "hint": "Пошук гравців...", + "searching": "Пошук...", + "no_results": "Гравця не знайдено" + }, + "controls": { + "title": "Відстежуємо %s1", + "subtitle": "Від %s1", + "reset": "Скинути", + "change": "Змінити" + }, + "no_cc": { + "title": "", + "body": "CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned.", + "alright": "Alright" + }, + "no_battles": { + "title": "Немає боїв", + "hint": "Go ahead and play a game" + }, + "table": { + "tank": "Танк", + "total": "Загалом", + "remove": "Прибрати" + } + }, + "tier_list": { + "name": "Tier list", + "button": "Rank tanks", + "description": "Rank Blitz tanks", + "seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz", + "buttons": { + "reset": "Reset", + "share": "Share" + } + }, + "embed": { + "name": "Embed", + "button": "Create now", + "description": "Embeds for streaming", + "seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization", + "coming_soon": "More embeds coming soon.", + "suggest": "Suggest ideas.", + "preview": { + "title": "Example preview", + "upload": "Upload test background" + }, + "configuration": { + "import": { + "title": "Import", + "description": "Paste your current URL if you have one already to import your settings", + "hint": "Paste URL...", + "apply": "Apply", + "idiot_title": "Invalid URL", + "idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!", + "idiot_button": "Okay, my bad" + }, + "export": { + "title": "Export", + "description": "Copy all properties for you streaming software", + "generate": { + "button": "Generate URL", + "title": "Generate URL", + "subtitle": "Search an account to track", + "cancel": "Cancel", + "success": { + "title": "Copy URL", + "info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".", + "back": "Назад", + "copy": "Copy URL" + } + }, + "css": "Copy CSS", + "width": "Copy Width", + "height": "Copy Height", + "framerate": "Copy Framerate" + }, + "customize": { + "title": "Customize", + "reset": "Reset", + "description": "Bells and whistles of your embed" + } + }, + "types": { + "breakdown": { + "name": "Breakdown", + "options": { + "width": "Width", + "height": "Height", + "showTotal": "Show total", + "listGap": "List gap", + "listMaxTanks": "Max tanks", + "cardRadius": "Card radius", + "cardHeaderBackgroundColor": "Card header background color", + "cardHeaderPadding": "Card header padding", + "cardTitle": "Card title", + "cardTitleTypeColor": "Card title type color", + "cardTitleClassIcon": "Card title class icon", + "cardBodyBackgroundColor": "Card body background color", + "cardBodyPadding": "Card body padding", + "columnGap": "Column gap", + "columnValue": "Column value", + "columnLabel": "Column label", + "column1": "Column 1", + "column2": "Column 2", + "column3": "Column 3", + "column4": "Column 4", + "column1CustomLabel": "Column 1 label", + "column2CustomLabel": "Column 2 label", + "column3CustomLabel": "Column 3 label" + } + } + } + }, + "charts": { + "name": "Charts", + "button": "Nerd out", + "description": "Lots of graphs", + "seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz" + }, + "rating": { + "name": "Rating", + "button": "View leaderboard", + "description": "Full rating leaderboard", + "seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz" + }, + "discord": { + "name": "Discord bot", + "button": "Install now", + "description": "Stats right in Discord", + "seo_description": "Get stats right in Discord with BlitzKit's Discord bot" + }, + "more": { + "name": "More coming soon", + "button": "Join Discord", + "description": "What we're making", + "seo_description": "What we're making with BlitzKit" + }, + "players": { + "name": "Гравці", + "button": "Lookup player", + "description": "Periodical player statistics", + "seo_description": "Periodical player statistics in World of Tanks Blitz" + }, + "home": { + "name": "Головна сторінка", + "button": "Get started", + "description": "Get started with BlitzKit", + "seo_description": "Get started with BlitzKit" + }, + "blitz_analysis": { + "name": "BlitzAnalysis[]", + "button": "Analyze now", + "description": "Rich historic average stats", + "seo_description": "Rich historic average stats in World of Tanks Blitz" + }, + "blitz_insider": { + "name": "Blitz Insider", + "button": "Discover today", + "description": "In-game session stats", + "seo_description": "In-game session stats in World of Tanks Blitz" + } + }, + "footer": { + "motto": "Everything World of Tanks Blitz", + "copyright": "Unaffiliated with Wargaming or any service", + "about": { + "title": "About", + "credits": "Credits", + "discord": "Discord", + "github": "GitHub", + "patches": "Patches" + }, + "legal": { + "title": "Legal", + "privacy": "Privacy policy", + "terms": "Terms of service" + } + } + } +} diff --git a/packages/i18n/strings/zh.json b/packages/i18n/strings/zh.json new file mode 100644 index 000000000..6e6a794ba --- /dev/null +++ b/packages/i18n/strings/zh.json @@ -0,0 +1,1272 @@ +{ + "test": { + "default": "測試", + "long": "这是一个测试用长字符串。你不会再用户界面中看见这些。我最喜欢的水果是%s1因为它的颜色是%s2。我如此喜欢它以至于我会把它的名字念3次!%s1!%s1!%s1!重申一次,它是%s2而不是%s3。", + "fox": "褐色的狐狸迅速地跳过那只懒狗" + }, + "common": { + "units": { + "m": "%s公尺", + "mm": "%s毫米", + "deg": "%s1度", + "hp": "%s生命值", + "x": "x%s1" + }, + "gun_types": { + "auto_loader": "彈夾型戰車", + "auto_reloader": "自動裝填", + "regular": "單發" + }, + "shells": { + "ap": "穿甲彈", + "apcr": "硬式複合穿甲彈", + "heat": "反戰車高爆彈", + "he": "高爆彈" + }, + "player_count_period": { + "total": "全部", + "d_120": "過去120天", + "d_90": "過去90天", + "d_60": "過去60天", + "d_30": "過去一個月", + "d_7": "過去一週", + "d_1": "昨日" + }, + "branches": { + "local": "開發人員", + "dev": "測試版", + "opentest": "開放測試", + "preview": "預覽" + }, + "locales": { + "en": "英文", + "es": "西班牙文", + "pt": "葡萄牙文", + "ru": "俄文", + "uk": "乌克兰文", + "zh": "中文", + "ja": "日文", + "fr": "法文", + "pl": "波兰文" + }, + "wn8_percentile": { + "0": "非常差", + "1": "差", + "2": "較差", + "3": "平均", + "4": "高於平均", + "5": "好", + "6": "非常好", + "7": "很好", + "8": "極品", + "9": "超強勢玩家" + }, + "bkni_percentile": { + "0": "Super malum", + "1": "Malum", + "2": "非常差", + "3": "差", + "4": "低於平均", + "5": "平均", + "6": "高於平均", + "7": "好", + "8": "非常好", + "9": "極品", + "10": "超级强势玩家" + }, + "nations": { + "ussr": "苏联 (S系)", + "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": "玩家表現評分", + "cumulative_battle_life_time": "總戰鬥時間" + } + }, + "bot": { + "common": { + "errors": { + "clan_not_found": "# 找不到公會\n找不到\"%s1\"。請嘗試從搜尋結果中選擇一個公會。", + "player_not_found": "# 找不到玩家\n無法找到玩家 \"%s1\"。 嘗試從搜尋結果中選擇玩家,或輸入一個有效的玩家名稱或 \"/link\" 命令。", + "player_not_linked": "# 使用 `/link` 來開始\n我不知道你是誰,但你今天可以告訴我!", + "tank_name_not_found": "# 找不到戰車\n找不到名為 \"%s1 \"的戰車。", + "tank_id_not_found": "# 找不到戰車\n無法找到ID為 \"%s1\"的戰車。 請嘗試輸入戰車的名稱而不是數字。", + "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": { + "name": "玩家表現評分", + "types": { + "very_bad": "非常差", + "bad": "差", + "below_average": "低於平均", + "average": "平均", + "above_average": "高於平均", + "good": "好", + "very_good": "非常好", + "great": "很好", + "unicum": "極品", + "super_unicum": "超強勢玩家" + } + }, + "subcommands": { + "today": { + "name": "今日", + "description": "今日:" + }, + "30": { + "name": "30", + "description": "30 天" + }, + "60": { + "name": "60", + "description": "60 天" + }, + "90": { + "name": "90", + "description": "90 天" + }, + "career": { + "name": "生涯", + "description": "玩家資料" + }, + "custom": { + "name": "自定", + "description": "自定範圍", + "options": { + "start": { + "name": "開始", + "description": "從今天起算的天數前開始" + }, + "end": { + "name": "結束", + "description": "從今天起算的天數前結束" + } + } + } + }, + "options": { + "clan": { + "name": "公會", + "description": "公會名稱或標籤" + }, + "tank": { + "name": "戰車", + "description": "要獲取統計資料的戰車" + }, + "username": { + "name": "使用者名稱", + "description": "玩家的閃擊戰暱稱" + }, + "filters": { + "nation": { + "name": "國別", + "description": "國別" + }, + "class": { + "name": "分類", + "description": "戰車類別" + }, + "type": { + "name": "戰車類型", + "description": "戰車類型" + } + }, + "region": { + "name": "所在地區", + "description": "地區" + }, + "tier": { + "name": "等級", + "description": "等级", + "choices": { + "tier": "%s1- %s2 级" + } + } + }, + "periods": { + "career": "玩家資料", + "today": "今日:", + "days": "%s1 天", + "custom": "%s1 - %s2 天" + }, + "filters": { + "none": "無篩選器", + "tier": "等級 %s1" + }, + "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": { + "name": "拍賣", + "description": "目前拍賣中的戰車", + "body": { + "title": "Blitz 拍賣活動", + "subtitle": "%s1 可用 • 下一個價格 %s2\n現在出價: %s3", + "next": "下一個價格: %s1", + "available": "可用: %s1 / %s2" + }, + "errors": { + "no_auction": "# 目前没有正在進行的拍賣\n\n拍賣中沒有可供購買的戰車。 如果您認為這是一個錯誤,請在[官方的 Discord伺服器](https://discord.gg/nDt7AjGJQH)中報告這個問題。" + }, + "actions": { + "bid": "立即購買" + } + }, + "replay": { + "name": "重播", + "description": "顯示重播結果" + }, + "research": { + "name": "研發", + "description": "列出研發線路的花費", + "options": { + "target_tank": { + "name": "目標戰車", + "description": "科技樹裡最後的待研發戰車" + }, + "starting_tank": { + "name": "起始戰車", + "description": "科技樹中起始戰車(如果未提供則自動推定)" + } + }, + "body": { + "subtitle": "研發", + "total": "全部", + "research": "研發: %s1", + "upgrades": "升級:%s1", + "purchase": "購買: %s1", + "equipment": "裝備: %s1", + "estimation": "# 這是一個估計值!\n\n這個列表可能包含已經研發過的戰車,因為 Warming 提供了不正確的資料。 如果您可以,請嘗試從列表中提供一個 \"起始戰車\" 。" + }, + "errors": { + "non_tech_tree": "#%s1不在科技樹裡\n\n你不能研究一個 加值/收藏家 戰車。", + "tier_1": "”%s1“是一級戰車\n\n一級戰車是可免費解鎖的。", + "already_researched": "%s1 是研發完成的\n\n當在使用命令查看您已研發過的線路總數時請嘗試提供“起始戰車”。", + "start_end_equal": "%s1 是研發完成的\n\n當在使用命令查看您已研發過的線路總數時請嘗試提供“起始戰車”。", + "tanks_not_on_line": "”%s1“和 \"%s2\" 不在同一個科技樹中\n\n請確認”起始戰車“和 \"目標戰車\" 為同一科技樹。", + "unordered": "# %s1高到 %s2\n\n請務必提供 %s3 作爲起始戰車,而不是低階戰車。" + } + }, + "about": { + "name": "關於", + "description": "所有你需要的機器人資訊", + "subcommands": { + "commands": { + "name": "技能", + "description": "所有技能及其描述列表", + "body": "# 技能\n\nBlitzKit 提供了以下技能:\n\n" + }, + "embeds": { + "name": "嵌入", + "description": "嵌入如何運作" + }, + "introduction": { + "name": "介紹", + "description": "關於此機器人" + }, + "invite": { + "name": "邀請", + "description": "如何邀請機器人" + }, + "timezones": { + "name": "時區", + "description": "BlitzKit 如何判斷並使用你的時區" + }, + "credits": { + "name": "積分", + "description": "誰幫助過 BlitzKit" + } + } + }, + "breakdown": { + "name": "詳細資訊", + "description": "特定時期的戰車使用詳細資料", + "body": { + "total": "全部", + "battles": "戰鬥場次", + "wn8": "玩家表現評分", + "winrate": "勝率", + "damage": "傷害", + "unknown_tank": "未知戰車 %s1" + } + }, + "debug": { + "name": "偵錯", + "description": "機器人偵錯資訊", + "body": { + "title": "# 偵錯\n\n", + "version": "版本", + "shard": { + "label": "碎片", + "default": "預設" + }, + "uptime": "運行時間" + } + }, + "evolution": { + "name": "演變", + "description": "統計資料的演變", + "groups": { + "player": { + "name": "玩家", + "description": "玩家統計資料" + }, + "tank": { + "name": "戰車", + "description": "戰車統計資料" + } + } + }, + "inactive": { + "name": "不活躍", + "description": "列出所有在公會中且活躍度低於某個閾值的玩家", + "options": { + "threshold": { + "name": "閾值", + "description": "未上線天數 (預設: %s1 天)" + } + }, + "body": { + "subtitle": "%s1+ 天内未活動", + "listing": "%s1 天" + } + }, + "full_stats": { + "name": "完整統計資料", + "description": "遊戲內完整統計資料", + "body": { + "cards": { + "games": { + "title": "遊玩次數", + "winrate": "勝率", + "battles": "戰鬥場次", + "wins_losses": "勝利/戰敗" + }, + "efficiency": { + "title": "效率", + "wn8": "玩家表現評分", + "survival": "生存性", + "accuracy": "命中率" + }, + "lethality": { + "title": "致命傷害", + "average_damage": "平均傷害", + "damage_per_tier": "各等級傷害", + "damage_ratio": "傷害比", + "average_kills": "平均擊毀" + }, + "miscellaneous": { + "title": "雜項", + "average_tier": "平均等級", + "average_spots": "平均偵查", + "average_xp": "平均經驗", + "kills_to_death_ratio": "擊殺陣亡比" + } + } + } + }, + "link": { + "name": "連結", + "description": "連結你的 Blitz 和 Discord 帳戶", + "body": { + "accounts_linked": "帳號已連結", + "no_role_permissions": { + "title": "%s1 驗證失敗", + "description": "我没有管理者權限。" + } + }, + "embed": { + "title": "帳號不正確?", + "description": "再次使用此命令,從搜尋結果中選擇一個用戶。" + } + }, + "search_clans": { + "name": "搜尋-公會資料", + "description": "搜尋-公會資料", + "options": { + "limit": { + "name": "限制", + "description": "公會結果的最大數量 (預設: %s1)" + } + }, + "body": { + "title": "公會搜尋 %s1 在區域 %s2", + "no_results": "没有找到該公會" + } + }, + "search_players": { + "name": "搜尋玩家", + "description": "搜尋玩家", + "options": { + "limit": { + "name": "限制", + "description": "最大玩家結果數 (預設: %s1)" + } + }, + "body": { + "title": "玩家在區域 %s2 中搜尋 %s1", + "no_results": "未找到玩家" + } + }, + "rating_leaderboard": { + "name": "評價排行榜", + "description": "評價戰鬥統計資料", + "subcommands": { + "neighbors": { + "name": "附近的", + "description": "排行榜上附近的玩家" + }, + "league": { + "name": "聯盟", + "description": "聯盟的頂尖玩家" + } + }, + "options": { + "limit": { + "name": "限制", + "description": "顯示附近有多少玩家(預設: %s1)" + }, + "season": { + "name": "賽季", + "description": "賽季數 (預設:目前)", + "choices": { + "current": "%s1 (目前)" + } + }, + "league": { + "name": "聯盟", + "description": "要顯示的聯賽" + } + }, + "body": { + "top_players": "前 %s1玩家", + "no_participation": "# %s1 在賽季 %s2進行過評價戰鬥\n\n該玩家没有參加本賽季或没有獲得過去的校準。", + "no_ongoing_season": "# 没有進行中的賽季\n\n目前没有賽季季。", + "deleted_player": "刪除 %s1", + "neighbors": "對鄰居進行評分", + "more": "+ 增加 %s1" + } + }, + "today": { + "name": "今日", + "description": "今天玩過的戰車和統計資料" + }, + "stats": { + "name": "統計資料", + "description": "一段時間內快速統計" + }, + "owned_tanks": { + "name": "已擁有的戰車", + "description": "在一個等級中顯示所有已擁有的戰車", + "body": { + "title": "# %s1所擁有的 %s2階戰車\n\n" + } + }, + "permissions": { + "name": "權限", + "description": "檢查機器人是否擁有運作所需的權限", + "body": { + "title": "# 權限\n\n%s1", + "view_channels": "查看頻道:需要找到重新整理按鈕使用的訊息", + "read_message_history": "讀取歷史訊息: 需要找到訊息以重新整理", + "attach_files": "附加文件:需要將圖像添加到基於圖像的命令", + "external_emojis": "外部表情:需要添加國家旗幟和戰車類別" + } + }, + "ping": { + "name": "連線延遲", + "description": "檢查機器人處於活動狀態。", + "body": { + "pong": "桌球拍", + "pong_with_time": "桌球拍-%s1ms" + }, + "subcommands": { + "blitzkit": { + "name": "blitzkit", + "description": "BlitzKit 的 Ping 值" + }, + "wotb": { + "name": "戰車世界閃擊戰", + "description": "戰車世界閃擊戰的 Ping 值" + } + } + }, + "player_info": { + "name": "玩家資訊", + "description": "玩家基本資訊", + "body": { + "title": "%s1的資訊", + "nickname": "暱稱", + "battles": "戰鬥場次", + "winrate": "勝率", + "account_id": "帳號 ID", + "created": "已創建", + "last_battle": "最後戰鬥" + } + }, + "rating": { + "name": "评级战斗", + "description": "評價戰鬥統計資料", + "subcommands": { + "today": { + "name": "今日", + "description": "今日評價統計資料" + }, + "season": { + "name": "賽季", + "description": "本賽季評價統計資料" + } + }, + "body": { + "subtitle": { + "today": "今日評價統計資料", + "season": "本賽季評價統計資料" + } + }, + "errors": { + "no_participation": "# %s1 未完成評價\n該玩家未參與本賽季的評價戰鬥。", + "unrecorded_stats": "# 統計資料目前無法使用\n\nWargaming 可能暫停了伺服器。 如果有連續的賽季,統計資料可能在24小時内提供。" + } + } + } + }, + "website": { + "settings": { + "dev_mode": "開發人員模式", + "language": "語言" + }, + "common": { + "player_search": { + "hint": "搜尋玩家……", + "no_results": "沒有結果", + "searching": "搜尋中……" + }, + "copy_button": { + "copied": "已複製!" + }, + "tank_search": { + "search_bar_hint": "搜尋戰車....", + "recent": "最近查看", + "select_all": "選擇全部", + "count_singular": "1 台戰車", + "count_plural": "%s1戰車", + "sorting_by": "按 %s1 排序", + "no_tanks_found": { + "body": "找不到戰車。", + "search_again": "請嘗試重新搜尋。", + "clear_filters": "請嘗試重置篩選器。" + }, + "sort": { + "meta.none": "無", + "survivability.health": "生命值", + "survivability.viewRange": "偵查範圍", + "survivability.camouflageStill": "静止中隐蔽值", + "survivability.camouflageMoving": "移動中隐蔽值", + "survivability.camouflageShooting": "射擊隐蔽值", + "survivability.volume": "體積", + "survivability.length": "長度", + "fire.dpm": "標準砲彈 每分鐘傷害血量", + "fire.dpmPremium": "加值砲彈 每分鐘傷害血量", + "fire.reload": "裝填", + "fire.caliber": "口徑", + "fire.standardPenetration": "標準砲彈穿透力", + "fire.premiumPenetration": "加值砲彈穿透力", + "fire.damage": "殺傷力", + "fire.moduleDamage": "模組傷害值", + "fire.shellVelocity": "砲彈飛行速度", + "fire.shellRange": "最大射程", + "fire.shellCapacity": "攜彈量", + "fire.aimTime": "瞄準時間", + "fire.dispersionStill": "一百公尺擴散度", + "fire.dispersionMoving": "移動時擴散度", + "fire.gunDepression": "俯角", + "fire.gunElevation": "仰角", + "maneuverability.forwardsSpeed": "最高前進速度", + "maneuverability.backwardsSpeed": "倒退速度", + "maneuverability.power": "功率", + "maneuverability.powerToWeight": "推重比", + "maneuverability.weight": "重量", + "maneuverability.traverseSpeed": "轉速" + }, + "sort_dropdown": { + "label": "排序", + "groups": { + "sort": { + "no_sort": "未排序", + "label": "排序依據", + "fire": "開火", + "maneuverability": "機動性", + "survivability": "生存能力" + }, + "direction": { + "label": "轉向", + "ascending": "爬升", + "descending": "下坡" + } + } + } + }, + "warnings": { + "test": "測試中的戰車可能會發生變化,並不代表最終結果。" + } + }, + "home": { + "hero": { + "credit": "由 %s1 製作❤️" + }, + "hot_tanks": { + "title": "受歡迎的戰車", + "subtitle": "過去 %s1 天" + }, + "other_projects": { + "title": "其他酷炫的專案" + }, + "help_translate": "請幫忙將 %s1 翻譯成 %s2!", + "seo_description": "戰車世界閃擊戰 全方位工具" + }, + "tools": { + "tankopedia": { + "name": "戰車百科", + "button": "尋找戰車", + "description": "閃擊戰戰車百科全書", + "seo_description": "世界坦克布莱茨坦克坦克世界闪击战的所有坦克的统计数字、装甲和更多", + "tank_seo_title": "%s1-Tier %s2 %s3 %s4 | %s5 Tankopedia | 戰車世界閃擊戰 (WoTB)", + "tank_seo_description": "%s1-Tier %s2 %s3 %s4 | %s5 Tankopedia | 戰車世界閃擊戰 (WoTB)", + "sandbox": { + "poses": { + "hull_down": "戰車下部", + "face_hug": "面部色调", + "default": "預設" + }, + "settings": { + "armor": "裝甲", + "green_penetration": "击穿概率", + "hide_model_under_armor": "隐藏装甲下的模型", + "opaque": "不透明", + "dev_wireframe": "设备:线程", + "environment": "环境", + "show_grid": "显示网格" + }, + "screenshot": { + "download": "下載", + "copy": "複製" + }, + "model": { + "name": "模型" + }, + "dynamic": { + "name": "動態裝甲", + "custom_shell": { + "type": "戰車類型", + "caliber": "口徑", + "penetration": "穿深", + "normalization": "轉正", + "ricochet": "跳彈", + "explosion_radius": "爆炸半徑", + "exit": "退出", + "reset": "重置" + }, + "advanced": "進階高亮顯示", + "shooter": "主視角射擊者:", + "shot_card": { + "element": { + "primary": "主裝甲", + "spaced": "間隙裝甲", + "external": "外部模組", + "gap": "空隙", + "gun": "主炮", + "track": "履帶" + }, + "status": { + "penetration": "%s生命值 擊穿", + "blocked": "阻擋傷害", + "ricochet": "跳彈", + "splash": "%s生命值 爆炸範圍" + }, + "stats": { + "thickness_and_angle": "等效%s毫米 %s2度", + "nominal": "%s毫米 裝甲厚度", + "ricochet_loss": "%s1%機率擊穿" + } + } + }, + "static": { + "name": "静態裝甲", + "primary": "主裝甲", + "spaced": "間隙裝甲", + "external": "外部模組", + "edit": { + "label": "編輯", + "reset": "重置" + } + } + }, + "meta": { + "back": "返回", + "compare": "比較", + "swap": { + "button": "調換", + "title": "調換戰車" + }, + "vanilla": "原版", + "opentest": "開放測試", + "full_name": "全名", + "nation": "國別", + "class": "戰車分類", + "tier": "等級", + "type": "戰車類型", + "dev_id": "開發人員ID", + "purchase": "購買價格", + "restoration": "恢復戰車價格", + "sale": "出售價格", + "research": "研發所需經驗" + }, + "tech_tree": { + "title": "科技樹", + "battle": "一場比賽", + "battles": "%s1場比賽", + "route": "路線 %s1 的 %s2" + }, + "game_modes": { + "title": "路線 %s1 的 %s2" + }, + "review": { + "title": "重播影片", + "disclaimer": "BlitzKit 不支援任何影片重播。", + "join": "应用到 %s1 YouTuber程序" + }, + "configuration": { + "title": "配置設定", + "modules": { + "title": "功能模組", + "stock": "庫存", + "upgrade": "升級" + }, + "equipment": { + "title": "裝備", + "clear": "清除" + }, + "provisions": { + "title": "補給品", + "clear": "清除" + }, + "consumables": { + "title": "消耗品", + "clear": "清除", + "info": "黄色消耗品不影响统计数据" + }, + "skills": { + "title": "乘員技能", + "clear": "清除", + "maximize": "最大化", + "info": "黄色技能只适用于特殊情况。按空格键可在0到7级之间快速切换。" + }, + "miscellaneous": { + "title": "雜項" + } + }, + "characteristics": { + "title": "属性", + "compare": { + "label": "比较", + "class": "%s1 %s2 级", + "tier": "所有级别 %s1", + "all": "所有坦克" + }, + "values": { + "shell_index": "炮弹 %s1", + "at_distance": "在 %s1m", + "at_distance_slider_label": "距离", + "dispersion_at_distance": "%s1公尺擴散度", + "gun_flexibility": "炮控灵活度", + "speed": "速度", + "terrain_coefficients": "地势系数", + "raw_terrain_coefficients": "原始地形系数", + "power_to_weight_ratio": "推重比", + "hull_traverse_speed": "炮塔旋转速度", + "camouflage": "偽裝", + "reload": "裝填", + "crewCount": "乘员人数", + "shellNormalization": "轉正", + "shellRicochet": "跳彈", + "dpm": "每分鍾傷害血量", + "dpmEffective": "60秒内生效", + "shells": "砲彈", + "mostOptimalShellIndex": "最优炮弹", + "shellReloads": "炮弹重装", + "shellReload": "炮弹重装", + "intraClip": "内置素材", + "caliber": "口徑", + "penetration": "穿深", + "damage": "傷害", + "clipDamage": "爬坡性能", + "moduleDamage": "模組傷害", + "explosionRadius": "爆炸半徑", + "shellVelocity": "砲彈飛行速度", + "aimTime": "瞄準時間", + "dispersion": "静止時", + "dispersionMoving": "移動時", + "dispersionHullTraversing": "車身轉向時", + "dispersionTurretTraversing": "炮塔轉向時", + "dispersionShooting": "开火时", + "dispersionGunDamaged": "主炮損壞", + "gunDepression": "俯角", + "gunElevation": "仰角", + "gunFrontalDepression": "車輛前部俯角", + "gunFrontalElevation": "車輛前部仰角", + "gunRearDepression": "車輛後部俯角", + "gunRearElevation": "車輛後部俯角", + "azimuthLeft": "左射界", + "azimuthRight": "右射界", + "speedForwards": "前進最高速度", + "speedBackwards": "后退", + "hardTerrainCoefficient": "坚硬的", + "hardTerrainCoefficientRaw": "坚硬的", + "mediumTerrainCoefficient": "中型戰車", + "mediumTerrainCoefficientRaw": "中型戰車", + "softTerrainCoefficient": "柔软的", + "softTerrainCoefficientRaw": "軟的", + "enginePower": "引擎功率", + "powerToWeightRatioHardTerrain": "硬的", + "powerToWeightRatioMediumTerrain": "中型戰車", + "powerToWeightRatioSoftTerrain": "柔软的", + "weight": "重量", + "turretTraverseSpeed": "炮塔迴轉速度", + "gunTraverseSpeed": "炮塔旋转速度", + "hullTraverseHardTerrain": "坚硬的", + "hullTraverseMediumTerrain": "中型戰車", + "hullTraverseSoftTerrain": "柔软的", + "health": "生命值", + "fireChance": "着火概率", + "viewRange": "可視範圍", + "camouflageStill": "靜止時", + "camouflageMoving": "移動時", + "camouflageShootingStill": "静止時", + "camouflageShootingMoving": "移動時", + "camouflageCaughtOnFire": "被击中", + "width": "宽度", + "height": "高度", + "length": "長度", + "volume": "體積", + "shellRange": "射程", + "shellCapacity": "彈藥容量", + "gunType": "炮管類型", + "penetrationAt250m": "兩百五十公尺處的穿透力" + } + }, + "firepower": { + "title": "火力" + }, + "crew": { + "title": "乘員訓練", + "info": "代表車組成員可以擔任的其他角色。", + "count": "乘員人數", + "commander": "車長", + "gunner": "炮手", + "driver": "駕駛員", + "radioman": "無線電操作員", + "loader": "裝填手" + }, + "maneuverability": { + "title": "機動性" + }, + "survivability": { + "title": "生存性" + } + }, + "compare": { + "name": "比較", + "button": "戰車比較", + "description": "比較戰車統計資料", + "seo_description": "比較《戰車世界:閃擊戰》中的戰車統計資料和裝備配置", + "actions": { + "add": { + "button": "增加...", + "title": "增加戰車", + "description": "選擇要加入比較的戰車" + }, + "clear": "清除", + "deltas": { + "none": "無變化", + "percentage": "百分比", + "absolute": "絕對" + } + }, + "no_tanks": { + "title": "未選擇戰車", + "body": "按下 Add 按鈕來開始" + }, + "table": { + "equipment": { + "clear": "清除", + "apply_to_all": "套用所有" + }, + "skills": { + "clear": "清空", + "maximize": "最大化" + }, + "modules": { + "stock": "库存量", + "upgrade": "升级" + }, + "provisions": { + "clear": "清空" + }, + "consumables": { + "clear": "清空" + } + } + }, + "performance": { + "name": "性能表现", + "button": "查看坦克", + "description": "戰車性能統計", + "seo_description": "最好的戰車及其在戰車世界閃擊戰中的表現", + "estimation": { + "body": "基于 %s1个玩家的估计数据。更新 %s2 之前。", + "hours": "%s1 小時", + "minutes": "%s1 分鐘" + }, + "player_count": { + "label": "玩家数量" + }, + "table": { + "tanks": { + "header": "戰車", + "total": "全部" + }, + "stats": { + "winrate": "勝率", + "players": "玩家", + "damage": "傷害", + "survival": "生存性", + "xp": "經驗值", + "kills": "擊殺數", + "spots": "被敵方偵查次數", + "accuracy": "命中率", + "shots": "開火次數", + "hits": "命中數", + "damage_ratio": "伤害效率", + "damage_taken": "伤害值", + "capture_points": "佔領點數" + } + } + }, + "gallery": { + "name": "圖庫", + "button": "瀏覽所有", + "description": "Blitz 玩家頭像", + "seo_description": "所有戰車世界閃擊戰玩家頭像", + "last_updated": "最後更新於 %s1", + "search": { + "hint": "搜尋頭像……", + "results": "%s1 頭像" + } + }, + "session": { + "name": "場次", + "button": "開始追蹤", + "description": "即時場次統計", + "seo_description": "在遊玩 戰車世界閃擊戰 時,實時追蹤你的統計數據", + "search": { + "lookup": "查詢玩家以開始使用", + "hint": "搜尋玩家……", + "searching": "搜尋中……", + "no_results": "找不到玩家" + }, + "controls": { + "title": "追蹤 %s1", + "subtitle": "起始於 %s1", + "reset": "重置", + "change": "變更" + }, + "no_cc": { + "title": "你正在嘗試追蹤一個 CC 帳號", + "body": "CC(社群貢獻者)帳號沒有公開的戰車統計數據。我們正在為 CC 帳號添加嵌入功能,敬請期待。", + "alright": "好的" + }, + "no_battles": { + "title": "沒有戰鬥", + "hint": "開始遊戲吧" + }, + "table": { + "tank": "戰車", + "total": "全部", + "remove": "移除" + } + }, + "tier_list": { + "name": "階級列表", + "button": "排名戰車", + "description": "戰車世界閃擊戰 戰車等級排名", + "seo_description": "探索根據玩家評分和統計表現,從最佳到最差排名的《戰車世界:閃擊戰》戰車等級列表", + "buttons": { + "reset": "重置", + "share": "分享" + } + }, + "embed": { + "name": "嵌入", + "button": "現在創建", + "description": "串流嵌入功能", + "seo_description": "創建具有極高自訂化的《戰車世界:閃擊戰》串流嵌入功能", + "coming_soon": "更多嵌入功能即將推出。", + "suggest": "建議與想法。", + "preview": { + "title": "範例", + "upload": "上傳測試背景" + }, + "configuration": { + "import": { + "title": "導入", + "description": "如果您已经有一个要导入您的设置,请粘贴您当前的 URL", + "hint": "貼上 URL……", + "apply": "套用", + "idiot_title": "無效的 URL", + "idiot_description": "試著貼上 BlitzKit 先前生成的網址吧,你這隻傻鵝!", + "idiot_button": "好吧,我的錯" + }, + "export": { + "title": "匯出", + "description": "為你的串流軟體複製所有屬性", + "generate": { + "button": "生成網址", + "title": "生成網址", + "subtitle": "搜尋帳號以進行追蹤", + "cancel": "取消", + "success": { + "title": "複製 URL", + "info": "已為 %s1 生成網址。當網址貼上後,嵌入功能將開始追蹤。您可以隨時點擊右鍵 \"PC\" 或長按 \"手機\" 並選擇「重設」來重置。", + "back": "返回", + "copy": "複製 URL" + } + }, + "css": "複製 CSS", + "width": "複製寬度", + "height": "複製高度", + "framerate": "複製幀率" + }, + "customize": { + "title": "自訂", + "reset": "重置", + "description": "嵌入內容的各項附加內容" + } + }, + "types": { + "breakdown": { + "name": "詳細分析", + "options": { + "width": "寬度", + "height": "高度", + "showTotal": "顯示總和", + "listGap": "項目間隔", + "listMaxTanks": "最多戰車數量", + "cardRadius": "資訊卡半徑", + "cardHeaderBackgroundColor": "資訊卡標題背景顏色", + "cardHeaderPadding": "資訊卡標題內間距", + "cardTitle": "資訊卡標題", + "cardTitleTypeColor": "資訊卡標題形式顏色", + "cardTitleClassIcon": "資訊卡標題類別圖示", + "cardBodyBackgroundColor": "資訊卡本體背景顏色", + "cardBodyPadding": "資訊卡內容區間距", + "columnGap": "欄位間距", + "columnValue": "欄位值", + "columnLabel": "欄位標籤", + "column1": "欄位1", + "column2": "欄位2", + "column3": "欄位3", + "column4": "欄位4", + "column1CustomLabel": "欄位1標籤", + "column2CustomLabel": "欄位2標籤", + "column3CustomLabel": "欄位3標籤" + } + } + } + }, + "charts": { + "name": "圖表", + "button": "瘋狂研究", + "description": "大量圖標", + "seo_description": "有關 戰車世界閃擊戰 戰車、玩家與事件的各種圖表與圖形" + }, + "rating": { + "name": "評價戰鬥", + "button": "查看排行榜", + "description": "完整評價戰鬥排行榜", + "seo_description": "《戰車世界:閃擊戰》即時與歷史完整評分排行榜" + }, + "discord": { + "name": "Discord 機器人", + "button": "現在安裝", + "description": "直接在 Discord 上查看統計資料", + "seo_description": "透過 BlitzKit 的 Discord 機器人,直接在 Discord 上獲取統計資料" + }, + "more": { + "name": "更多內容即將推出", + "button": "加入我們的 Discord", + "description": "我們正在製作的內容", + "seo_description": "我們正在與 BlitzKit 一起製作的內容" + }, + "players": { + "name": "玩家", + "button": "查詢玩家", + "description": "定期玩家統計", + "seo_description": "戰車世界閃擊戰 定期玩家統計" + }, + "home": { + "name": "首頁", + "button": "開始使用", + "description": "開始使用 BlitzKit", + "seo_description": "開始使用 BlitzKit" + }, + "blitz_analysis": { + "name": "閃擊戰分析", + "button": "現在分析", + "description": "豐富的歷史平均統計資料", + "seo_description": "戰車世界閃擊戰中的歷史平均統計資料" + }, + "blitz_insider": { + "name": "閃擊戰內部", + "button": "今日探索", + "description": "遊戲內場次統計", + "seo_description": "戰車世界閃擊戰內的遊戲場次統計" + } + }, + "footer": { + "motto": "戰車世界閃擊戰全方位資訊", + "copyright": "本網站與 Wargaming 戰遊網 或任何服務無關", + "about": { + "title": "關於", + "credits": "製作名單", + "discord": "Discord", + "github": "GitHub", + "patches": "修補更新" + }, + "legal": { + "title": "法律資訊", + "privacy": "隐私条款", + "terms": "服务条款" + } + } + } +} diff --git a/packages/i18n/vite-env.d.ts b/packages/i18n/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/packages/i18n/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/scripts/package.json b/packages/scripts/package.json index f62b1ebb5..c2257f005 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -10,10 +10,12 @@ "build:reviews": "bun src/reviews.ts", "build:ids": "bun src/discoverIds.ts", "build:bot": "bun src/buildBot.ts", - "build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts" + "build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts", + "prunestrings": "bun src/pruneStrings.ts" }, "dependencies": { "@blitzkit/core": "workspace:*", + "@blitzkit/i18n": "workspace:*", "@gltf-transform/core": "^4.0.10", "@gltf-transform/extensions": "^4.0.10", "@gltf-transform/functions": "^4.0.10", diff --git a/packages/scripts/src/applyPatches.ts b/packages/scripts/src/applyPatches.ts index a4fd58e83..e2b3d2641 100644 --- a/packages/scripts/src/applyPatches.ts +++ b/packages/scripts/src/applyPatches.ts @@ -9,7 +9,7 @@ import { DATA } from './buildAssets/constants'; import { readYAMLDVPL } from './core/blitz/readYAMLDVPL'; import { writeDVPL } from './core/blitz/writeDVPL'; -const versionTextFile = await readStringDVPL(`${DATA}/version.txt.dvpl`); +const versionTextFile = await readStringDVPL(`${DATA}/version.txt`); const currentVersion = versionTextFile .split(' ')[0] .split('.') @@ -45,19 +45,27 @@ while (true) { files.length, ); - await Promise.all( - files.map(async ({ path, data }) => { - const { dir } = parsePath(path); + for (const { path, data } of files) { + console.log(`Patching "${path}"...`); + const { dir } = parsePath(path); + + try { await mkdir(`${DATA}/${dir}`, { recursive: true }); - await writeFile( - `${DATA}/${path}.dvpl`, - new Uint8Array(writeDVPL(Buffer.from(data))), - ); + } catch (error) { + console.warn(`Failed to make directory "${dir}"`); + } - bar.tick(); - }), - ); + const isDvpl = path.endsWith('.dvpl'); + const buffer = Buffer.from(data); + + await writeFile( + `${DATA}/${path}${isDvpl ? '' : '.dvpl'}`, + new Uint8Array(isDvpl ? buffer : writeDVPL(buffer)), + ); + + bar.tick(); + } if ('dynamicContentLocalizationsDir' in data) { console.log('Found dynamic content localizations; patching...'); @@ -69,13 +77,13 @@ while (true) { ); const newStrings = parseYaml(await localizationsResponse.text()); const oldStrings = await readYAMLDVPL>( - `${DATA}/Strings/en.yaml.dvpl`, + `${DATA}/Strings/en.yaml`, ); const patchedStrings = { ...oldStrings, ...newStrings }; const patchedContent = stringifyYaml(patchedStrings); await writeFile( - `${DATA}/Strings/en.yaml.dvpl`, + `${DATA}/Strings/en.yaml`, new Uint8Array(writeDVPL(Buffer.from(patchedContent))), ); } diff --git a/packages/scripts/src/buildAssets/boosterIcons.ts b/packages/scripts/src/buildAssets/boosterIcons.ts index 27710363f..d23233290 100644 --- a/packages/scripts/src/buildAssets/boosterIcons.ts +++ b/packages/scripts/src/buildAssets/boosterIcons.ts @@ -8,16 +8,16 @@ import { DATA } from './constants'; export async function boosterIcons() { const boosterFiles = (await readdir(`${DATA}/Gfx/Shared/boosters`)).filter( - (file) => !file.endsWith('@2x.txt.dvpl') && !file.startsWith('texture0'), + (file) => !file.includes('@2x.txt') && !file.startsWith('texture0'), ); const image = sharp( - await readDVPLFile(`${DATA}/Gfx/Shared/boosters/texture0.packed.webp.dvpl`), + await readDVPLFile(`${DATA}/Gfx/Shared/boosters/texture0.packed.webp`), ); const changes: FileChange[] = []; await Promise.all( boosterFiles.map(async (file) => { - const name = file.match(/booster_(.+).txt.dvpl/)?.[1]; + const name = file.match(/booster_(.+).txt/)?.[1]; if (name === undefined) return; diff --git a/packages/scripts/src/buildAssets/camouflageIcons.ts b/packages/scripts/src/buildAssets/camouflageIcons.ts index 6035876d5..88b17057a 100644 --- a/packages/scripts/src/buildAssets/camouflageIcons.ts +++ b/packages/scripts/src/buildAssets/camouflageIcons.ts @@ -6,7 +6,7 @@ import { DATA } from './constants'; export async function camouflageIcons() { console.log('Building camouflage icons...'); const content = await readDVPLFile( - `${DATA}/Gfx/UI/Hangar/IconCamouflage.packed.webp.dvpl`, + `${DATA}/Gfx/UI/Hangar/IconCamouflage.packed.webp`, ); const changes: FileChange[] = [ { diff --git a/packages/scripts/src/buildAssets/constants.ts b/packages/scripts/src/buildAssets/constants.ts index 9954fe7f6..d1ab4aff3 100644 --- a/packages/scripts/src/buildAssets/constants.ts +++ b/packages/scripts/src/buildAssets/constants.ts @@ -1,11 +1,33 @@ import { readdir } from 'fs/promises'; import { argv } from 'process'; +const WINDOWS_APPS = 'C:/Program Files/WindowsApps'; +const wotbPackageRegex = /7458BE2C\..+_\d+(\.\d+)+_x64__.+/; + const isDepot = argv.includes('--depot'); -export const DATA = isDepot - ? await (async () => { - const [installationVersion] = await readdir(`../../depots/444202`); - return `../../depots/444202/${installationVersion}/Data`; - })() - : 'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data'; +let data: string; +let compressed: boolean; + +if (isDepot) { + const [installationVersion] = await readdir(`../../depots/444202`); + data = `../../depots/444202/${installationVersion}/Data`; + compressed = true; +} else { + const directories = await readdir(WINDOWS_APPS).then((files) => + files.filter((file) => wotbPackageRegex.test(file)), + ); + + if (directories.length !== 1) { + throw new Error( + `Expected 1 directory for WoTB Windows App, got ${directories.length}.`, + ); + } + + const [windowsApp] = directories; + data = `${WINDOWS_APPS}/${windowsApp}/Data`; + compressed = false; +} + +export const DATA = data; +export const DATA_COMPRESSED = compressed; diff --git a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts b/packages/scripts/src/buildAssets/consumableProvisionIcons.ts index e8e5afa3c..137bd3a30 100644 --- a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts +++ b/packages/scripts/src/buildAssets/consumableProvisionIcons.ts @@ -34,21 +34,19 @@ export async function consumableProvisionIcons() { const styleSheets = await Promise.all( styles.map(async (path) => Object.values( - (await readYAMLDVPL(`${DATA}/${path}.dvpl`)).StyleSheets, + (await readYAMLDVPL(`${DATA}/${path}`)).StyleSheets, ), ), ).then((array) => array.flat()); for (const match of ( await readStringDVPL( - `${DATA}/XML/item_defs/vehicles/common/consumables/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`, ) ).matchAll(listItemsPattern)) { const consumablesCommon = await readXMLDVPL<{ root: ConsumablesCommon; - }>( - `${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml.dvpl`, - ); + }>(`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`); await Promise.all( Object.values(consumablesCommon.root).map(async (consumable) => { @@ -70,9 +68,9 @@ export async function consumableProvisionIcons() { .replace('.psd', '') .replace('.txt', ''); - if (existsSync(`${DATA}/${configPath}.packed.webp.dvpl`)) { + if (existsSync(`${DATA}/${configPath}.packed.webp`)) { const image = sharp( - await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`), + await readDVPLFile(`${DATA}/${configPath}.packed.webp`), ); const content = await image.trim({ threshold: 100 }).toBuffer(); @@ -83,10 +81,10 @@ export async function consumableProvisionIcons() { } else { const consumablesTexture = sharp( await readDVPLFile( - `${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp.dvpl`, + `${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp`, ), ); - const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`)) + const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`)) .split('\n')[4] .split(' ') .map(Number); @@ -111,12 +109,12 @@ export async function consumableProvisionIcons() { for (const match of ( await readStringDVPL( - `${DATA}/XML/item_defs/vehicles/common/provisions/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`, ) ).matchAll(listItemsPattern)) { const provisionsCommon = await readXMLDVPL<{ root: ProvisionsCommon; - }>(`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml.dvpl`); + }>(`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`); await Promise.all( Object.values(provisionsCommon.root).map(async (provision) => { const styleSheet = styleSheets.find((styleSheet) => @@ -137,9 +135,9 @@ export async function consumableProvisionIcons() { .replace('.psd', '') .replace('.txt', ''); - if (existsSync(`${DATA}/${configPath}.packed.webp.dvpl`)) { + if (existsSync(`${DATA}/${configPath}.packed.webp`)) { const image = sharp( - await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`), + await readDVPLFile(`${DATA}/${configPath}.packed.webp`), ); const content = await image.trim({ threshold: 100 }).toBuffer(); @@ -150,10 +148,10 @@ export async function consumableProvisionIcons() { } else { const provisionsTexture = sharp( await readDVPLFile( - `${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp.dvpl`, + `${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp`, ), ); - const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`)) + const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`)) .split('\n')[4] .split(' ') .map(Number); diff --git a/packages/scripts/src/buildAssets/currencies.ts b/packages/scripts/src/buildAssets/currencies.ts index 836ad0903..ad071f090 100644 --- a/packages/scripts/src/buildAssets/currencies.ts +++ b/packages/scripts/src/buildAssets/currencies.ts @@ -23,7 +23,7 @@ export async function currencies() { await Promise.all( ICONS.map(async ([file, name]) => { const content = await sharp( - await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}.dvpl`), + await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}`), ) .trim() .toBuffer(); diff --git a/packages/scripts/src/buildAssets/definitions.ts b/packages/scripts/src/buildAssets/definitions.ts index d356268bf..5d6ff2439 100644 --- a/packages/scripts/src/buildAssets/definitions.ts +++ b/packages/scripts/src/buildAssets/definitions.ts @@ -1,6 +1,5 @@ import { Armor, - assertSecret, BlitzTankClass, CamouflageDefinitions, Consumable, @@ -14,6 +13,7 @@ import { GameDefinitions, GunDefinition, GunDefinitionBase, + I18nString, MapDefinitions, ModelDefinitions, ModuleType, @@ -31,6 +31,11 @@ import { Unlock, Vector3, } from '@blitzkit/core'; +import { + DEFAULT_LOCALE, + SUPPORTED_LOCALE_BLITZ_MAP, + SUPPORTED_LOCALES, +} from '@blitzkit/i18n'; import { readdir } from 'fs/promises'; import { parse as parsePath } from 'path'; import { Vector3Tuple } from 'three'; @@ -212,7 +217,7 @@ interface VehicleDefinitions { circularVisionRadius: number; maxHealth: number; armor: VehicleDefinitionArmor; - userString: number; + userString: string; level: number; yawLimits: string | string[]; gunPosition: string | string[]; @@ -497,9 +502,6 @@ const blitzShellKindToBlitzkit: Record = { HIGH_EXPLOSIVE: ShellType.HE, HOLLOW_CHARGE: ShellType.HEAT, }; -const missingStrings: Record = { - '#artefacts:tungstentip/name': 'Tungsten Shells', -}; export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU(R?)))$/; const blitzModuleTypeToBlitzkit: Record = { chassis: ModuleType.TRACKS, @@ -512,20 +514,10 @@ const blitzModuleTypeToBlitzkit: Record = { export async function definitions() { console.log('Building definitions...'); - const wargamingTankopedia = (await fetch( - `https://api.wotblitz.com/wotb/encyclopedia/vehicles/?application_id=${assertSecret( - import.meta.env.PUBLIC_WARGAMING_APPLICATION_ID, - )}&fields=description`, - ).then((response) => response.json())) as { - data: { [key: number]: { description: null | string } }; - }; - const gameDefinitions: GameDefinitions = { - version: (await readStringDVPL(`${DATA}/version.txt.dvpl`)).split(' ')[0], + version: (await readStringDVPL(`${DATA}/version.txt`)).split(' ')[0], nations: ( - await readYAMLDVPL( - `${DATA}/available_nations.yaml.dvpl`, - ) + await readYAMLDVPL(`${DATA}/available_nations.yaml`) ).available_nations, gameModes: {}, roles: {}, @@ -552,30 +544,67 @@ export async function definitions() { (nations) => nations.filter((nation) => nation !== 'common'), ); const tankStringIdMap: Record = {}; - const stringsPreInstalled = await readYAMLDVPL( - `${DATA}/Strings/en.yaml.dvpl`, - ); const stringsCache = await fetch( 'https://stufficons.wgcdn.co/localizations/en.yaml', ) .then((response) => response.text()) .then((string) => parseYaml(string) as BlitzStrings); const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>( - `${DATA}/XML/item_defs/vehicles/common/optional_devices.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`, ); - const strings = { - ...stringsPreInstalled, - ...stringsCache, - }; + const stringsI18n: Record> = {}; + + await Promise.all( + SUPPORTED_LOCALES.map(async (locale) => { + const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale]; + const cache = await fetch( + `https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`, + ) + .then((response) => response.text()) + .then((string) => parseYaml(string) as BlitzStrings); + const preInstalled = await readYAMLDVPL( + `${DATA}/Strings/${blitzLocale}.yaml`, + ); + + stringsI18n[locale] = { + ...cache, + ...preInstalled, + }; + + console.log(`Loaded strings for ${locale}`); + }), + ); + + function getString(name: string) { + const locales: Record = { + [DEFAULT_LOCALE]: stringsI18n[DEFAULT_LOCALE][name], + }; + + for (const locale of SUPPORTED_LOCALES) { + const localizedString = stringsI18n[locale][name]; + + if ( + localizedString === undefined || + localizedString === locales[DEFAULT_LOCALE] + ) { + continue; + } + + locales[locale] = localizedString; + } + + return { locales } satisfies I18nString; + } + const optionalDeviceSlots = await readXMLDVPL<{ root: OptionalDeviceSlots; - }>(`${DATA}/XML/item_defs/vehicles/common/optional_device_slots.xml.dvpl`); + }>(`${DATA}/XML/item_defs/vehicles/common/optional_device_slots.xml`); const consumables: ConsumablesCommon = {}; const provisions: ProvisionsCommon = {}; for (const match of ( await readStringDVPL( - `${DATA}/XML/item_defs/vehicles/common/consumables/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`, ) ).matchAll(//g)) { if (match[1] === 'prototypes') continue; @@ -584,7 +613,7 @@ export async function definitions() { consumables, ( await readXMLDVPL<{ root: ConsumablesCommon }>( - `${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`, ) ).root, ); @@ -592,7 +621,7 @@ export async function definitions() { for (const match of ( await readStringDVPL( - `${DATA}/XML/item_defs/vehicles/common/provisions/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`, ) ).matchAll(//g)) { if (match[1] === 'prototypes') continue; @@ -601,33 +630,33 @@ export async function definitions() { provisions, ( await readXMLDVPL<{ root: ConsumablesCommon }>( - `${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`, ) ).root, ); } const avatar = await readXMLDVPL<{ root: Avatar }>( - `${DATA}/XML/item_defs/tankmen/avatar.xml.dvpl`, + `${DATA}/XML/item_defs/tankmen/avatar.xml`, ); - const maps = await readYAMLDVPL(`${DATA}/maps.yaml.dvpl`); + const maps = await readYAMLDVPL(`${DATA}/maps.yaml`); const tankXps = new Map(); const camouflagesXml = await readXMLDVPL<{ root: CamouflagesXML }>( - `${DATA}/XML/item_defs/vehicles/common/camouflages.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/camouflages.xml`, ); const camouflagesYaml = await readYAMLDVPL( - `${DATA}/camouflages.yaml.dvpl`, + `${DATA}/camouflages.yaml`, ); const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages); const squadBattleTypeStyles = await readYAMLDVPL( - `${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml.dvpl`, + `${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`, ); const gameTypeSelectorStyles = await readYAMLDVPL( - `${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml.dvpl`, + `${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`, ); const gameModeNativeNames: Record = {}; const combatRoles = await readYAMLDVPL( - `${DATA}/XML/item_defs/vehicles/common/combat_roles.yaml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/combat_roles.yaml`, ); const consumableNativeNames: Record = {}; const provisionNativeNames: Record = {}; @@ -636,7 +665,7 @@ export async function definitions() { /"(\d+)" -> "(battleType\/([a-zA-Z]+))"/g, )) { const id = Number(match[1]); - const name = strings[match[2]]; + const name = getString(match[2]); gameModeNativeNames[match[3]] = id; gameDefinitions.gameModes[id] = { @@ -657,10 +686,10 @@ export async function definitions() { camouflagesXmlEntries.forEach(([camoKey, camo]) => { const yamlEntry = camouflagesYaml[camoKey]; const fullName = yamlEntry.userString - ? strings[yamlEntry.userString] + ? getString(yamlEntry.userString) : undefined; const shortName = yamlEntry.shortUserString - ? strings[yamlEntry.shortUserString] + ? getString(yamlEntry.shortUserString) : undefined; const resolvedTankName = shortName ?? fullName; const resolvedTankNameFull = @@ -668,7 +697,7 @@ export async function definitions() { camouflageDefinitions.camouflages[camo.id] = { id: camo.id, - name: strings[camo.userString], + name: getString(camo.userString), tank_name: resolvedTankName, tank_name_full: resolvedTankNameFull, }; @@ -677,29 +706,23 @@ export async function definitions() { await Promise.all( nations.map(async (nation) => { const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>( - `${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/${nation}/list.xml`, ); const turretList = await readXMLDVPL<{ root: TurretDefinitionsList; - }>( - `${DATA}/XML/item_defs/vehicles/${nation}/components/turrets.xml.dvpl`, - ); + }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/turrets.xml`); const gunList = await readXMLDVPL<{ root: GunDefinitionsList; - }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/guns.xml.dvpl`); + }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/guns.xml`); const shellList = await readXMLDVPL<{ root: ShellDefinitionsList; - }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/shells.xml.dvpl`); + }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/shells.xml`); const enginesList = await readXMLDVPL<{ root: EngineDefinitionsList; - }>( - `${DATA}/XML/item_defs/vehicles/${nation}/components/engines.xml.dvpl`, - ); + }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/engines.xml`); const chassisList = await readXMLDVPL<{ root: ChassisDefinitionsList; - }>( - `${DATA}/XML/item_defs/vehicles/${nation}/components/chassis.xml.dvpl`, - ); + }>(`${DATA}/XML/item_defs/vehicles/${nation}/components/chassis.xml`); function resolveUnlocks(unlocks?: BlitzModuleType) { if (!unlocks) return []; @@ -751,10 +774,10 @@ export async function definitions() { const tank = tankList.root[tankKey]; let tankPrice: TankPrice; const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>( - `${DATA}/XML/item_defs/vehicles/${nation}/${tankKey}.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/${nation}/${tankKey}.xml`, ); const tankParameters = await readYAMLDVPL( - `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`, + `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, ); const turretOrigin = tankDefinition.root.hull.turretPositions.turret .split(' ') @@ -866,8 +889,6 @@ export async function definitions() { id: tankId, roles: {}, camouflages: camouflages, - description: - wargamingTankopedia.data[tankId]?.description ?? undefined, fixed_camouflage: fixedCamouflage, crew, weight: tankDefinition.root.hull.weight, @@ -880,9 +901,9 @@ export async function definitions() { max_provisions: tankDefinition.root.provisionSlots, name: (tank.shortUserString - ? strings[tank.shortUserString] - : undefined) ?? strings[tank.userString], - name_full: strings[tank.userString], + ? getString(tank.shortUserString) + : undefined) ?? getString(tank.userString), + name_full: getString(tank.userString), nation, type: tankTags.includes('collectible') ? TankType.COLLECTOR @@ -959,7 +980,7 @@ export async function definitions() { tankDefinitions.tanks[tankId].tracks.push({ id: trackId, weight: track.weight, - name: strings[track.userString], + name: getString(track.userString), traverse_speed: track.rotationSpeed, dispersion_move: track.shotDispersionFactors.vehicleMovement, dispersion_traverse: track.shotDispersionFactors.vehicleRotation, @@ -987,7 +1008,7 @@ export async function definitions() { totalUnlocks.push(engine.unlocks); tankDefinitions.tanks[tankId].engines.push({ id: engineId, - name: strings[engineListEntry.userString], + name: getString(engineListEntry.userString), fire_chance: engineListEntry.fireStartingChance, tier: engineListEntry.level, weight: engineListEntry.weight, @@ -1041,14 +1062,12 @@ export async function definitions() { } }); + turret.userString; + tankDefinitions.tanks[tankId].turrets.push({ id: turretId, traverse_speed: turret.rotationSpeed, - name: - strings[turret.userString] ?? - turretKey - .replaceAll('_', ' ') - .replace(/^(Turret ([0-9] )?)+/, ''), + name: getString(turret.userString), tier: turret.level, guns: [], health: turret.maxHealth, @@ -1100,8 +1119,7 @@ export async function definitions() { const gunModel = Number( parsePath(gun.models.undamaged).name.split('_')[1], ); - const gunName = - strings[gunListEntry.userString] ?? gunKey.replaceAll('_', ' '); + const gunName = getString(gunListEntry.userString); const gunType = 'clip' in gun ? gun.pumpGunMode @@ -1250,8 +1268,7 @@ export async function definitions() { const gunShellEntry = gunListEntry.shots[shellKey]; const shell = shellList.root[shellKey]; const shellId = toUniqueId(nation, shell.id); - const shellName = - strings[shell.userString] ?? shellKey.replaceAll('_', ' '); + const shellName = getString(shell.userString); const penetrationRaw = gunShellEntry.piercingPower .split(' ') .filter((penetrationString) => penetrationString !== '') @@ -1385,8 +1402,8 @@ export async function definitions() { if (optionalDeviceKey === 'nextAvailableId') return; equipmentDefinitions.equipments[optionalDeviceEntry.id] = { - name: strings[optionalDeviceEntry.userString], - description: strings[optionalDeviceEntry.description], + name: getString(optionalDeviceEntry.userString), + description: getString(optionalDeviceEntry.description), }; }, ); @@ -1418,10 +1435,7 @@ export async function definitions() { game_mode_exclusive: 'gameModeFilter' in consumable, cooldown: consumable.script.cooldown, duration: consumable.script.duration, - name: - strings[consumable.userString] ?? - missingStrings[consumable.userString] ?? - `Unknown ${consumable.id}`, + name: getString(consumable.userString), exclude: [], include: [], }; @@ -1523,10 +1537,7 @@ export async function definitions() { exclude: [], include: [], game_mode_exclusive: 'gameModeFilter' in provision, - name: - strings[provision.userString] ?? - missingStrings[provision.userString] ?? - `Unknown ${provision.id}`, + name: getString(provision.userString), }; provisionDefinitions.provisions[provision.id] = entry; @@ -1650,7 +1661,7 @@ export async function definitions() { Object.entries(maps.maps).forEach(([key, map]) => { mapDefinitions.maps[map.id] = { id: map.id, - name: strings[`#maps:${key}:${map.localName}`], + name: getString(`#maps:${key}:${map.localName}`), }; }); diff --git a/packages/scripts/src/buildAssets/equipmentIcons.ts b/packages/scripts/src/buildAssets/equipmentIcons.ts index 762ab7b6d..e4cebc5a8 100644 --- a/packages/scripts/src/buildAssets/equipmentIcons.ts +++ b/packages/scripts/src/buildAssets/equipmentIcons.ts @@ -22,14 +22,14 @@ export async function equipmentIcons() { const changes: FileChange[] = []; const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>( - `${DATA}/XML/item_defs/vehicles/common/optional_devices.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`, ); const mappings = await readYAMLDVPL( - `${DATA}/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml.dvpl`, + `${DATA}/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`, ); const image = sharp( await readDVPLFile( - `${DATA}/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp.dvpl`, + `${DATA}/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`, ), ); @@ -57,7 +57,7 @@ export async function equipmentIcons() { if (configPath.startsWith('Gfx/Lobby')) { const configPathWebp = configPath.replace('.txt', ''); const image = sharp( - await readDVPLFile(`${DATA}/${configPathWebp}.packed.webp.dvpl`), + await readDVPLFile(`${DATA}/${configPathWebp}.packed.webp`), ); const content = await image.trim().toBuffer(); @@ -66,7 +66,7 @@ export async function equipmentIcons() { content, }); } else { - const config = await readStringDVPL(`${DATA}/${configPath}.dvpl`); + const config = await readStringDVPL(`${DATA}/${configPath}`); const sizes = config.split('\n')[4].split(' ').map(Number); const content = await image .clone() diff --git a/packages/scripts/src/buildAssets/flags.ts b/packages/scripts/src/buildAssets/flags.ts index 93f21b10a..2d1c8d134 100644 --- a/packages/scripts/src/buildAssets/flags.ts +++ b/packages/scripts/src/buildAssets/flags.ts @@ -14,7 +14,7 @@ export async function flags() { .filter( (flag) => flag.startsWith('flag_profile-stat_') && - !flag.endsWith('@2x.packed.webp.dvpl'), + !flag.endsWith('@2x.packed.webp'), ) .map(async (flag) => { const image = sharp( @@ -34,7 +34,7 @@ export async function flags() { .filter( (flag) => flag.startsWith('flag_tutor-tank_') && - !flag.endsWith('@2x.packed.webp.dvpl'), + !flag.endsWith('@2x.packed.webp'), ) .map(async (flag) => { const content = await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`); @@ -50,8 +50,7 @@ export async function flags() { (await readdir(`${DATA}/Gfx/Lobby/flags`)) .filter( (flag) => - flag.startsWith('flag_filter_') && - flag.endsWith('@2x.packed.webp.dvpl'), + flag.startsWith('flag_filter_') && flag.endsWith('@2x.packed.webp'), ) .map(async (flag) => { const content = await sharp( diff --git a/packages/scripts/src/buildAssets/gameModeBanners.ts b/packages/scripts/src/buildAssets/gameModeBanners.ts index c30e6b223..7923e6c60 100644 --- a/packages/scripts/src/buildAssets/gameModeBanners.ts +++ b/packages/scripts/src/buildAssets/gameModeBanners.ts @@ -11,10 +11,10 @@ export async function gameModeBanners() { const changes: FileChange[] = []; const gameTypeSelectorStyles = await readYAMLDVPL( - `${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml.dvpl`, + `${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`, ); const squadBattleTypeStyles = await readYAMLDVPL( - `${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml.dvpl`, + `${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`, ); const bannerMatches: { name: string; path: string }[] = []; @@ -38,7 +38,7 @@ export async function gameModeBanners() { } const content = await sharp( - await readDVPLFile(`${DATA}${path}.packed.webp.dvpl`), + await readDVPLFile(`${DATA}${path}.packed.webp`), ) .trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } }) .toBuffer(); diff --git a/packages/scripts/src/buildAssets/moduleIcons.ts b/packages/scripts/src/buildAssets/moduleIcons.ts index 23d240f67..b446a7314 100644 --- a/packages/scripts/src/buildAssets/moduleIcons.ts +++ b/packages/scripts/src/buildAssets/moduleIcons.ts @@ -12,10 +12,10 @@ export async function moduleIcons() { (await readdir(`${DATA}/Gfx/UI/ModulesTechTree`)) .filter( (file) => - !file.endsWith('@2x.packed.webp.dvpl') && file.startsWith('vehicle'), + !file.endsWith('@2x.packed.webp') && file.startsWith('vehicle'), ) .map(async (file) => { - const nameRaw = file.match(/vehicle(.+)\.packed\.webp\.dvpl/)![1]; + const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1]; const name = nameRaw[0].toLowerCase() + nameRaw.slice(1); const content = await sharp( await readDVPLFile(`${DATA}/Gfx/UI/ModulesTechTree/${file}`), diff --git a/packages/scripts/src/buildAssets/shellIcons.ts b/packages/scripts/src/buildAssets/shellIcons.ts index c20f5c12c..0e8d1f81b 100644 --- a/packages/scripts/src/buildAssets/shellIcons.ts +++ b/packages/scripts/src/buildAssets/shellIcons.ts @@ -11,15 +11,15 @@ export async function shellIcons() { const image = sharp( await readDVPLFile( - `${DATA}/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp.dvpl`, + `${DATA}/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`, ), ); const changes = await Promise.all( (await readdir(`${DATA}/Gfx/Shared/tank-supply/ammunition/big`)) - .filter((file) => file.endsWith('_l.txt.dvpl')) + .filter((file) => file.endsWith('_l.txt')) .map(async (file) => { - const name = file.match(/(.+)_l\.txt\.dvpl/)![1]; + const name = file.match(/(.+)_l\.txt/)![1]; const sizes = ( await readStringDVPL( `${DATA}/Gfx/Shared/tank-supply/ammunition/big/${file}`, diff --git a/packages/scripts/src/buildAssets/skillIcons.ts b/packages/scripts/src/buildAssets/skillIcons.ts index 98bb67f5b..191b90969 100644 --- a/packages/scripts/src/buildAssets/skillIcons.ts +++ b/packages/scripts/src/buildAssets/skillIcons.ts @@ -29,13 +29,13 @@ export async function skillIcons() { console.log('Building skill icons...'); const avatar = await readXMLDVPL<{ root: Avatar }>( - `${DATA}/XML/item_defs/tankmen/avatar.xml.dvpl`, + `${DATA}/XML/item_defs/tankmen/avatar.xml`, ); const changes = await Promise.all( Object.values(avatar.root.skills).map(async (skill) => { const icon = Array.isArray(skill.icon) ? skill.icon[0] : skill.icon; const name = icon.name.split('/').at(-1)!.replace(/_\d$/, ''); - const path = `${DATA}${icon.name.replace('~res:', '')}.packed.webp.dvpl`; + const path = `${DATA}${icon.name.replace('~res:', '')}.packed.webp`; const image = sharp(await readDVPLFile(path)).trim(); const content = await image.toBuffer(); diff --git a/packages/scripts/src/buildAssets/tankArmors.ts b/packages/scripts/src/buildAssets/tankArmors.ts index 96b956a8d..fd733144e 100644 --- a/packages/scripts/src/buildAssets/tankArmors.ts +++ b/packages/scripts/src/buildAssets/tankArmors.ts @@ -20,7 +20,7 @@ export async function tankArmors() { await Promise.all( nations.map(async (nation) => { const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>( - `${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/${nation}/list.xml`, ); await Promise.all( diff --git a/packages/scripts/src/buildAssets/tankIcons.ts b/packages/scripts/src/buildAssets/tankIcons.ts index d3421c42d..42d4ba309 100644 --- a/packages/scripts/src/buildAssets/tankIcons.ts +++ b/packages/scripts/src/buildAssets/tankIcons.ts @@ -53,7 +53,7 @@ export async function tankIcons() { await Promise.all( nations.map(async (nation) => { const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>( - `${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/${nation}/list.xml`, ); await Promise.all( @@ -63,14 +63,14 @@ export async function tankIcons() { const nationVehicleId = tank.id; const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; const parameters = await readYAMLDVPL( - `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`, + `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, ); const smallPath = `${DATA}/${parameters.resourcesPath.smallIconPath .replace(/~res:\//, '') - .replace(/\..+/, '')}.packed.webp.dvpl`; + .replace(/\..+/, '')}.packed.webp`; const bigPath = `${DATA}/${parameters.resourcesPath.bigIconPath .replace(/~res:\//, '') - .replace(/\..+/, '')}.packed.webp.dvpl`; + .replace(/\..+/, '')}.packed.webp`; const big = await sharp(await readDVPLFile(bigPath)) .trim() .toBuffer(); diff --git a/packages/scripts/src/buildAssets/tankModels.ts b/packages/scripts/src/buildAssets/tankModels.ts index 9a2a759ad..112889086 100644 --- a/packages/scripts/src/buildAssets/tankModels.ts +++ b/packages/scripts/src/buildAssets/tankModels.ts @@ -23,7 +23,7 @@ export async function tankModels() { const nation = nations[nationIndex]; const changes: FileChange[] = []; const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>( - `${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`, + `${DATA}/XML/item_defs/vehicles/${nation}/list.xml`, ); console.log(`Building models for ${nation}`); @@ -35,7 +35,7 @@ export async function tankModels() { const id = toUniqueId(nation, tank.id); const parameters = await readYAMLDVPL( - `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`, + `${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, ); const model = await extractModel( DATA, diff --git a/packages/scripts/src/core/blitz/extractArmor.ts b/packages/scripts/src/core/blitz/extractArmor.ts index a5c6e6b2d..5d55e9a63 100644 --- a/packages/scripts/src/core/blitz/extractArmor.ts +++ b/packages/scripts/src/core/blitz/extractArmor.ts @@ -13,8 +13,8 @@ import { import { readDVPLFile } from './readDVPLFile'; export async function extractArmor(data: string, fileName: string) { - const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`; - const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`; + const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2`; + const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg`; const sc2 = new Sc2ReadStream( (await readDVPLFile(sc2Path)).buffer as ArrayBuffer, ).sc2(); diff --git a/packages/scripts/src/core/blitz/extractModel/index.ts b/packages/scripts/src/core/blitz/extractModel/index.ts index a1a036b12..e53941803 100644 --- a/packages/scripts/src/core/blitz/extractModel/index.ts +++ b/packages/scripts/src/core/blitz/extractModel/index.ts @@ -26,8 +26,8 @@ const omitMeshNames = { }; export async function extractModel(data: string, path: string) { - const sc2Path = `${data}/3d/${path}.sc2.dvpl`; - const scgPath = `${data}/3d/${path}.scg.dvpl`; + const sc2Path = `${data}/3d/${path}.sc2`; + const scgPath = `${data}/3d/${path}.scg`; const sc2 = new Sc2ReadStream( (await readDVPLFile(sc2Path)).buffer as ArrayBuffer, ).sc2(); diff --git a/packages/scripts/src/core/blitz/readDVPLFile.ts b/packages/scripts/src/core/blitz/readDVPLFile.ts index ef83c77b2..bf62d8d8c 100644 --- a/packages/scripts/src/core/blitz/readDVPLFile.ts +++ b/packages/scripts/src/core/blitz/readDVPLFile.ts @@ -1,6 +1,11 @@ import { readFile } from 'fs/promises'; +import { DATA_COMPRESSED } from '../../buildAssets/constants'; import { readDVPL } from './readDVPL'; export async function readDVPLFile(file: string) { - return readDVPL(await readFile(file)); + if (DATA_COMPRESSED && !file.endsWith('.dvpl')) { + return readDVPL(await readFile(`${file}.dvpl`)); + } + + return readFile(file); } diff --git a/packages/scripts/src/core/blitz/readTexture/index.ts b/packages/scripts/src/core/blitz/readTexture/index.ts index ce3ff9e88..b20d4b461 100644 --- a/packages/scripts/src/core/blitz/readTexture/index.ts +++ b/packages/scripts/src/core/blitz/readTexture/index.ts @@ -5,8 +5,8 @@ import { readDVPLFile } from '../readDVPLFile'; import { TextureMutation } from './constants'; export async function readTexture(path: string, mutation?: TextureMutation) { - const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl'); - const isDds = existsSync(ddsTexturePath); + const ddsTexturePath = path.replace('.tex', '.dx11.dds'); + const isDds = existsSync(`${ddsTexturePath}.dvpl`); const resolvedTexturePath = isDds ? ddsTexturePath : ddsTexturePath.replace('.dds', '.pvr'); diff --git a/packages/scripts/src/core/github/commitMultipleFiles.ts b/packages/scripts/src/core/github/commitMultipleFiles.ts index c3b41765b..ac826f778 100644 --- a/packages/scripts/src/core/github/commitMultipleFiles.ts +++ b/packages/scripts/src/core/github/commitMultipleFiles.ts @@ -30,10 +30,7 @@ export async function commitMultipleFiles( } else if (response.status === 200) { const buffer = Buffer.from(await response.arrayBuffer()); - // we discard blob if they're the same size; it's unlikely their - // contents will be different; I love playing russian roulette! - const equal = buffer.length === change.content.length || buffer.equals(change.content); - if (!equal) { + if (!buffer.equals(change.content)) { const diff = change.content.length - buffer.length; console.log( diff --git a/packages/scripts/src/pruneStrings.ts b/packages/scripts/src/pruneStrings.ts new file mode 100644 index 000000000..0ad18ca63 --- /dev/null +++ b/packages/scripts/src/pruneStrings.ts @@ -0,0 +1,53 @@ +import { + DEFAULT_LOCALE, + SUPPORTED_LOCALES, + TranslationTree, +} from '@blitzkit/i18n'; +import { writeFile } from 'fs/promises'; + +const ROOT = '@blitzkit/i18n/strings'; +const WORKING_ROOT = '../../packages/i18n/strings'; + +const defaultStrings = require(`${ROOT}/${DEFAULT_LOCALE}.json`).default; +const nonDefaultLocales = SUPPORTED_LOCALES.filter( + (locale) => locale !== DEFAULT_LOCALE, +); + +function prune(defaultStrings: TranslationTree, strings: TranslationTree) { + for (const key in strings) { + if (key in defaultStrings) { + if (typeof defaultStrings[key] === typeof strings[key]) { + if (typeof strings[key] === 'string') { + if ( + // are equivalent regardless of %s flavor + (defaultStrings[key] as string).replaceAll(/%s\d/g, '%s') === + (strings[key] as string).replaceAll(/%s\d/g, '%s') || + // string is empty + strings[key] === '' || + // string has old flavor of %s + strings[key].match(/%s([^\d]|$)/g) !== null + ) { + delete strings[key]; + } + } else { + prune(defaultStrings[key] as TranslationTree, strings[key]); + } + } else { + delete strings[key]; + } + } else { + delete strings[key]; + } + } + + return strings; +} + +for (const locale of nonDefaultLocales) { + console.log(`Pruning ${locale}...`); + + const strings = require(`${ROOT}/${locale}.json`).default; + const pruned = prune(defaultStrings, strings); + + writeFile(`${WORKING_ROOT}/${locale}.json`, JSON.stringify(pruned, null, 2)); +} diff --git a/packages/scripts/src/sitemap.ts b/packages/scripts/src/sitemap.ts deleted file mode 100644 index 09bacb4ae..000000000 --- a/packages/scripts/src/sitemap.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { fetchTankDefinitions } from '@blitzkit/core'; - -// random vercel bug forces require import -const { writeFileSync } = require('fs'); - -console.log('Building sitemap...'); - -const tankDefinitions = await fetchTankDefinitions(); -const values = Object.values(tankDefinitions); -const txt = values - .map((tank) => `https://blitzkit.app/tools/tankopedia/${tank.id}/`) - .join('\n'); -const xml = ` - -${values - .map( - (tank) => - `https://blitzkit.app/tools/tankopedia/${tank.id}/`, - ) - .join('\n')} -`; - -writeFileSync('public/sitemap.txt', txt); -writeFileSync('public/sitemap.xml', xml); - -console.log(`Built sitemap for ${values.length} tanks`); diff --git a/packages/website/astro.config.mjs b/packages/website/astro.config.mjs index 31cecc7e0..3b002a848 100644 --- a/packages/website/astro.config.mjs +++ b/packages/website/astro.config.mjs @@ -2,9 +2,18 @@ import react from '@astrojs/react'; import sitemap from '@astrojs/sitemap'; +import { SUPPORTED_LOCALES } from '@blitzkit/i18n'; import { defineConfig } from 'astro/config'; export default defineConfig({ + i18n: { + defaultLocale: 'en', + locales: [...SUPPORTED_LOCALES], + routing: { + prefixDefaultLocale: false, + }, + }, + devToolbar: { enabled: false }, output: 'static', site: 'https://blitzkit.app', diff --git a/packages/website/package.json b/packages/website/package.json index 30d60d383..a49292006 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@blitzkit/core": "workspace:*", + "@blitzkit/i18n": "workspace:*", "@nanostores/persistent": "^0.10.2", "@nanostores/react": "^0.7.3", "@nivo/bar": "^0.87.0", diff --git a/packages/website/src/components/Armor/components/ShotDisplay.tsx b/packages/website/src/components/Armor/components/ShotDisplay.tsx index cc5a966c9..7cdd9027d 100644 --- a/packages/website/src/components/Armor/components/ShotDisplay.tsx +++ b/packages/website/src/components/Armor/components/ShotDisplay.tsx @@ -16,6 +16,7 @@ import { Path, Quaternion, } from 'three'; +import { LocaleProvider, useLocale } from '../../../hooks/useLocale'; import { TankopediaEphemeral, type ShotLayerNonExternal, @@ -45,6 +46,7 @@ export function ShotDisplay() { const splashRadiusWrapper = useRef(null); const splashRadiusMaterial = useRef(null); let animationStartTime: number | null = null; + const { locale } = useLocale(); useEffect(() => { if (shot?.splashRadius !== undefined) { @@ -180,7 +182,9 @@ export function ShotDisplay() { mt={tracerGoingUp ? '9' : '0'} pt={tracerGoingUp ? '4' : '0'} > - + + + diff --git a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx index ad5214ffa..fb7d56ffc 100644 --- a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx +++ b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx @@ -1,3 +1,4 @@ +import { literals } from '@blitzkit/i18n/src/literals'; import { Card, Flex, @@ -8,33 +9,17 @@ import { } from '@radix-ui/themes'; import type { ComponentProps } from 'react'; import { radToDeg } from 'three/src/math/MathUtils.js'; -import type { - Shot, - ShotLayer, - ShotStatus, -} from '../../../stores/tankopediaEphemeral'; +import { useLocale } from '../../../hooks/useLocale'; +import type { Shot, ShotLayer } from '../../../stores/tankopediaEphemeral'; import { shotStatusColors } from './ShotDisplay'; import { ArmorType } from './SpacedArmorScene'; -import type { ExternalModuleVariant } from './SpacedArmorSceneComponent'; -export const layerTypeNames: Record = { - [ArmorType.Primary]: 'Primary', - [ArmorType.Spaced]: 'Spaced', - [ArmorType.External]: 'External', - null: 'Gap', -}; - -const externalLayerNames: Record = { - gun: 'Gun', - track: 'Tracks', -}; - -const shotStatusNames: Record = { - blocked: 'Blocked', - ricochet: 'Ricochet', - penetration: 'penetration', - splash: 'splash', -}; +export const layerTypeNames = { + [ArmorType.Primary]: 'primary', + [ArmorType.Spaced]: 'spaced', + [ArmorType.External]: 'external', + null: 'gap', +} as const; function LayerEntry({ layerIndex, @@ -49,6 +34,8 @@ function LayerEntry({ layer: ShotLayer; angle?: number; }) { + const { locale, strings } = useLocale(); + return ( @@ -61,20 +48,26 @@ function LayerEntry({ {layer.type === null && ( <> - {Math.round(layer.distance * 1000).toLocaleString()} - mm + {literals(strings.common.units.mm, [ + Math.round(layer.distance * 1000).toLocaleString(locale), + ])} - {Math.max(-100, Math.round(-50 * layer.distance))}% penetration + {literals( + strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats + .ricochet_loss, + [Math.max(-100, -50 * layer.distance).toFixed(0)], + )} )} {layer.type === ArmorType.External && ( - {Math.round(layer.thickness).toLocaleString()} - mm + {literals(strings.common.units.mm, [ + Math.round(layer.thickness).toLocaleString(locale), + ])} )} @@ -82,15 +75,24 @@ function LayerEntry({ layer.type === ArmorType.Spaced) && ( <> - {Math.round(layer.thicknessAngled).toLocaleString()} - {`mm${ - angle === undefined ? '' : ` ${Math.round(radToDeg(angle))}°` - }`} + {literals( + angle === undefined + ? strings.common.units.mm + : strings.website.tools.tankopedia.sandbox.dynamic.shot_card + .stats.thickness_and_angle, + [ + Math.round(layer.thicknessAngled).toLocaleString(locale), + radToDeg(angle ?? 0).toFixed(0), + ], + )} - {Math.round(layer.thickness).toLocaleString()} - mm nominal + {literals( + strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats + .nominal, + [Math.round(layer.thickness).toLocaleString(locale)], + )} )} @@ -103,6 +105,8 @@ interface ShotDisplayCardProps extends CardProps { } export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { + const { locale, strings } = useLocale(); + if (!shot) return null; const outTitleColor = shot.out @@ -115,21 +119,23 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { - {shot.in.status !== 'blocked' && shot.in.status !== 'ricochet' && ( - <> - {Math.round(shot.damage).toLocaleString()} - hp - + {literals( + strings.website.tools.tankopedia.sandbox.dynamic.shot_card.status[ + shot.in.status + ], + [Math.round(shot.damage).toLocaleString(locale)], )} - {` ${shotStatusNames[shot.in.status]} `} {shot.in.layers.map((layer, index) => { const layerName = - layer.type === ArmorType.External - ? externalLayerNames[layer.variant] - : layerTypeNames[`${layer.type}`]; + strings.website.tools.tankopedia.sandbox.dynamic.shot_card + .element[ + layer.type === ArmorType.External + ? layer.variant + : layerTypeNames[`${layer.type}`] + ]; const layerIndex = layer.type === null ? undefined @@ -179,22 +185,22 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { - {shot.out.status !== 'blocked' && - shot.out.status !== 'ricochet' && ( - <> - {Math.round(shot.damage).toLocaleString()} - hp - - )} - {` ${shotStatusNames[shot.out.status]} `} + {literals( + strings.website.tools.tankopedia.sandbox.dynamic.shot_card + .status[shot.out.status], + [Math.round(shot.damage).toLocaleString(locale)], + )} {shot.out.layers.map((layer, index) => { const layerName = - layer.type === ArmorType.External - ? externalLayerNames[layer.variant] - : layerTypeNames[`${layer.type}`]; + strings.website.tools.tankopedia.sandbox.dynamic.shot_card + .element[ + layer.type === ArmorType.External + ? layer.variant + : layerTypeNames[`${layer.type}`] + ]; const shiftedIndex = index + shot.out!.layers.length; const layerIndex = layer.type === null diff --git a/packages/website/src/components/Armor/components/StaticArmor.tsx b/packages/website/src/components/Armor/components/StaticArmor.tsx index 08675a521..c6f410263 100644 --- a/packages/website/src/components/Armor/components/StaticArmor.tsx +++ b/packages/website/src/components/Armor/components/StaticArmor.tsx @@ -180,6 +180,11 @@ export const StaticArmor = memo(({ thicknessRange }) => { duel.protagonist.tank.equipment_preset, duel.protagonist.equipmentMatrix, ); + const hasDownImprovedVerticalStabilizer = await hasEquipment( + 124, + duel.protagonist.tank.equipment_preset, + duel.protagonist.equipmentMatrix, + ); const boundingRect = canvas.getBoundingClientRect(); delta.set(event.clientX, event.clientY).sub(position); @@ -191,6 +196,7 @@ export const StaticArmor = memo(({ thicknessRange }) => { gunModelDefinition.pitch, turretModelDefinition.yaw, hasImprovedVerticalStabilizer, + hasDownImprovedVerticalStabilizer, ); modelTransformEvent.emit({ pitch, yaw }); } @@ -275,6 +281,11 @@ export const StaticArmor = memo(({ thicknessRange }) => { duel.protagonist.tank.equipment_preset, duel.protagonist.equipmentMatrix, ); + const hasDownImprovedVerticalStabilizer = await hasEquipment( + 124, + duel.protagonist.tank.equipment_preset, + duel.protagonist.equipmentMatrix, + ); const boundingRect = canvas.getBoundingClientRect(); delta.set(event.clientX, event.clientY).sub(position); position.set(event.clientX, event.clientY); @@ -285,6 +296,7 @@ export const StaticArmor = memo(({ thicknessRange }) => { gunModelDefinition.pitch, turretModelDefinition.yaw, hasImprovedVerticalStabilizer, + hasDownImprovedVerticalStabilizer, ); modelTransformEvent.emit({ pitch, yaw }); } @@ -357,6 +369,11 @@ export const StaticArmor = memo(({ thicknessRange }) => { duel.protagonist.tank.equipment_preset, duel.protagonist.equipmentMatrix, ); + const hasDownImprovedVerticalStabilizer = await hasEquipment( + 124, + duel.protagonist.tank.equipment_preset, + duel.protagonist.equipmentMatrix, + ); const boundingRect = canvas.getBoundingClientRect(); delta.set(event.clientX, event.clientY).sub(position); position.set(event.clientX, event.clientY); @@ -367,6 +384,7 @@ export const StaticArmor = memo(({ thicknessRange }) => { gunModelDefinition.pitch, turretModelDefinition.yaw, hasImprovedVerticalStabilizer, + hasDownImprovedVerticalStabilizer, ); modelTransformEvent.emit({ pitch, yaw }); } diff --git a/packages/website/src/components/Charts/ChartsControl.tsx b/packages/website/src/components/Charts/ChartsControl.tsx deleted file mode 100644 index 8c019dd77..000000000 --- a/packages/website/src/components/Charts/ChartsControl.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Flex, Select, Text } from '@radix-ui/themes'; -import { Charts, type ChartsPeriod } from '../../stores/charts'; - -export function ChartsControl() { - const period = Charts.use((state) => state.period); - const mutateCharts = Charts.useMutation(); - - return ( - - Player count - { - mutateCharts((draft) => { - draft.period = period as ChartsPeriod; - }); - }} - > - - - Total - Past 120 days - Past 90 days - Past 60 days - This month - This week - Yesterday - - - - ); -} diff --git a/packages/website/src/components/Charts/PlayersInPeriod.tsx b/packages/website/src/components/Charts/PlayersInPeriod.tsx deleted file mode 100644 index 0eb390da3..000000000 --- a/packages/website/src/components/Charts/PlayersInPeriod.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { - TANK_CLASSES, - TIERS, - TIER_ROMAN_NUMERALS, - TankClass, - formatCompact, -} from '@blitzkit/core'; -import strings from '@blitzkit/core/lang/en.json'; -import { useStore } from '@nanostores/react'; -import { Box, Flex, Heading } from '@radix-ui/themes'; -import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; -import { awaitableTankDefinitions } from '../../core/awaitables/tankDefinitions'; -import { filterTank } from '../../core/blitzkit/filterTank'; -import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; -import { Charts } from '../../stores/charts'; -import { $tankFilters } from '../../stores/tankFilters'; -import { ThemedBar } from '../Nivo/ThemedBar'; - -const [tankDefinitions, averageDefinitions] = await Promise.all([ - awaitableTankDefinitions, - awaitableAverageDefinitions, -]); - -const averageDefinitionsArray = Object.entries(averageDefinitions.averages); - -export function PlayersInPeriod() { - const period = Charts.use((state) => state.period); - const ratio = useAveragesExclusionRatio(); - const filters = useStore($tankFilters); - const data = TIERS.map((tier) => { - const players: Record = { - 0: 0, - 1: 0, - 2: 0, - 3: 0, - }; - - averageDefinitionsArray.forEach(([idString, entry]) => { - const id = Number(idString); - const tank = tankDefinitions.tanks[id]; - - if ( - tank === undefined || - tank.tier !== tier || - !filterTank(filters, tank) - ) { - return; - } - - players[tank.class] += entry.samples[period] * ratio; - }); - - const bar: Record = { - tier: TIER_ROMAN_NUMERALS[tier], - }; - - TANK_CLASSES.forEach((c) => { - bar[strings.common.tank_class_short[c]] = players[c]; - }); - - return bar; - }); - - return ( - - Players by tier & class - - strings.common.tank_class_short[c])} - indexBy="tier" - margin={{ right: 90, bottom: 50, left: 90 }} - axisBottom={{ - legend: 'Tier', - legendPosition: 'middle', - legendOffset: 32, - }} - axisLeft={{ - legend: 'Players', - legendPosition: 'middle', - legendOffset: -60, - format: formatCompact, - }} - legends={[ - { - dataFrom: 'keys', - anchor: 'bottom-right', - direction: 'column', - translateX: 120, - itemWidth: 100, - itemHeight: 20, - }, - ]} - valueFormat={formatCompact} - /> - - - ); -} diff --git a/packages/website/src/components/Charts/constants.ts b/packages/website/src/components/Charts/constants.ts deleted file mode 100644 index c5dc4f7c2..000000000 --- a/packages/website/src/components/Charts/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const FIRST_AVERAGES_ENTRY = 19925; diff --git a/packages/website/src/components/Checks/index.tsx b/packages/website/src/components/Checks/index.tsx index 9ba56edcc..7cbbfcaeb 100644 --- a/packages/website/src/components/Checks/index.tsx +++ b/packages/website/src/components/Checks/index.tsx @@ -1,8 +1,14 @@ import { assertSecret, idToRegion } from '@blitzkit/core'; -import { Button, Flex, Heading, Link, Text } from '@radix-ui/themes'; +import { Button, Flex, Heading, Text } from '@radix-ui/themes'; import { useEffect, useState } from 'react'; +import { + LocaleProvider, + useLocale, + type LocaleAcceptorProps, +} from '../../hooks/useLocale'; import { App } from '../../stores/app'; import { CURRENT_POLICIES_AGREEMENT_INDEX } from '../../stores/app/constants'; +import { LinkI18n } from '../LinkI18n'; import './index.css'; type Extension = @@ -18,11 +24,13 @@ type Extension = status: 'error'; }; -export function Checks() { +export function Checks({ locale }: LocaleAcceptorProps) { return ( - - - + + + + + ); } @@ -30,6 +38,7 @@ export function Checks() { * Wargaming: 14 (refresh: 7) */ function Content() { + const { locale } = useLocale(); const logins = App.use((state) => state.logins); const mutateApp = App.useMutation(); const appStore = App.useStore(); @@ -118,9 +127,14 @@ function Content() { This website utilizes cookies to perform analytics and personalize your experience. You can learn more through{' '} - our privacy policy. - By using BlitzKit, you also agree to our{' '} - terms of service. + + our privacy policy + + . By using BlitzKit, you also agree to our{' '} + + terms of service + + . diff --git a/packages/website/src/components/Compare/Body.tsx b/packages/website/src/components/Compare/Body.tsx index c87b2e532..b4c0afd57 100644 --- a/packages/website/src/components/Compare/Body.tsx +++ b/packages/website/src/components/Compare/Body.tsx @@ -1,6 +1,7 @@ import { asset } from '@blitzkit/core'; import { Flex, Heading, IconButton, Table } from '@radix-ui/themes'; import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell'; import { CompareRow } from './CompareRow'; @@ -15,6 +16,7 @@ export function Body({ stats }: BodyProps) { const hasNonRegularGun = members.some( ({ gun }) => gun.gun_type!.$case !== 'regular', ); + const { unwrap, strings } = useLocale(); const mutateCompareEphemeral = CompareEphemeral.useMutation(); return ( @@ -60,7 +62,7 @@ export function Body({ stats }: BodyProps) { }} > {thisShell.name} - + stats.shellReload ?? @@ -96,90 +98,44 @@ export function Body({ stats }: BodyProps) { <> - + )} - - + + {hasNonRegularGun && ( - + )} + + + - - - + + + - - - - - + + Maneuverability + + + + + - - - - - + - Survivability - + (stats.fireChance * 100).toFixed(0)} deltaNominalDisplay={(delta) => (delta * 100).toFixed(0)} /> + - (stats.camouflageStill * 100).toFixed(2)} deltaNominalDisplay={(delta) => (delta * 100).toFixed(2)} /> (stats.camouflageMoving * 100).toFixed(2)} @@ -379,7 +275,6 @@ export function Body({ stats }: BodyProps) { /> (stats.camouflageShootingStill * 100).toFixed(2)} @@ -387,7 +282,6 @@ export function Body({ stats }: BodyProps) { /> (stats.camouflageShootingMoving * 100).toFixed(2)} @@ -395,7 +289,6 @@ export function Body({ stats }: BodyProps) { /> (stats.camouflageCaughtOnFire * 100).toFixed(2)} @@ -403,14 +296,12 @@ export function Body({ stats }: BodyProps) { /> number | undefined); +type CompareRowProps = { display?: ( member: Awaited, ) => number | string | undefined; @@ -23,24 +21,39 @@ interface CompareRowProps { deltaNominalDisplay?: (delta: number) => string | number; indent?: boolean; stats: TankCharacteristics[]; -} +} & ( + | { + name: string; + value: (member: TankCharacteristics) => number | undefined; + } + | { + value: keyof TankCharacteristics; + } +); export function CompareRow({ - value, - name, display, deltaType = 'higherIsBetter', decimals, deltaNominalDisplay, indent, stats, + ...props }: CompareRowProps) { + const { strings } = useLocale(); const mutateCompareEphemeral = CompareEphemeral.useMutation(); const sorting = CompareEphemeral.use((state) => state.sorting); const deltaMode = ComparePersistent.use((state) => state.deltaMode); const values = stats.map((stat) => - typeof value === 'function' ? value(stat)! : (stat[value] as number), + typeof props.value === 'function' + ? props.value(stat)! + : (stat[props.value] as number), ); + const id = useRef(Math.random()); + const name = + typeof props.value === 'string' + ? strings.website.tools.tankopedia.characteristics.values[props.value] + : props.name; return ( @@ -51,7 +64,7 @@ export function CompareRow({ gap={indent ? { initial: '4', sm: '6' } : { initial: '1', sm: '2' }} > { mutateCompareEphemeral((draft) => { @@ -62,26 +75,26 @@ export function CompareRow({ const valueB = values[indexB]; return draft.sorting?.direction === 'ascending' && - draft.sorting.by === name + draft.sorting.by === id.current ? valueA - valueB : valueB - valueA; }); draft.sorting = { - by: name, + by: id.current, direction: draft.sorting?.direction === 'ascending' && - draft.sorting.by === name + draft.sorting.by === id.current ? 'descending' : 'ascending', }; }); }} > - {(sorting === undefined || sorting.by !== name) && ( + {(sorting === undefined || sorting.by !== id.current) && ( )} - {sorting?.by === name && ( + {sorting?.by === id.current && ( <> {sorting.direction === 'ascending' && } {sorting.direction === 'descending' && } diff --git a/packages/website/src/components/Compare/CompareTable.tsx b/packages/website/src/components/Compare/CompareTable.tsx index 240f053dd..f92d3ece0 100644 --- a/packages/website/src/components/Compare/CompareTable.tsx +++ b/packages/website/src/components/Compare/CompareTable.tsx @@ -9,6 +9,7 @@ import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDe import { awaitableSkillDefinitions } from '../../core/awaitables/skillDefinitions'; import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics'; import { Var } from '../../core/radix/var'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; import type { EquipmentMatrix } from '../../stores/duel'; import { BlitzkitButtonGrayIcon } from '../BlitzkitButtonGrayIcon'; @@ -43,6 +44,7 @@ export function CompareTable({ stats }: CompareTableProps) { const crewSkills = CompareEphemeral.use((state) => state.crewSkills); const members = CompareEphemeral.use((state) => state.members); const mutateCompareEphemeral = CompareEphemeral.useMutation(); + const { unwrap, strings } = useLocale(); return ( - Clear + {strings.website.tools.compare.table.skills.clear} @@ -255,7 +257,7 @@ export function CompareTable({ stats }: CompareTableProps) { }); }} > - Stock + {strings.website.tools.compare.table.modules.stock} @@ -293,10 +298,10 @@ export function CompareTable({ stats }: CompareTableProps) { {provisions.map((provision, index) => ( { - Clear + { + strings.website.tools.compare.table.provisions + .clear + } @@ -439,7 +447,10 @@ export function CompareTable({ stats }: CompareTableProps) { }); }} > - Clear + { + strings.website.tools.compare.table.equipment + .clear + } @@ -473,11 +487,11 @@ export function CompareTable({ stats }: CompareTableProps) { {consumables.map((consumable, index) => ( { - Clear + { + strings.website.tools.compare.table.consumables + .clear + } diff --git a/packages/website/src/components/Compare/Controls.tsx b/packages/website/src/components/Compare/Controls.tsx index 50edd5113..b17f8745e 100644 --- a/packages/website/src/components/Compare/Controls.tsx +++ b/packages/website/src/components/Compare/Controls.tsx @@ -2,6 +2,7 @@ import { PlusIcon, TrashIcon } from '@radix-ui/react-icons'; import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes'; import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions'; import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; import { ComparePersistent, @@ -23,6 +24,7 @@ export function Controls({ const deltaMode = ComparePersistent.use((state) => state.deltaMode); const mutateCompareEphemeral = CompareEphemeral.useMutation(); const mutateComparePersistent = ComparePersistent.useMutation(); + const { strings } = useLocale(); return ( @@ -32,14 +34,16 @@ export function Controls({ > - Add tanks + + {strings.website.tools.compare.actions.add.title} + - Select tanks to add to comparison + {strings.website.tools.compare.actions.add.description} @@ -87,7 +91,7 @@ export function Controls({ }); }} > - Clear + {strings.website.tools.compare.actions.clear} - No deltas + {strings.website.tools.compare.actions.deltas.none} - Percentage + {strings.website.tools.compare.actions.deltas.percentage} - Absolute + {strings.website.tools.compare.actions.deltas.absolute} diff --git a/packages/website/src/components/Compare/TankCard.tsx b/packages/website/src/components/Compare/TankCard.tsx index 803eb19a6..274b54e59 100644 --- a/packages/website/src/components/Compare/TankCard.tsx +++ b/packages/website/src/components/Compare/TankCard.tsx @@ -2,6 +2,7 @@ import { type TankDefinition, tankIcon } from '@blitzkit/core'; import { Flex, Text } from '@radix-ui/themes'; import { useEffect, useRef } from 'react'; import { Vector2 } from 'three'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell'; import { InsertionMarker } from './IntersectionMarker'; @@ -16,6 +17,7 @@ interface TankCardProps { export function TankCard({ index, tank }: TankCardProps) { const draggable = useRef(null); const mutateCompareEphemeral = CompareEphemeral.useMutation(); + const { unwrap } = useLocale(); useEffect(() => { const initial = new Vector2(); @@ -118,7 +120,7 @@ export function TankCard({ index, tank }: TankCardProps) { }} > {tank.name} - {tank.name} + {unwrap(tank.name)} diff --git a/packages/website/src/components/Compare/TankControl.tsx b/packages/website/src/components/Compare/TankControl.tsx index 83d6b920f..96aa5840f 100644 --- a/packages/website/src/components/Compare/TankControl.tsx +++ b/packages/website/src/components/Compare/TankControl.tsx @@ -1,9 +1,11 @@ import { ExternalLinkIcon, LoopIcon, TrashIcon } from '@radix-ui/react-icons'; -import { Dialog, Flex, IconButton, Link } from '@radix-ui/themes'; +import { Dialog, Flex, IconButton } from '@radix-ui/themes'; import { useState } from 'react'; import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions'; import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; +import { LinkI18n } from '../LinkI18n'; import { TankSearch } from '../TankSearch'; interface TankControlProps { @@ -14,6 +16,7 @@ interface TankControlProps { const provisionDefinitions = await awaitableProvisionDefinitions; export function TankControl({ index, id }: TankControlProps) { + const { locale } = useLocale(); const [switchTankDialogOpen, setSwitchTankDialogOpen] = useState(false); const mutateCompareEphemeral = CompareEphemeral.useMutation(); @@ -69,11 +72,15 @@ export function TankControl({ index, id }: TankControlProps) { - + - + ); } diff --git a/packages/website/src/components/CopyButton.tsx b/packages/website/src/components/CopyButton.tsx index 1365316ca..8fa174242 100644 --- a/packages/website/src/components/CopyButton.tsx +++ b/packages/website/src/components/CopyButton.tsx @@ -1,5 +1,6 @@ import { Button, type ButtonProps, Tooltip } from '@radix-ui/themes'; import { useState } from 'react'; +import { useLocale } from '../hooks/useLocale'; interface CopyButtonProps extends ButtonProps { copy: () => string | undefined; @@ -7,9 +8,13 @@ interface CopyButtonProps extends ButtonProps { export function CopyButton({ copy, ...props }: CopyButtonProps) { const [tooltipOpen, setTooltipOpen] = useState(false); + const { strings } = useLocale(); return ( - + - Generate URL - Search an account to track + + {strings.website.tools.embed.configuration.export.generate.title} + + + { + strings.website.tools.embed.configuration.export.generate + .subtitle + } + @@ -98,12 +107,14 @@ export function GenerateURL({ embed }: GenerateURLProps) { {searching && ( - Searching + {strings.website.common.player_search.searching} )} {!searching && results.length === 0 && ( - No results + + {strings.website.common.player_search.no_results} + )} {!searching && results.length > 0 && ( @@ -131,7 +142,7 @@ export function GenerateURL({ embed }: GenerateURLProps) { @@ -140,12 +151,18 @@ export function GenerateURL({ embed }: GenerateURLProps) { - Copy URL + + { + strings.website.tools.embed.configuration.export.generate + .success.title + } + - Generated the URL for {copyUser?.nickname}. The embed will start - tracking as soon as the URL is pasted. Reset at any time by - right-clicking on PC or long-pressing on mobile and then selecting - "Reset". + {literals( + strings.website.tools.embed.configuration.export.generate + .success.info, + [`${copyUser?.nickname}`], + )} @@ -158,7 +175,11 @@ export function GenerateURL({ embed }: GenerateURLProps) { setSearchDialogOpen(true); }} > - Back + {' '} + { + strings.website.tools.embed.configuration.export.generate + .success.back + } - Copy URL + {' '} + { + strings.website.tools.embed.configuration.export.generate + .success.copy + } diff --git a/packages/website/src/components/Embeds/Import.tsx b/packages/website/src/components/Embeds/Import.tsx index 32ad5f769..da1e34fed 100644 --- a/packages/website/src/components/Embeds/Import.tsx +++ b/packages/website/src/components/Embeds/Import.tsx @@ -9,6 +9,7 @@ import { } from '@radix-ui/themes'; import { useRef, useState } from 'react'; import { parse } from 'urlon'; +import { useLocale } from '../../hooks/useLocale'; import { EmbedState } from '../../stores/embedState'; export function Import() { @@ -16,12 +17,15 @@ export function Import() { const [showInvalidURLPrompt, setShowInvalidURLPrompt] = useState(false); const [showConfirmation, setConfirmation] = useState(false); const mutateEmbedState = EmbedState.useMutation(); + const { strings } = useLocale(); return ( <> - Import + + {strings.website.tools.embed.configuration.import.title} + - Paste your current URL if you have one already to import your settings + {strings.website.tools.embed.configuration.import.description} - Invalid URL + + {strings.website.tools.embed.configuration.import.idiot_title} + - Try pasting the URL that BlitzKit previously generated, you silly - goose! + {strings.website.tools.embed.configuration.import.idiot_description} - + @@ -46,7 +53,7 @@ export function Import() { @@ -72,7 +79,7 @@ export function Import() { } }} > - Apply + {strings.website.tools.embed.configuration.import.apply} diff --git a/packages/website/src/components/Embeds/Radius.tsx b/packages/website/src/components/Embeds/Radius.tsx index cd4f2c05c..f770b43b0 100644 --- a/packages/website/src/components/Embeds/Radius.tsx +++ b/packages/website/src/components/Embeds/Radius.tsx @@ -1,5 +1,5 @@ import { Slider } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState, type RadixRadius } from '../../stores/embedState'; export function Radius({ configKey }: EmbedPreviewControllerProps) { diff --git a/packages/website/src/components/Embeds/RichText.tsx b/packages/website/src/components/Embeds/RichText.tsx index 76fe0acf2..ec5c6e24f 100644 --- a/packages/website/src/components/Embeds/RichText.tsx +++ b/packages/website/src/components/Embeds/RichText.tsx @@ -1,6 +1,6 @@ import { FontBoldIcon, FontSizeIcon } from '@radix-ui/react-icons'; import { Flex, Slider, Text } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState, type RadixSizeWithout0 } from '../../stores/embedState'; import { radixTextWeights, diff --git a/packages/website/src/components/Embeds/Size.tsx b/packages/website/src/components/Embeds/Size.tsx index 248cec4ea..c885b0b86 100644 --- a/packages/website/src/components/Embeds/Size.tsx +++ b/packages/website/src/components/Embeds/Size.tsx @@ -1,5 +1,5 @@ import { Slider } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState, type RadixSize } from '../../stores/embedState'; export function Size({ configKey }: EmbedPreviewControllerProps) { diff --git a/packages/website/src/components/Embeds/SizeWithout0.tsx b/packages/website/src/components/Embeds/SizeWithout0.tsx index ea0ed192f..c44625a4e 100644 --- a/packages/website/src/components/Embeds/SizeWithout0.tsx +++ b/packages/website/src/components/Embeds/SizeWithout0.tsx @@ -1,5 +1,5 @@ import { Slider } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState, type RadixSizeWithout0 } from '../../stores/embedState'; export function SizeWithout0({ configKey }: EmbedPreviewControllerProps) { diff --git a/packages/website/src/components/Embeds/Slider.tsx b/packages/website/src/components/Embeds/Slider.tsx index 6fe525684..e2b59f02a 100644 --- a/packages/website/src/components/Embeds/Slider.tsx +++ b/packages/website/src/components/Embeds/Slider.tsx @@ -1,5 +1,5 @@ import { Slider as RadixSlider } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState, type RadixSize } from '../../stores/embedState'; import type { EmbedConfigItemType, diff --git a/packages/website/src/components/Embeds/TextController.tsx b/packages/website/src/components/Embeds/TextController.tsx index 5e0c95cf0..5d819731f 100644 --- a/packages/website/src/components/Embeds/TextController.tsx +++ b/packages/website/src/components/Embeds/TextController.tsx @@ -1,5 +1,5 @@ import { TextField } from '@radix-ui/themes'; -import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index'; +import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index'; import { EmbedState } from '../../stores/embedState'; export function TextController({ configKey }: EmbedPreviewControllerProps) { diff --git a/packages/website/src/components/Embeds/breakdown.tsx b/packages/website/src/components/Embeds/breakdown.tsx index 279a3dad4..d98b9ecf5 100644 --- a/packages/website/src/components/Embeds/breakdown.tsx +++ b/packages/website/src/components/Embeds/breakdown.tsx @@ -8,7 +8,7 @@ import { sumCompositeStats, type IndividualTankStats, } from '@blitzkit/core'; -import strings from '@blitzkit/core/lang/en.json'; +import { STRINGS } from '@blitzkit/i18n'; import { ReloadIcon } from '@radix-ui/react-icons'; import { ContextMenu } from '@radix-ui/themes'; import { useEffect, useMemo, useState } from 'react'; @@ -20,8 +20,13 @@ import { useEmbedStateCurry } from '../../stores/embedState/utilities'; import { BreakdownEmbedCard, BreakdownEmbedWrapper } from '../TanksEmbed'; export const compositeStatsKeysOptions = compositeStatsKeys.map((value) => ({ - label: strings.common.composite_stats[value], value, + locales: Object.entries(STRINGS).reduce((accumulator, [locale, strings]) => { + return { + ...accumulator, + [locale]: strings.common.composite_stats[value], + }; + }, {}), })); const [tankDefinitions, averageDefinitions] = await Promise.all([ diff --git a/packages/website/src/components/Footer.astro b/packages/website/src/components/Footer.astro index 4b28bffe8..b4094b9ea 100644 --- a/packages/website/src/components/Footer.astro +++ b/packages/website/src/components/Footer.astro @@ -1,10 +1,16 @@ --- import { assertSecret, fetchGameDefinitions } from '@blitzkit/core'; -import { Code, Flex, Heading, Link, Text } from '@radix-ui/themes'; +import { DEFAULT_LOCALE } from '@blitzkit/i18n'; +import { Box, Code, Flex, Heading, Link, Text } from '@radix-ui/themes'; import packageJson from '../../../../package.json'; +import { getStrings } from '../core/i18n/getStrings'; +import { LinkI18n } from './LinkI18n'; +import { LocaleSwitcherThemeWrapper } from './LocaleSwitcher'; const gameDefinitions = await fetchGameDefinitions(); const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH); +const locale = Astro.params.locale ?? DEFAULT_LOCALE; +const strings = getStrings(locale); --- - + diff --git a/packages/website/src/pages/tools/embed/_index.tsx b/packages/website/src/pages/[...locale]/tools/embed/_index.tsx similarity index 62% rename from packages/website/src/pages/tools/embed/_index.tsx rename to packages/website/src/pages/[...locale]/tools/embed/_index.tsx index 5d27fcb22..15a183f76 100644 --- a/packages/website/src/pages/tools/embed/_index.tsx +++ b/packages/website/src/pages/[...locale]/tools/embed/_index.tsx @@ -1,56 +1,44 @@ import { imgur, ImgurSize } from '@blitzkit/core'; import { Box, Flex, Link, Text } from '@radix-ui/themes'; -import { capitalize } from 'lodash-es'; -import { PreviewWrapper } from '../../../components/Embeds/PreviewWrapper'; -import { PageWrapper } from '../../../components/PageWrapper'; +import { PreviewWrapper } from '../../../../components/Embeds/PreviewWrapper'; +import { LinkI18n } from '../../../../components/LinkI18n'; +import { PageWrapper } from '../../../../components/PageWrapper'; import { embedConfigurations, extractEmbedConfigDefaults, -} from '../../../constants/embeds'; -import { Var } from '../../../core/radix/var'; -import { App } from '../../../stores/app'; -import { EmbedState } from '../../../stores/embedState'; +} from '../../../../constants/embeds'; +import { Var } from '../../../../core/radix/var'; +import { + LocaleProvider, + useLocale, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { App } from '../../../../stores/app'; +import { EmbedState } from '../../../../stores/embedState'; -export function Page() { +export function Page({ locale }: LocaleAcceptorProps) { return ( - - - + + + + + ); } function Content() { - // const wargaming = App.useDeferred((state) => state.logins.wargaming, { - // token: '', - // id: 0, - // expires: 0, - // }); + const { locale, strings } = useLocale(); return ( - {/* {!wargaming && ( - - - - - - - You must log in to generate embeds. - - - - Log in with Wargaming - - )} */} - - More embeds coming soon.{' '} + {strings.website.tools.embed.coming_soon}{' '} - Suggest ideas + {strings.website.tools.embed.suggest} . @@ -61,7 +49,8 @@ function Content() { key={embed} data={extractEmbedConfigDefaults(config)} > - - {capitalize(embed)} + + { + strings.website.tools.embed.types[ + embed as keyof typeof strings.website.tools.embed.types + ].name + } + - + ))} diff --git a/packages/website/src/pages/[...locale]/tools/embed/index.astro b/packages/website/src/pages/[...locale]/tools/embed/index.astro new file mode 100644 index 000000000..366e9631e --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/embed/index.astro @@ -0,0 +1,13 @@ +--- +import { embedTool } from '../../../../constants/tools'; +import ToolLayout from '../../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +--- + + + + diff --git a/packages/website/src/pages/[...locale]/tools/gallery/_index.tsx b/packages/website/src/pages/[...locale]/tools/gallery/_index.tsx new file mode 100644 index 000000000..06a07c78a --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/gallery/_index.tsx @@ -0,0 +1,53 @@ +import { literals } from '@blitzkit/i18n/src/literals'; +import { CalendarIcon } from '@radix-ui/react-icons'; +import { Callout, Flex } from '@radix-ui/themes'; +import { GalleryList, type Avatar } from '../../../../components/Gallery/List'; +import { GallerySearch } from '../../../../components/Gallery/Search'; +import { PageWrapper } from '../../../../components/PageWrapper'; +import { + LocaleProvider, + useLocale, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { GalleryEphemeral } from '../../../../stores/galleryEphemeral'; + +interface ContentProps { + avatars: Avatar[]; + lastUpdated: string; +} + +type PageProps = ContentProps & LocaleAcceptorProps; + +export function Page({ locale, ...props }: PageProps) { + return ( + + + + ); +} + +function Content({ avatars, lastUpdated }: ContentProps) { + const { strings } = useLocale(); + + return ( + + + + + + + + {literals(strings.website.tools.gallery.last_updated, [ + lastUpdated, + ])} + + + + + + + + + + ); +} diff --git a/packages/website/src/pages/[...locale]/tools/gallery/index.astro b/packages/website/src/pages/[...locale]/tools/gallery/index.astro new file mode 100644 index 000000000..b32f542ef --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/gallery/index.astro @@ -0,0 +1,29 @@ +--- +import type { Avatar } from '../../../../components/Gallery/List'; +import { galleryTool } from '../../../../constants/tools'; +import { fetchGlossary } from '../../../../core/blitz/fetchGlossary'; +import ToolLayout from '../../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +const glossary = await fetchGlossary(locale); +const avatars = Object.entries(glossary) + .filter( + ([key, value]) => + key.startsWith('avatar') && + !key.endsWith('_part') && + value.image_url !== null, + ) + .map( + ([key, value]) => + ({ id: key.replace('avatar_', ''), name: value.title }) satisfies Avatar, + ) + .sort((a, b) => a.name.localeCompare(b.name)); +const lastUpdated = new Date().toLocaleDateString(locale); +--- + + + + diff --git a/packages/website/src/pages/[...locale]/tools/performance/_index.tsx b/packages/website/src/pages/[...locale]/tools/performance/_index.tsx new file mode 100644 index 000000000..c2029990f --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/performance/_index.tsx @@ -0,0 +1,38 @@ +import { PageWrapper } from '../../../../components/PageWrapper'; +import { PerformanceInfo } from '../../../../components/Performance/PerformanceInfo'; +import { PlayerCountControl } from '../../../../components/Performance/PlayerCountControl'; +import { TankTable } from '../../../../components/Performance/Table'; +import { FilterControl } from '../../../../components/TankSearch/components/FilterControl'; +import { + LocaleProvider, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { TankPerformanceEphemeral } from '../../../../stores/tankPerformanceEphemeral'; +import { TankPerformanceSort } from '../../../../stores/tankPerformanceSort'; +import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps'; + +type PageProps = MaybeSkeletonComponentProps & LocaleAcceptorProps; + +export function Page({ skeleton, locale }: PageProps) { + return ( + + + + + + + + ); +} + +function Content({ skeleton }: MaybeSkeletonComponentProps) { + return ( + + + + + + + + ); +} diff --git a/packages/website/src/pages/[...locale]/tools/performance/index.astro b/packages/website/src/pages/[...locale]/tools/performance/index.astro new file mode 100644 index 000000000..f561bb676 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/performance/index.astro @@ -0,0 +1,18 @@ +--- +import ToolOpenGraph from '../../../../components/ToolOpenGraph.astro'; +import { performanceTool } from '../../../../constants/tools'; +import BaseLayout from '../../../../layouts/BaseLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +--- + + + + + + + + diff --git a/packages/website/src/pages/tools/session/_index.tsx b/packages/website/src/pages/[...locale]/tools/session/_index.tsx similarity index 82% rename from packages/website/src/pages/tools/session/_index.tsx rename to packages/website/src/pages/[...locale]/tools/session/_index.tsx index 70f81f265..f231a4617 100644 --- a/packages/website/src/pages/tools/session/_index.tsx +++ b/packages/website/src/pages/[...locale]/tools/session/_index.tsx @@ -13,7 +13,7 @@ import { type IndividualAccountInfo, type IndividualTankStats, } from '@blitzkit/core'; -import strings from '@blitzkit/core/lang/en.json'; +import { literals } from '@blitzkit/i18n/src/literals'; import { ArrowDownIcon, MagnifyingGlassIcon, @@ -33,28 +33,36 @@ import { } from '@radix-ui/themes'; import { debounce } from 'lodash-es'; import { useEffect, useMemo, useRef, useState } from 'react'; -import { PageWrapper } from '../../../components/PageWrapper'; -import { StickyRowHeaderCell } from '../../../components/StickyRowHeaderCell'; -import { StickyTableRoot } from '../../../components/StickyTableRoot'; -import { TankRowHeaderCell } from '../../../components/TankRowHeaderCell'; -import { awaitableAverageDefinitions } from '../../../core/awaitables/averageDefinitions'; -import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions'; -import { Session, type SessionTracking } from '../../../stores/session'; +import { PageWrapper } from '../../../../components/PageWrapper'; +import { StickyRowHeaderCell } from '../../../../components/StickyRowHeaderCell'; +import { StickyTableRoot } from '../../../../components/StickyTableRoot'; +import { TankRowHeaderCell } from '../../../../components/TankRowHeaderCell'; +import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; +import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; +import { + LocaleProvider, + useLocale, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { Session, type SessionTracking } from '../../../../stores/session'; const [tankDefinitions, averageDefinitions] = await Promise.all([ awaitableTankDefinitions, awaitableAverageDefinitions, ]); -export function Page() { +export function Page({ locale }: LocaleAcceptorProps) { return ( - - - + + + + + ); } function Content() { + const { locale, strings } = useLocale(); const [showSearch, setShowSearch] = useState(false); const [searching, setSearching] = useState(false); const [searchResults, setSearchResults] = useState([]); @@ -163,17 +171,17 @@ function Content() { > - You're attempting to track a CC account + {strings.website.tools.session.no_cc.title} - CC (community contributor) accounts do not have public tank - statistics. We are in the process of adding embeds for CC accounts, - stay tuned. + {strings.website.tools.session.no_cc.body} - + @@ -188,7 +196,9 @@ function Content() { gap="3" > - Look up a player to get started + + {strings.website.tools.session.search.lookup} + @@ -200,7 +210,7 @@ function Content() { maxWidth: 480, }} ref={input} - placeholder="Search players..." + placeholder={strings.website.tools.session.search.hint} onChange={() => { if (!input.current) return; @@ -224,13 +234,17 @@ function Content() { {searching && ( - Searching... + + {strings.website.tools.session.search.searching} + )} {!searching && searchResults.length === 0 && ( - No players found + + {strings.website.tools.session.search.no_results} + )} @@ -263,9 +277,15 @@ function Content() { gap="2" > - Tracking {accountInfo?.nickname} + + {literals(strings.website.tools.session.controls.title, [ + `${accountInfo?.nickname}`, + ])} + - Since {new Date(session.player.since).toLocaleString()} + {literals(strings.website.tools.session.controls.subtitle, [ + new Date(session.player.since).toLocaleString(locale), + ])} @@ -288,7 +308,7 @@ function Content() { }); }} > - Reset + {strings.website.tools.session.controls.reset} @@ -308,7 +328,9 @@ function Content() { - Tank + + {strings.website.tools.session.table.tank} + {session.columns.map((column, index) => ( + + {strings.website.tools.session.table.remove} + + + + {compositeStatsKeys .filter( (item) => @@ -383,10 +411,6 @@ function Content() { {strings.common.composite_stats[key]} ))} - - - - Remove @@ -407,7 +431,7 @@ function Content() { overflowY: 'hidden', }} > - Total + {strings.website.tools.session.table.total} {session.columns.map((column) => ( @@ -451,8 +475,12 @@ function Content() { flex: 1, }} > - No battles - Go ahead and play a game + + {strings.website.tools.session.no_battles.title} + + + {strings.website.tools.session.no_battles.hint} + )} diff --git a/packages/website/src/pages/[...locale]/tools/session/index.astro b/packages/website/src/pages/[...locale]/tools/session/index.astro new file mode 100644 index 000000000..12984b189 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/session/index.astro @@ -0,0 +1,13 @@ +--- +import { sessionTool } from '../../../../constants/tools'; +import ToolLayout from '../../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +--- + + + + diff --git a/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/_index.tsx b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/_index.tsx new file mode 100644 index 000000000..617382f94 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/_index.tsx @@ -0,0 +1,64 @@ +import { TankType } from '@blitzkit/core'; +import { PageWrapper } from '../../../../../components/PageWrapper'; +import { CalloutsSection } from '../../../../../components/Tankopedia/CalloutsSection'; +import { CharacteristicsSection } from '../../../../../components/Tankopedia/CharacteristicsSection'; +import { GameModeSection } from '../../../../../components/Tankopedia/GameModeSection'; +import { HeroSection } from '../../../../../components/Tankopedia/HeroSection'; +import { MetaSection } from '../../../../../components/Tankopedia/MetaSection'; +import { TechTreeSection } from '../../../../../components/Tankopedia/TechTreeSection'; +import { VideoSection } from '../../../../../components/Tankopedia/VideoSection'; +import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions'; +import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; +import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions'; +import { + LocaleProvider, + type LocaleAcceptorProps, +} from '../../../../../hooks/useLocale'; +import { App } from '../../../../../stores/app'; +import { Duel } from '../../../../../stores/duel'; +import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral'; +import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent'; +import type { MaybeSkeletonComponentProps } from '../../../../../types/maybeSkeletonComponentProps'; + +type PageProps = MaybeSkeletonComponentProps & + LocaleAcceptorProps & { + id: number; + }; + +const [tankDefinitions, provisionDefinitions, modelDefinitions] = + await Promise.all([ + awaitableTankDefinitions, + awaitableProvisionDefinitions, + awaitableModelDefinitions, + ]); + +export function Page({ id, skeleton, locale }: PageProps) { + const tank = tankDefinitions.tanks[id]; + const model = modelDefinitions.models[id]; + + return ( + + + + + + + + + + {tank.type === TankType.RESEARCHABLE && !tank.deprecated && ( + + )} + + + + + + + + + + ); +} diff --git a/packages/website/src/pages/tools/tankopedia/[id]/index.astro b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro similarity index 59% rename from packages/website/src/pages/tools/tankopedia/[id]/index.astro rename to packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro index d37161a4f..29fdad238 100644 --- a/packages/website/src/pages/tools/tankopedia/[id]/index.astro +++ b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro @@ -10,23 +10,32 @@ import { fetchTankDefinitions, TIER_ROMAN_NUMERALS, } from '@blitzkit/core'; -import strings from '@blitzkit/core/lang/en.json'; +import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n'; import type { GetStaticPaths } from 'astro'; -import { tankCharacteristics } from '../../../../core/blitzkit/tankCharacteristics'; -import BaseLayout from '../../../../layouts/BaseLayout.astro'; -import { genericDefaultEquipmentMatrix } from '../../../../stores/duel/constants'; +import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics'; +import { getStrings } from '../../../../../core/i18n/getStrings'; +import { literals } from '@blitzkit/i18n/src/literals'; +import BaseLayout from '../../../../../layouts/BaseLayout.astro'; +import { genericDefaultEquipmentMatrix } from '../../../../../stores/duel/constants'; +import { getStaticPaths as parentGetStaticPaths } from '../index.astro'; import { Page } from './_index'; export const getStaticPaths = (async () => { const tankDefinitions = await fetchTankDefinitions(); + const parentPaths = parentGetStaticPaths(); - return Object.values(tankDefinitions.tanks).map((tank) => ({ - params: { id: tank.id }, - })); + return Object.values(tankDefinitions.tanks) + .map((tank) => + parentPaths.map((parent) => ({ + params: { ...parent.params, id: tank.id }, + })), + ) + .flat(); }) satisfies GetStaticPaths; const { id } = Astro.params; - +const locale = Astro.params.locale ?? DEFAULT_LOCALE; +const strings = getStrings(locale); const tankDefinitions = await fetchTankDefinitions(); const skillDefinitions = await fetchSkillDefinitions(); const provisionDefinitions = await fetchProvisionDefinitions(); @@ -35,11 +44,17 @@ const tankModelDefinitions = await fetchModelDefinitions(); const tank = tankDefinitions.tanks[id]; const tankModelDefinition = tankModelDefinitions.models[tank.id]; const name = tank.name_full ?? tank.name; -const title = `${name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${ +const unwrap = unwrapper(locale); +const title = literals(strings.website.tools.tankopedia.tank_seo_title, [ + unwrap(name), + TIER_ROMAN_NUMERALS[tank.tier], strings.common.nations_adjectives[ tank.nation as keyof typeof strings.common.nations_adjectives - ] -} ${strings.common.tank_class_short[tank.class]} | BlitzKit Tankopedia | World of Tanks Blitz (WoTB)`; + ], + strings.common.tank_class_short[tank.class], + 'BlitzKit', + strings.common.nations[tank.nation as keyof typeof strings.common.nations], +]); const url = import.meta.env.SITE + Astro.url.pathname; const icon = asset(`icons/tanks/big/${id}.webp`); const turret = tank.turrets.at(-1)!; @@ -67,15 +82,17 @@ const characteristics = tankCharacteristics( }, { equipmentDefinitions, provisionDefinitions, tankModelDefinition }, ); -const description = `${ - name -} armor, statistics, equipment, provisions, and more. ${Math.round( - characteristics.dpm, -).toLocaleString()} DPM, ${characteristics.damage.toFixed(0)}hp alpha, ${characteristics.penetration.toFixed( - 0, -)}mm penetration, ${characteristics.speedForwards.toFixed( - 0, -)}kph top speed, ${characteristics.health}hp health.`; +const description = literals( + strings.website.tools.tankopedia.tank_seo_description, + [ + unwrap(name), + Math.round(characteristics.dpm).toLocaleString(locale), + Math.round(characteristics.damage).toLocaleString(locale), + Math.round(characteristics.penetration).toLocaleString(locale), + Math.round(characteristics.speedForwards).toLocaleString(locale), + Math.round(characteristics.health).toLocaleString(locale), + ], +); --- {title} @@ -90,7 +107,7 @@ const description = `${ - - + + diff --git a/packages/website/src/pages/[...locale]/tools/tankopedia/_index.tsx b/packages/website/src/pages/[...locale]/tools/tankopedia/_index.tsx new file mode 100644 index 000000000..9586154a7 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/tankopedia/_index.tsx @@ -0,0 +1,22 @@ +import { PageWrapper } from '../../../../components/PageWrapper'; +import { TankSearch } from '../../../../components/TankSearch'; +import { + LocaleProvider, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { App } from '../../../../stores/app'; +import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent'; + +export function Page({ locale }: LocaleAcceptorProps) { + return ( + + + + + + + + + + ); +} diff --git a/packages/website/src/pages/[...locale]/tools/tankopedia/index.astro b/packages/website/src/pages/[...locale]/tools/tankopedia/index.astro new file mode 100644 index 000000000..71a69ab5c --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/tankopedia/index.astro @@ -0,0 +1,15 @@ +--- +import { tankopediaTool } from '../../../../constants/tools'; +import ToolLayout from '../../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +--- + + + + + + diff --git a/packages/website/src/pages/[...locale]/tools/tier-list/_index.tsx b/packages/website/src/pages/[...locale]/tools/tier-list/_index.tsx new file mode 100644 index 000000000..c068da096 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/tier-list/_index.tsx @@ -0,0 +1,36 @@ +import { PageWrapper } from '../../../../components/PageWrapper'; +import { FilterControl } from '../../../../components/TankSearch/components/FilterControl'; +import { TierListControls } from '../../../../components/TierList/Controls'; +import { TierListTable } from '../../../../components/TierList/Table'; +import { TierListTiles } from '../../../../components/TierList/Tiles'; +import { URLManager } from '../../../../components/TierList/URLManager'; +import { + LocaleProvider, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; +import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent'; +import { TierList } from '../../../../stores/tierList'; + +export function Page({ locale }: LocaleAcceptorProps) { + return ( + + + + + + + + ); +} + +function Content() { + return ( + + + + + + + + ); +} diff --git a/packages/website/src/pages/[...locale]/tools/tier-list/index.astro b/packages/website/src/pages/[...locale]/tools/tier-list/index.astro new file mode 100644 index 000000000..e22b87824 --- /dev/null +++ b/packages/website/src/pages/[...locale]/tools/tier-list/index.astro @@ -0,0 +1,13 @@ +--- +import { tierListTool } from '../../../../constants/tools'; +import ToolLayout from '../../../../layouts/ToolLayout.astro'; +import { Page } from './_index'; + +export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; +--- + + + + diff --git a/packages/website/src/pages/api/gallery/[avatar].webp.ts b/packages/website/src/pages/api/gallery/[avatar].webp.ts index abc8221b8..08dab9670 100644 --- a/packages/website/src/pages/api/gallery/[avatar].webp.ts +++ b/packages/website/src/pages/api/gallery/[avatar].webp.ts @@ -1,8 +1,9 @@ +import { DEFAULT_LOCALE } from '@blitzkit/i18n'; import type { APIRoute, GetStaticPaths } from 'astro'; import { fetchGlossary } from '../../../core/blitz/fetchGlossary'; export const getStaticPaths = (async () => { - const glossary = await fetchGlossary(); + const glossary = await fetchGlossary(DEFAULT_LOCALE); const avatarKeys = Object.keys(glossary) .filter((key) => key.startsWith('avatar_')) .map((key) => key.replace('avatar_', '')); @@ -10,7 +11,7 @@ export const getStaticPaths = (async () => { return avatarKeys.map((avatar) => ({ params: { avatar } })); }) satisfies GetStaticPaths; -const glossary = await fetchGlossary(); +const glossary = await fetchGlossary(DEFAULT_LOCALE); export const GET: APIRoute<{}, { avatar: string }> = async ({ params }) => { const image = diff --git a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx index 213149ea1..96a50aecc 100644 --- a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx +++ b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx @@ -2,6 +2,7 @@ import { TankSandbox } from '../../../../../components/Tankopedia/HeroSection/co import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions'; import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions'; +import { LocaleProvider } from '../../../../../hooks/useLocale'; import { App } from '../../../../../stores/app'; import { Duel } from '../../../../../stores/duel'; import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral'; @@ -23,15 +24,17 @@ export function Page({ id }: PageProps) { const model = modelDefinitions.models[id]; return ( - - - - - - - - - + + + + + + + + + + + ); } diff --git a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro index 7aa817f2b..e8e8d414a 100644 --- a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro +++ b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro @@ -1,7 +1,7 @@ --- import { assertSecret } from '@blitzkit/core'; import type { GetStaticPaths } from 'astro'; -import { getStaticPaths as tankopediaGetStaticPaths } from '../../../../tools/tankopedia/[id]/index.astro'; +import { getStaticPaths as tankopediaGetStaticPaths } from '../../../../[...locale]/tools/tankopedia/[id]/index.astro'; import { Page } from './_index'; export const getStaticPaths = (async () => { diff --git a/packages/website/src/pages/docs/[directory]/[document]/index.astro b/packages/website/src/pages/docs/[directory]/[document]/index.astro deleted file mode 100644 index c213c2d4c..000000000 --- a/packages/website/src/pages/docs/[directory]/[document]/index.astro +++ /dev/null @@ -1,38 +0,0 @@ ---- -import type { GetStaticPaths } from 'astro'; -import { readFile } from 'fs/promises'; -import BaseLayout from '../../../../layouts/BaseLayout.astro'; -import { Page } from './_index'; - -export const getStaticPaths = (async () => { - const ROOT = '../../../../../../../docs/'; - const files = import.meta.glob('../../../../../../../docs/**/*.md', { - eager: true, - }); - - return await Promise.all( - Object.keys(files).map(async (file) => { - const couple = file.replace(ROOT, '').split('/'); - couple[1] = couple[1].replace('.md', ''); - const content = await readFile( - `../../docs/${couple[0]}/${couple[1]}.md`, - ).then((buffer) => buffer.toString()); - - return { - params: { directory: couple[0], document: couple[1] }, - props: { content }, - }; - }), - ); -}) satisfies GetStaticPaths; - -const { content } = Astro.props; - -const title = content.split('\n')[0].replace('# ', ''); ---- - -{title} - - - - diff --git a/packages/website/src/pages/index.astro b/packages/website/src/pages/index.astro deleted file mode 100644 index a5d5ca4fa..000000000 --- a/packages/website/src/pages/index.astro +++ /dev/null @@ -1,102 +0,0 @@ ---- -import { assertSecret } from '@blitzkit/core'; -import { Box, Flex, Heading } from '@radix-ui/themes'; -import { AesonPlug } from '../components/AesonPlug'; -import HomeHero from '../components/HomeHero.astro'; -import { Plugs } from '../components/Plugs.tsx'; -import ToolCard from '../components/ToolCard.astro'; -import { - compareTool, - discordTool, - embedTool, - galleryTool, - moreTool, - performanceTool, - sessionTool, - tankopediaTool, - tierListTool, -} from '../constants/tools'; -import BaseLayout from '../layouts/BaseLayout.astro'; - -const branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH); ---- - - - - - - - { - branch === 'preview' && ( - - - - ) - } - - - - - - - {/* */} - - - - - - - - - - - - - - - - - - - - - - - - - Other cool projects - - - - - - - - -
-
diff --git a/packages/website/src/pages/settings/_index.tsx b/packages/website/src/pages/settings/_index.tsx deleted file mode 100644 index d1693ebe8..000000000 --- a/packages/website/src/pages/settings/_index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Flex, Heading, Switch, Text } from '@radix-ui/themes'; -import { PageWrapper } from '../../components/PageWrapper'; -import { App } from '../../stores/app'; - -export function Page() { - return ( - - - - ); -} - -function Content() { - const developerMode = App.useDeferred((state) => state.developerMode, false); - const appStore = App.useStore(); - - return ( - - - - Advanced - - - Developer mode - - appStore.setState({ developerMode: checked }) - } - /> - - - - - ); -} diff --git a/packages/website/src/pages/settings/index.astro b/packages/website/src/pages/settings/index.astro deleted file mode 100644 index e3e75c5d4..000000000 --- a/packages/website/src/pages/settings/index.astro +++ /dev/null @@ -1,8 +0,0 @@ ---- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import { Page } from './_index'; ---- - - - - diff --git a/packages/website/src/pages/tools/charts/_index.tsx b/packages/website/src/pages/tools/charts/_index.tsx deleted file mode 100644 index 1dcd37693..000000000 --- a/packages/website/src/pages/tools/charts/_index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Flex } from '@radix-ui/themes'; -import { ChartsControl } from '../../../components/Charts/ChartsControl'; -import { PlayersInPeriod } from '../../../components/Charts/PlayersInPeriod'; -import { PageWrapper } from '../../../components/PageWrapper'; -import { FilterControl } from '../../../components/TankSearch/components/FilterControl'; -import { Charts } from '../../../stores/charts'; - -export function Page() { - return ( - - - - ); -} - -function Content() { - return ( - - - - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/charts/index.astro b/packages/website/src/pages/tools/charts/index.astro deleted file mode 100644 index bf78728e5..000000000 --- a/packages/website/src/pages/tools/charts/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import { chartsTool } from '../../../constants/tools'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; ---- - - - - diff --git a/packages/website/src/pages/tools/compare/index.astro b/packages/website/src/pages/tools/compare/index.astro deleted file mode 100644 index 518668228..000000000 --- a/packages/website/src/pages/tools/compare/index.astro +++ /dev/null @@ -1,12 +0,0 @@ ---- -import ToolOpenGraph from '../../../components/ToolOpenGraph.astro'; -import { compareTool } from '../../../constants/tools'; -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import { Page } from './_index'; ---- - - - - - - diff --git a/packages/website/src/pages/tools/embed/index.astro b/packages/website/src/pages/tools/embed/index.astro deleted file mode 100644 index 70f7dcf2c..000000000 --- a/packages/website/src/pages/tools/embed/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import { embedTool } from '../../../constants/tools'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; ---- - - - - diff --git a/packages/website/src/pages/tools/gallery/_index.tsx b/packages/website/src/pages/tools/gallery/_index.tsx deleted file mode 100644 index b2f6bb97f..000000000 --- a/packages/website/src/pages/tools/gallery/_index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { CalendarIcon } from '@radix-ui/react-icons'; -import { Callout, Flex } from '@radix-ui/themes'; -import { GalleryList, type Avatar } from '../../../components/Gallery/List'; -import { GallerySearch } from '../../../components/Gallery/Search'; -import { PageWrapper } from '../../../components/PageWrapper'; -import { GalleryEphemeral } from '../../../stores/galleryEphemeral'; - -interface PageProps { - avatars: Avatar[]; - lastUpdated: string; -} - -export function Page({ avatars, lastUpdated }: PageProps) { - return ( - - - - - - - Last updated on {lastUpdated} - - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/gallery/index.astro b/packages/website/src/pages/tools/gallery/index.astro deleted file mode 100644 index b917eca87..000000000 --- a/packages/website/src/pages/tools/gallery/index.astro +++ /dev/null @@ -1,26 +0,0 @@ ---- -import type { Avatar } from '../../../components/Gallery/List'; -import { galleryTool } from '../../../constants/tools'; -import { fetchGlossary } from '../../../core/blitz/fetchGlossary'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; - -const glossary = await fetchGlossary(); -const avatars = Object.entries(glossary) - .filter( - ([key, value]) => - key.startsWith('avatar') && - !key.endsWith('_part') && - value.image_url !== null, - ) - .map( - ([key, value]) => - ({ id: key.replace('avatar_', ''), name: value.title }) satisfies Avatar, - ) - .sort((a, b) => a.name.localeCompare(b.name)); -const lastUpdated = new Date().toLocaleDateString(); ---- - - - - diff --git a/packages/website/src/pages/tools/performance/_index.tsx b/packages/website/src/pages/tools/performance/_index.tsx deleted file mode 100644 index 86aea4d74..000000000 --- a/packages/website/src/pages/tools/performance/_index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { PageWrapper } from '../../../components/PageWrapper'; -import { PerformanceInfo } from '../../../components/Performance/PerformanceInfo'; -import { PlayerCountControl } from '../../../components/Performance/PlayerCountControl'; -import { TankTable } from '../../../components/Performance/Table'; -import { FilterControl } from '../../../components/TankSearch/components/FilterControl'; -import { TankPerformanceEphemeral } from '../../../stores/tankPerformanceEphemeral'; -import { TankPerformanceSort } from '../../../stores/tankPerformanceSort'; -import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps'; - -export function Page({ skeleton }: MaybeSkeletonComponentProps) { - return ( - - - - - - ); -} - -function Content({ skeleton }: MaybeSkeletonComponentProps) { - return ( - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/performance/index.astro b/packages/website/src/pages/tools/performance/index.astro deleted file mode 100644 index e9568b801..000000000 --- a/packages/website/src/pages/tools/performance/index.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import ToolOpenGraph from '../../../components/ToolOpenGraph.astro'; -import { performanceTool } from '../../../constants/tools'; -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import { Page } from './_index'; ---- - - - - - - - - diff --git a/packages/website/src/pages/tools/session/index.astro b/packages/website/src/pages/tools/session/index.astro deleted file mode 100644 index cc086bdeb..000000000 --- a/packages/website/src/pages/tools/session/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import { sessionTool } from '../../../constants/tools'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; ---- - - - - diff --git a/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx b/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx deleted file mode 100644 index ed1ca3be8..000000000 --- a/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { TankType } from '@blitzkit/core'; -import { PageWrapper } from '../../../../components/PageWrapper'; -import { CalloutsSection } from '../../../../components/Tankopedia/CalloutsSection'; -import { CharacteristicsSection } from '../../../../components/Tankopedia/CharacteristicsSection'; -import { GameModeSection } from '../../../../components/Tankopedia/GameModeSection'; -import { HeroSection } from '../../../../components/Tankopedia/HeroSection'; -import { HistorySection } from '../../../../components/Tankopedia/HistorySection'; -import { MetaSection } from '../../../../components/Tankopedia/MetaSection'; -import { TankopediaCharts } from '../../../../components/Tankopedia/TankopediaCharts'; -import { TechTreeSection } from '../../../../components/Tankopedia/TechTreeSection'; -import { VideoSection } from '../../../../components/Tankopedia/VideoSection'; -import { awaitableModelDefinitions } from '../../../../core/awaitables/modelDefinitions'; -import { awaitableProvisionDefinitions } from '../../../../core/awaitables/provisionDefinitions'; -import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; -import { App } from '../../../../stores/app'; -import { Duel } from '../../../../stores/duel'; -import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral'; -import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent'; -import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps'; - -type PageProps = MaybeSkeletonComponentProps & { - id: number; -}; - -const [tankDefinitions, provisionDefinitions, modelDefinitions] = - await Promise.all([ - awaitableTankDefinitions, - awaitableProvisionDefinitions, - awaitableModelDefinitions, - ]); - -export function Page({ id, skeleton }: PageProps) { - const tank = tankDefinitions.tanks[id]; - const model = modelDefinitions.models[id]; - - return ( - - - - - - - - - {tank.type === TankType.RESEARCHABLE && !tank.deprecated && ( - - )} - - - - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/tankopedia/_index.tsx b/packages/website/src/pages/tools/tankopedia/_index.tsx deleted file mode 100644 index 75bd39002..000000000 --- a/packages/website/src/pages/tools/tankopedia/_index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { PageWrapper } from '../../../components/PageWrapper'; -import { TankSearch } from '../../../components/TankSearch'; -import { App } from '../../../stores/app'; -import { TankopediaPersistent } from '../../../stores/tankopediaPersistent'; - -export function Page() { - return ( - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/tankopedia/index.astro b/packages/website/src/pages/tools/tankopedia/index.astro deleted file mode 100644 index 6e2055143..000000000 --- a/packages/website/src/pages/tools/tankopedia/index.astro +++ /dev/null @@ -1,11 +0,0 @@ ---- -import { tankopediaTool } from '../../../constants/tools'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; ---- - - - - - - diff --git a/packages/website/src/pages/tools/tier-list/_index.tsx b/packages/website/src/pages/tools/tier-list/_index.tsx deleted file mode 100644 index 2cfe0749a..000000000 --- a/packages/website/src/pages/tools/tier-list/_index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { PageWrapper } from '../../../components/PageWrapper'; -import { FilterControl } from '../../../components/TankSearch/components/FilterControl'; -import { TierListControls } from '../../../components/TierList/Controls'; -import { TierListTable } from '../../../components/TierList/Table'; -import { TierListTiles } from '../../../components/TierList/Tiles'; -import { URLManager } from '../../../components/TierList/URLManager'; -import { TankopediaPersistent } from '../../../stores/tankopediaPersistent'; -import { TierList } from '../../../stores/tierList'; - -export function Page() { - return ( - - - - - - ); -} - -function Content() { - return ( - - - - - - - - ); -} diff --git a/packages/website/src/pages/tools/tier-list/index.astro b/packages/website/src/pages/tools/tier-list/index.astro deleted file mode 100644 index 552b02045..000000000 --- a/packages/website/src/pages/tools/tier-list/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import { tierListTool } from '../../../constants/tools'; -import ToolLayout from '../../../layouts/ToolLayout.astro'; -import { Page } from './_index'; ---- - - - - diff --git a/packages/website/src/stores/compareEphemeral.ts b/packages/website/src/stores/compareEphemeral.ts index 97d0c4f6e..19f404493 100644 --- a/packages/website/src/stores/compareEphemeral.ts +++ b/packages/website/src/stores/compareEphemeral.ts @@ -11,7 +11,7 @@ export interface CompareEphemeral { members: CompareMember[]; sorting?: { direction: 'ascending' | 'descending'; - by: string; + by: number; }; } diff --git a/packages/website/src/stores/embedState/index.ts b/packages/website/src/stores/embedState/index.ts index 91ed41320..28412a65d 100644 --- a/packages/website/src/stores/embedState/index.ts +++ b/packages/website/src/stores/embedState/index.ts @@ -94,8 +94,8 @@ export type EmbedConfigItem = ( type: EmbedItemType.Enum; default: string; options: { - label: string; value: string; + locales: Record; }[]; } ) & { diff --git a/packages/website/src/stores/tankPerformanceSort/constants.ts b/packages/website/src/stores/tankPerformanceSort/constants.ts index dd519eb98..273d6bff4 100644 --- a/packages/website/src/stores/tankPerformanceSort/constants.ts +++ b/packages/website/src/stores/tankPerformanceSort/constants.ts @@ -1,21 +1,6 @@ +import en from '@blitzkit/i18n/strings/en.json'; import type { TankPerformanceSortType } from '.'; -export const TankPerformanceSortTypeNames = { - winrate: 'Winrate', - players: 'Players', - damage: 'Damage', - survival: 'Survival', - xp: 'XP', - kills: 'Kills', - spots: 'Spots', - accuracy: 'Accuracy', - shots: 'Shots', - hits: 'Hits', - damageRatio: 'Dmg. ratio', - damageTaken: 'Dmg. taken', - capturePoints: 'Cap points', -} as const; - export const TankPerformanceSortTypeNamesArray = Object.keys( - TankPerformanceSortTypeNames, + en.website.tools.performance.table.stats, ) as TankPerformanceSortType[]; diff --git a/packages/website/src/stores/tankPerformanceSort/index.ts b/packages/website/src/stores/tankPerformanceSort/index.ts index e6a2f8c9a..78b16ce84 100644 --- a/packages/website/src/stores/tankPerformanceSort/index.ts +++ b/packages/website/src/stores/tankPerformanceSort/index.ts @@ -1,8 +1,9 @@ +import type en from '@blitzkit/i18n/strings/en.json'; import { create } from 'zustand'; import { createContextualStore } from '../../core/zustand/createContextualStore'; -import { TankPerformanceSortTypeNames } from './constants'; -export type TankPerformanceSortType = keyof typeof TankPerformanceSortTypeNames; +export type TankPerformanceSortType = + keyof typeof en.website.tools.performance.table.stats; export interface TankPerformanceSort { type: TankPerformanceSortType; diff --git a/packages/website/src/stores/tankopediaPersistent/constants.ts b/packages/website/src/stores/tankopediaPersistent/constants.ts index f06f1d624..49bc4bda1 100644 --- a/packages/website/src/stores/tankopediaPersistent/constants.ts +++ b/packages/website/src/stores/tankopediaPersistent/constants.ts @@ -32,38 +32,7 @@ export const SORT_UNITS: Record = { 'survivability.viewRange': 'm', 'survivability.volume': 'm^3', }; -export const SORT_NAMES = { - 'meta.none': 'none', - 'survivability.health': 'health', - 'survivability.viewRange': 'view range', - 'survivability.camouflageStill': 'camouflage still', - 'survivability.camouflageMoving': 'camouflage moving', - 'survivability.camouflageShooting': 'camouflage shooting', - 'survivability.volume': 'volume', - 'survivability.length': 'length', - 'fire.dpm': 'standard DPM', - 'fire.dpmPremium': 'premium DPM', - 'fire.reload': 'reload', - 'fire.caliber': 'caliber', - 'fire.standardPenetration': 'standard penetration', - 'fire.premiumPenetration': 'premium penetration', - 'fire.damage': 'damage', - 'fire.moduleDamage': 'module damage', - 'fire.shellVelocity': 'shell velocity', - 'fire.shellRange': 'shell range', - 'fire.shellCapacity': 'shell capacity', - 'fire.aimTime': 'aim time', - 'fire.dispersionStill': 'dispersion still', - 'fire.dispersionMoving': 'dispersion moving', - 'fire.gunDepression': 'gun depression', - 'fire.gunElevation': 'gun elevation', - 'maneuverability.forwardsSpeed': 'forwards speed', - 'maneuverability.backwardsSpeed': 'backwards speed', - 'maneuverability.power': 'power', - 'maneuverability.powerToWeight': 'power to weight', - 'maneuverability.weight': 'weight', - 'maneuverability.traverseSpeed': 'traverse speed', -} as const; + export enum TankopediaDisplay { Model, DynamicArmor, diff --git a/packages/website/src/stores/tankopediaPersistent/index.ts b/packages/website/src/stores/tankopediaPersistent/index.ts index 26807b389..e27a43502 100644 --- a/packages/website/src/stores/tankopediaPersistent/index.ts +++ b/packages/website/src/stores/tankopediaPersistent/index.ts @@ -1,10 +1,10 @@ +import type en from '@blitzkit/i18n/strings/en.json'; import * as lodash from 'lodash-es'; import { create } from 'zustand'; import { persist, subscribeWithSelector } from 'zustand/middleware'; import { createContextualStore } from '../../core/zustand/createContextualStore'; -import { SORT_NAMES } from './constants'; -export type TankopediaSortBy = keyof typeof SORT_NAMES; +export type TankopediaSortBy = keyof typeof en.website.common.tank_search.sort; export type TankopediaSortDirection = 'ascending' | 'descending'; export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only'; diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json index b0406060e..db4d5dcac 100644 --- a/packages/website/tsconfig.json +++ b/packages/website/tsconfig.json @@ -2,6 +2,7 @@ "extends": ["../../tsconfig.json", "astro/tsconfigs/base"], "compilerOptions": { "jsx": "react-jsx", - "jsxImportSource": "react" + "jsxImportSource": "react", + "types": ["vite/client"] } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2d169b953..f4bced458 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -9,28 +9,15 @@ datasource db { } model User { - blitz_id Int @id @map("blitz") // map: "blitz" was too vague - last_used DateTime? @default(now()) - tank_votes TankVote[] + blitz_id BigInt @id @map("blitz") // map: "blitz" was too vague + last_used DateTime? @default(now()) @@map("Users") // map: "Users" was mistakenly plural } -model TankVote { - id Int @id - last_updated DateTime @default(now()) - easiness Int? - firepower Int? - maneuverability Int? - survivability Int? - - user User @relation(fields: [user_blitz_id], references: [blitz_id]) - user_blitz_id Int -} - model DiscordUser { discord_id BigInt @id @map("discord") // map: "discord" was too vague - blitz_id Int @map("blitz") // map: "blitz" was too vague + blitz_id BigInt @map("blitz") // map: "blitz" was too vague @@map("DiscordUsers") // map: "DiscordUsers" was mistakenly plural } diff --git a/submodules/blitzkit-closed b/submodules/blitzkit-closed index c1d631764..31937a9f1 160000 --- a/submodules/blitzkit-closed +++ b/submodules/blitzkit-closed @@ -1 +1 @@ -Subproject commit c1d631764f74f8050660aa95c1da071baaadde43 +Subproject commit 31937a9f1e83e588d84a2ce1616870a442d2a7a4