Compare commits

...
117 Commits
Author SHA1 Message Date
tresabhi cf04fe21b3 fix: logs 2025-01-18 13:59:14 -06:00
tresabhi 84eb3c2da3 fix: version 2025-01-18 13:49:37 -06:00
tresabhi 22bf2dc1d8 feat: logs 2025-01-18 13:49:18 -06:00
tresabhi b9c339daa7 fix: search keys 2025-01-18 13:47:31 -06:00
tresabhi ef3006cac1 fix: double full stops 2025-01-18 13:42:52 -06:00
TrèsAbhi e482fd40c1 Update applyPatches.ts 2025-01-18 11:41:45 -06:00
TrèsAbhi cc4569966e Update applyPatches.ts 2025-01-18 09:19:38 -06:00
tresabhi 3708a3ced7 fix: handle deleted tanks 2025-01-17 20:29:33 -06:00
tresabhi 0ae59c2fd7 fix: check dir exists 2025-01-17 20:15:09 -06:00
tresabhi 7027f63236 feat: prune 2025-01-17 19:56:46 -06:00
TrèsAbhi 6725e79021 New Crowdin updates (#140) 2025-01-17 19:36:39 -06:00
tresabhi 5ca3cee9da feat: verbose 2025-01-17 19:22:02 -06:00
tresabhi 602a5a1e99 fix: move env 2025-01-17 19:19:45 -06:00
tresabhi bfbf3bdc39 fix: no double dvpl 2025-01-17 19:17:39 -06:00
tresabhi 644686582e fix: icon api 2025-01-17 19:06:07 -06:00
TrèsAbhi 1c00bc81f8 New Crowdin updates (#139)
* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)
2025-01-17 12:46:05 -06:00
TrèsAbhi ac3b69bec5 Update assets.yml 2025-01-17 12:45:12 -06:00
TrèsAbhi 3e14c943b6 Update assets.yml 2025-01-16 23:05:13 -06:00
TrèsAbhi d33633be2c Update assets.yml 2025-01-16 22:15:24 -06:00
TrèsAbhi 4bf70d02d0 Update Crowdin configuration file 2025-01-16 20:48:29 -06:00
tresabhi 377bcf0e38 fix: env 2025-01-16 20:44:55 -06:00
tresabhi 884b6d4318 fix: run tests even on dev 2025-01-16 20:36:49 -06:00
tresabhi ee94d762d0 fix: paths 2025-01-16 20:34:28 -06:00
tresabhi bcde319ac1 feat: move docs 2025-01-16 20:23:39 -06:00
tresabhi a4828774ca feat: translate compare 2025-01-16 20:14:34 -06:00
tresabhi e59725d763 feat: logs 2025-01-16 19:59:24 -06:00
tresabhi a62179a853 Merge branch 'main' into dev 2025-01-16 19:58:31 -06:00
TrèsAbhi 38ba940a98 New Crowdin updates (#138) 2025-01-16 19:56:42 -06:00
tresabhi a07d6c775e fix: remove writefile 2025-01-16 19:55:18 -06:00
tresabhi f9f7468544 feat: replace $ 2025-01-16 14:22:30 -06:00
tresabhi 98724df711 feat: update bot chunk 4 2025-01-16 14:10:28 -06:00
tresabhi 5499cb5cca feat: update bot chunk 3 2025-01-16 13:53:29 -06:00
tresabhi 122b3f6478 feat: update bot chunk 2 2025-01-16 13:48:13 -06:00
tresabhi d2b24fc953 feat: update bot chunk 1 2025-01-15 20:14:05 -06:00
tresabhi bb557a7dd7 feat: use thing 2025-01-15 14:21:27 -06:00
tresabhi f1612e8fe3 feat: path 2025-01-15 14:16:39 -06:00
tresabhi d12f4fb00e feat: logs 2025-01-15 14:06:28 -06:00
tresabhi ad555ece9a fix: warning 2025-01-15 14:00:06 -06:00
tresabhi 9aee8feaf4 feat: translate embeds 2025-01-15 13:55:24 -06:00
tresabhi baa16840df feat: footer lang 2025-01-15 13:10:57 -06:00
tresabhi 4e80490fd4 feat: localize sessin 2025-01-14 20:32:17 -06:00
tresabhi 6233c16b88 feat: fixed localize tankopedia chars 2025-01-14 20:19:37 -06:00
tresabhi c4f1d9f3b0 feat: localize tankopedia chars 2025-01-14 20:13:38 -06:00
tresabhi 2c365cdea0 feat: localize tankopedia config 2025-01-14 19:37:03 -06:00
tresabhi 18afa4cd28 feat: localize tankopedia reviews 2025-01-14 19:28:14 -06:00
tresabhi ef9778ea96 feat: localize game assets 2025-01-14 19:22:41 -06:00
tresabhi d8459d373c feat: start of localized proto strings 2025-01-14 14:38:41 -06:00
tresabhi 7900c61a9b feat: localize tankopedia tech tree 2025-01-14 14:13:00 -06:00
tresabhi ceb7842d49 feat: localize tankopedia meta section 2025-01-14 14:04:46 -06:00
tresabhi e0c897025e feat: localize tankopedia sandbox 2025-01-13 20:56:30 -06:00
tresabhi ade6ec8af1 feat: localize tank search 2025-01-13 20:06:43 -06:00
tresabhi 06cbe1e733 feat: localize language 2025-01-10 20:46:15 -06:00
tresabhi cb3e759f75 feat: remove redundant /en 2025-01-10 20:38:28 -06:00
tresabhi e4558d978c fix: tool og strings 2025-01-10 20:30:08 -06:00
tresabhi 260d819c77 feat: auto prefixor 2025-01-10 14:37:29 -06:00
tresabhi ffe58de179 Merge branch 'main' into dev 2025-01-10 14:04:54 -06:00
tresabhi 6fbe1d6339 feat: locales 2025-01-10 14:04:42 -06:00
TrèsAbhi 835d03d12c Delete packages/core/lang/hr.json 2025-01-10 14:00:38 -06:00
TrèsAbhi 2b208fa6a0 New Crowdin updates (#137) 2025-01-10 13:59:59 -06:00
tresabhi 66d63d5c60 Merge branch 'main' into dev 2025-01-10 13:49:28 -06:00
tresabhi 6ccffa0458 feat: enable vercel 2025-01-10 10:38:02 -06:00
tresabhi 4c0c10e39e feat: hide hot tanks 2025-01-10 10:12:31 -06:00
tresabhi 3d41c56c73 fix: use en 2025-01-10 10:10:59 -06:00
TrèsAbhi e0dda567f4 Update credits.md 2025-01-07 23:16:07 -06:00
tresabhi 91b367cd70 feat: start of tankopedia translation 2025-01-07 13:59:33 -06:00
tresabhi 5a65fee1d1 feat: translate tank search 2025-01-07 13:50:06 -06:00
tresabhi c7856364ad fix: some linting 2025-01-07 13:20:20 -06:00
tresabhi 65899a06c4 feat: move docs up 2025-01-07 13:12:22 -06:00
tresabhi 571ae81306 feat: translate tier list 2025-01-07 13:09:05 -06:00
tresabhi 6b2dda0f9d feat: translate gallery 2025-01-07 13:00:43 -06:00
tresabhi 9de41529d1 feat: localize performance 2025-01-07 12:47:55 -06:00
tresabhi 7dfdf4c11b feat: start of perforamnce 2025-01-06 20:31:21 -06:00
tresabhi db4132f666 feat: better lang selector 2025-01-06 20:14:33 -06:00
tresabhi d9688f6051 feat: homepage 2025-01-06 20:09:11 -06:00
TrèsAbhi 12e89a6b65 Update credits.md 2025-01-05 11:39:25 -06:00
TrèsAbhi 2d524cd273 Update Crowdin configuration file 2025-01-05 09:14:29 -06:00
tresabhi 1d44ecab18 Merge branch 'main' into dev 2025-01-03 12:36:02 -06:00
tresabhi bf9addf589 feat: iteration 2025-01-03 12:32:03 -06:00
tresabhi 4deee2d709 feat: update locales 2025-01-03 12:15:14 -06:00
tresabhi 309fda1ed1 feat: translators 2025-01-03 12:08:03 -06:00
tresabhi d6e7f35214 feat: translators 2025-01-02 19:43:13 -06:00
TrèsAbhi 7cd9131b60 Update assets.yml 2024-12-30 11:42:48 -06:00
tresabhi 599b81934e feat: simplification 2024-12-29 20:19:51 -06:00
tresabhi 3f595d0d8a Merge branch 'main' into dev 2024-12-29 20:07:12 -06:00
TrèsAbhi 9afed23bb8 feat: simplify 2024-12-29 20:06:34 -06:00
TrèsAbhi fc869f8b45 New Crowdin updates (#136) 2024-12-29 20:05:52 -06:00
TrèsAbhi cf7ba1c6b5 New translations en-us.json (Portuguese) (#135) 2024-12-29 20:04:04 -06:00
TrèsAbhi 07ca41e82a Merge pull request #134 from tresabhi/l10n_main
New Crowdin updates
2024-12-28 09:07:30 -06:00
tresabhi c487d9af61 feat: disable navbar account 2024-12-27 14:04:14 -06:00
tresabhi 9be6725043 feat: some more localized links 2024-12-27 14:03:43 -06:00
tresabhi e0daf85561 feat: some more localized links 2024-12-27 14:01:59 -06:00
tresabhi 8203db3dc2 feat: some locale links 2024-12-27 13:43:25 -06:00
tresabhi eb5527706e fix: remove dead code 2024-12-27 13:34:39 -06:00
tresabhi 1f577c2317 feat: i18n paths 2024-12-27 13:16:51 -06:00
tresabhi 68362c22b1 fix: imports 2024-12-27 12:59:56 -06:00
tresabhi 844965bcb0 feat: locale redirect 2024-12-27 12:55:52 -06:00
tresabhi 1843eb3bcf feat: move to locale 2024-12-27 12:38:38 -06:00
tresabhi 60ab845326 feat: set up locales 2024-12-26 14:36:48 -06:00
tresabhi 3bbc083b2a feat: logs 2024-12-26 14:10:41 -06:00
tresabhi 8df159b182 feat: i will do this later lol 2024-12-26 14:07:36 -06:00
tresabhi c7238b0342 feat: comments to ease users 2024-12-26 13:56:54 -06:00
tresabhi ba6006a320 feat: smaller title in header 2024-12-26 13:52:04 -06:00
tresabhi 43b9252f21 feat: hide chars 2024-12-26 13:47:51 -06:00
tresabhi d727ceaaf6 feat: more charts! 2024-12-26 13:47:06 -06:00
tresabhi eb95230a5f feat: start of charts 2024-12-23 12:48:52 -06:00
tresabhi b6fd5eaa66 feat: update readme 2024-12-23 12:37:12 -06:00
tresabhi 117f9892d6 feat: start of charts 2024-12-22 20:34:16 -06:00
tresabhi c20caf45a2 feat: rounding in blitzkit embeds 2024-12-22 20:30:08 -06:00
tresabhi 670b678348 feat: increase brightness of pbr tanks 2024-12-22 20:28:07 -06:00
tresabhi b1f0d4d844 feat: readd lobby lighting for static mode 2024-12-22 20:27:36 -06:00
tresabhi e9d1c82a60 feat: added option to hide tank under dynamic armor 2024-12-22 20:25:05 -06:00
tresabhi cc4345f1a3 fix: only show blue if 3rc saves the day 2024-12-22 20:21:23 -06:00
tresabhi 6d8326861b feat: background 2024-12-19 18:10:00 -06:00
tresabhi 8517ecfdb6 fix: app armor? 2024-12-19 13:26:25 -06:00
tresabhi a38b5d7ddd fix: remove show environment 2024-12-19 11:32:53 -06:00
tresabhi f726793819 the worst code i have ever written in my life 2024-12-19 11:27:35 -06:00
tresabhi 19531aa9ae feat: bring back good ol' 2024-12-19 11:22:38 -06:00
291 changed files with 14164 additions and 6887 deletions

No files matched your search

+28 -2
View File
@@ -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
@@ -113,11 +127,23 @@ jobs:
- name: Install dependencies
run: bun install
- name: Lint
run: bun run lint:website
- name: Download depots
uses: actions/download-artifact@v4
with:
name: depots
path: depots
- name: Build assets
run: bun run build:assets --all-targets --depot --production
- name: AppArmor hack
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
- name: Start server and build assets
run: |
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
kill $SERVER_PID || true
+3 -26
View File
@@ -26,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:
@@ -52,8 +54,7 @@ jobs:
path: dist/website
deploy-github-pages:
if: github.ref == 'refs/heads/main'
if: ${{ github.ref == 'refs/heads/main' }}
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
needs: build
@@ -65,27 +66,3 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
deploy-vercel:
name: Deploy to Vercel
needs: build
runs-on: ubuntu-latest
environment:
name: Preview
steps:
# vercel begs for the .git dir so here it is my guy
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
path: ./dist/website
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: ./dist/website
+1
View File
@@ -189,6 +189,7 @@
"unpickled",
"Unregistering",
"unrotate",
"unwrapper",
"vercel",
"vickers",
"vitepress",
+4 -14
View File
@@ -1,23 +1,13 @@
## Visit the website
## BlitzKit
Your premier source for all things Blitz.
### https://blitzkit.app/
![BlitzKit website](https://i.imgur.com/m8e39Rv.png)
# BlitzKit
[![built with Codeium](https://codeium.com/badges/main)](https://codeium.com)
Tools for everything World of Tanks Blitz
![BlitzKit website](https://i.imgur.com/KaxUxmt.png)
## Invite the bot
Go to [application directory for BlitzKit](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go.
![BlitzKit application directory](https://i.imgur.com/YWCtOuJ.png)
## Visit the documentation website
### https://tresabhi.github.io/blitzkit/
![BlitzKit documentation website](https://i.imgur.com/hm3KTsF.png)
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,3 +1,3 @@
files:
- source: /packages/core/lang/en-US.json
- source: /packages/i18n/strings/en.json
translation: /%original_path%/%locale%.json
+2 -1
View File
@@ -5,6 +5,7 @@ BlitzKit is a one man band **TrèsAbhi** but I am far from solo.
| Role | People |
| ------------------------------ | ------------------------------ |
| Bot verification | S0AP |
| General testing | Prince_NA |
| Bot advertisement platform | S0AP |
| Bot rendering pipeline | Vovko |
| Game asset decompression | Maddox |
@@ -12,7 +13,7 @@ BlitzKit is a one man band **TrèsAbhi** but I am far from solo.
| Game file layout | Aeson |
| 3-caliber rule edge cases | Minitelrose, ChickenMan7777 |
| Embed UX | \_34, HisRoyalFatness, Synx |
| Tankopedia pre-release testing | [OPTML], Aeson, \_34 |
| Tankopedia pre-release testing | [OPTML], Aeson, \_34, xkdkw |
| Website advertisement platform | Aeson |
| Compare pre-release testing | [OPTML] |
| Redacted | GonnaHetzMe |
+15
View File
@@ -0,0 +1,15 @@
# BlitzKit 1.26.6
This update only affects the website.
## Features
- Added an option to hide the tank model beneath the armor
## Fixes
- 3RC is now only display when it is used to avoid a ricochet in the advanced armor highlighting mode
- Fixed the excessively sharp lighting in static mode
- Brightness for non-PBR tanks is a bit better now
- Decimal places are now properly rounded in BlitzKit link embeds
- The titles in the Tankopedia pages aren't too massive now
+14
View File
@@ -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
+7
View File
@@ -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
+3 -2
View File
@@ -1,7 +1,7 @@
{
"name": "blitzkit",
"private": true,
"version": "1.26.5",
"version": "1.27.1",
"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": {
+1
View File
@@ -5,6 +5,7 @@
"types": "src/types.d.ts",
"dependencies": {
"@blitzkit/core": "workspace:*",
"@blitzkit/i18n": "workspace:*",
"satori": "^0.10.14",
"@resvg/resvg-js": "^2.6.2",
"discord.js": "^14.16.3",
+2 -2
View File
@@ -28,10 +28,10 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
async handler(interaction) {
const subcommand = interaction.options.getSubcommand();
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (subcommand === 'commands') {
return t`bot.commands.about.subcommands.commands.body${(
return `${strings.bot.commands.about.subcommands.commands.body}${(
await Promise.all(COMMANDS_RAW)
)
.sort((a, b) =>
+17 -13
View File
@@ -3,17 +3,18 @@ import {
Region,
regionToRegionSubdomain,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
import { chunkLines } from '../core/discord/chunkLines';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
interface Auction {
type Auction = null | {
count: number;
has_next: boolean;
results: Result[];
}
};
type Result =
| {
@@ -75,7 +76,7 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('auction'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const region: Region = 'com';
const pageSize = 80;
@@ -87,16 +88,16 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
pageSize
}&type[]=vehicle&saleable=${saleable}`,
).then((response) => response.json() as Promise<Auction>);
const saleableTanks = data.results.filter((result) => result.available);
if (saleableTanks.length === 0) {
return t`bot.commands.auction.errors.no_auction`;
if (data === null) {
return strings.bot.commands.auction.errors.no_auction;
}
const saleableTanks = data.results.filter((result) => result.available);
const nextPricesTimestamp = saleableTanks[0].next_price_timestamp;
const title = `# ${t`bot.commands.auction.body.title`}`;
const subtitle = `${translate('bot.commands.auction.body.subtitle', [
const title = `# ${strings.bot.commands.auction.body.title}`;
const subtitle = `${literals(strings.bot.commands.auction.body.subtitle, [
`${saleableTanks.length} / ${data.results.length}`,
`<t:${nextPricesTimestamp}:R>`,
'<https://wotblitz.com/auction/>',
@@ -112,15 +113,18 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
const next =
data.next_price === null
? ''
: `\n-# ${translate('bot.commands.auction.body.next', [
: `\n-# ${literals(strings.bot.commands.auction.body.next, [
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
interaction.locale,
)}`,
])}`;
const available = translate('bot.commands.auction.body.available', [
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
]);
const available = literals(
strings.bot.commands.auction.body.available,
[
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
],
);
const isOut = data.current_count === 0;
const outString = isOut ? '~~' : '';
const isLow = data.current_count <= WARNING_COUNT;
+17 -13
View File
@@ -7,6 +7,7 @@ import {
getWN8Percentile,
TankDefinition,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { chunk } from 'lodash-es';
import * as Breakdown from '../../components/Breakdown';
@@ -32,7 +33,7 @@ export async function renderBreakdown(
filters: StatFilters,
locale: Locale,
) {
const { t, translate } = translator(locale);
const { strings, unwrap } = translator(locale);
const awaitedTankDefinitions = await tankDefinitions;
const awaitedTankAverages = await blitzStarsTankAverages;
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
@@ -48,7 +49,7 @@ export async function renderBreakdown(
const orderedCareerWN8Full: (number | undefined)[] = [];
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
filteredOrder.forEach((id) => {
@@ -136,21 +137,21 @@ export async function renderBreakdown(
<Breakdown.Row
key={id}
displayType="summary"
title={t`bot.commands.breakdown.body.total`}
title={strings.bot.commands.breakdown.body.total}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: currentBattles,
career: careerBattles,
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * currentWinrate).toFixed(2)}%`,
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: currentWinrate - careerWinrate,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -165,7 +166,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(currentDamage).toLocaleString(),
career: Math.round(careerDamage).toLocaleString(),
delta: currentDamage - careerDamage,
@@ -190,17 +191,20 @@ export async function renderBreakdown(
class={tankDefinition?.class}
type={tankDefinition?.type}
title={
tankDefinition?.name ??
translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
tankDefinition
? unwrap(tankDefinition.name)
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
`${id}`,
])
}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: current.battles.toLocaleString(),
career: career.battles.toLocaleString(),
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * (current.wins / current.battles)).toFixed(
2,
)}%`,
@@ -209,7 +213,7 @@ export async function renderBreakdown(
current.wins / current.battles - career.wins / career.battles,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -224,7 +228,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(
current.damage_dealt / current.battles,
).toLocaleString(),
+6 -6
View File
@@ -11,17 +11,17 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('debug'),
async handler(interaction) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const currentTime = new Date().getTime();
const uptime = currentTime - executionStart;
const list = [
[t`bot.commands.debug.body.version`, packageJSON.version],
[strings.bot.commands.debug.body.version, packageJSON.version],
[
t`bot.commands.debug.body.shard`,
client.shard?.ids[0] ?? t`bot.commands.debug.body.shard.default`,
strings.bot.commands.debug.body.shard,
client.shard?.ids[0] ?? strings.bot.commands.debug.body.shard.default,
],
[
t`bot.commands.debug.body.uptime`,
strings.bot.commands.debug.body.uptime,
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor(
(uptime / 1000 / 60) % 60,
)}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
@@ -29,7 +29,7 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
)}ms`,
],
];
return t`bot.commands.debug.body.title${list
return `${strings.bot.commands.debug.body.title}${list
.map(([key, value]) => `- ${key}: ${value}`)
.join('\n')}`;
},
+12 -9
View File
@@ -1,4 +1,5 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { GenericStats } from '../components/GenericStats';
@@ -19,30 +20,31 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('inactive')
.addStringOption(addClanChoices)
.addNumberOption((option) => {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('threshold')
.setNameLocalizations(
localizationObject(
'bot.commands.inactive.options.threshold',
(strings) => strings.bot.commands.inactive.options.threshold.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.inactive.options.threshold.description'),
strings.bot.commands.inactive.options.threshold.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.inactive.options.threshold.description',
(strings) =>
strings.bot.commands.inactive.options.threshold.description,
),
)
.setMinValue(0);
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction);
const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
@@ -63,7 +65,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) =>
[
name,
translate('bot.commands.inactive.body.listing', [
literals(strings.bot.commands.inactive.body.listing, [
days.toFixed(0),
]),
] as [string, string],
@@ -75,9 +77,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={clanInfo.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${translate('bot.commands.inactive.body.subtitle', [
`${threshold}`,
])} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${literals(
strings.bot.commands.inactive.body.subtitle,
[`${threshold}`],
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
{!hasInactiveMembers && (
+7 -7
View File
@@ -22,7 +22,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const discordId = BigInt(interaction.user.id);
const accountInfo = await getAccountInfo(region, id);
@@ -120,9 +120,9 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
height: 16,
}}
/>
<span
style={{ fontSize: 16 }}
>{t`bot.commands.link.body.accounts_linked`}</span>
<span style={{ fontSize: 16 }}>
{strings.bot.commands.link.body.accounts_linked}
</span>
</div>
<div
@@ -174,7 +174,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
{clanAccountInfo?.clan
? `[${clanAccountInfo?.clan?.tag}] • `
: ''}
{translate(`common.regions.normal.${region}`)}
{strings.common.regions.normal[region]}
</span>
</div>
</div>
@@ -185,8 +185,8 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
</CommandWrapper>,
embedInfo(
t`bot.commands.link.embed.title`,
t`bot.commands.link.embed.description`,
strings.bot.commands.link.embed.title,
strings.bot.commands.link.embed.description,
),
];
},
+5 -4
View File
@@ -6,6 +6,7 @@ import {
getAccountInfo,
getTankStats,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import {
gameDefinitions,
@@ -27,14 +28,14 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const tier = Number(interaction.options.getString('tier'));
const { id, region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id);
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
const filteredTanks = (
@@ -59,7 +60,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
const awaitedTankDefinitions = await tankDefinitions;
const awaitedGameDefinitions = await gameDefinitions;
const title = translate('bot.commands.owned_tanks.body.title', [
const title = literals(strings.bot.commands.owned_tanks.body.title, [
markdownEscape(accountInfo.nickname),
TIER_ROMAN_NUMERALS[tier],
]);
@@ -77,7 +78,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
)
.map(
(tank) =>
`[${flags[tank.nation]} ${markdownEscape(tank.name)}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
);
const lines = [title, ...tankList];
+7 -6
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -7,26 +8,26 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('permissions'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
// friday the 13th permissions easter egg
const isFridayThe13th =
new Date().getDay() === 5 && new Date().getDate() === 13;
const permissions = [
[
interaction.appPermissions?.has('ViewChannel'),
t`bot.commands.permissions.body.view_channels`,
strings.bot.commands.permissions.body.view_channels,
],
[
interaction.appPermissions?.has('ReadMessageHistory'),
t`bot.commands.permissions.body.read_message_history`,
strings.bot.commands.permissions.body.read_message_history,
],
[
interaction.appPermissions?.has('AttachFiles'),
t`bot.commands.permissions.body.attach_files`,
strings.bot.commands.permissions.body.attach_files,
],
[
interaction.appPermissions?.has('UseExternalEmojis'),
t`bot.commands.permissions.body.external_emojis`,
strings.bot.commands.permissions.body.external_emojis,
],
];
const body = permissions
@@ -39,7 +40,7 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
})
.join('\n');
return translate('bot.commands.permissions.body.title', [body]);
return literals(strings.bot.commands.permissions.body.title, [body]);
},
});
});
+4 -3
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -12,19 +13,19 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
]),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand();
const executionStart = Date.now();
if (subcommand === 'blitzkit') {
await interaction.editReply(t`bot.commands.ping.body.pong`);
await interaction.editReply(strings.bot.commands.ping.body.pong);
} else {
await fetch('https://api.wotblitz.com/');
}
const executionTime = Date.now() - executionStart;
interaction.editReply(
translate('bot.commands.ping.body.pong_with_time', [
literals(strings.bot.commands.ping.body.pong_with_time, [
`${Math.round(executionTime)}`,
]),
);
+9 -8
View File
@@ -1,4 +1,5 @@
import { getAccountInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
@@ -15,27 +16,27 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const account = await resolvePlayerFromCommand(interaction);
const { id, region: region } = account;
const accountInfo = await getAccountInfo(region, id);
return embedInfo(
translate('bot.commands.player_info.body.title', [
literals(strings.bot.commands.player_info.body.title, [
markdownEscape(accountInfo.nickname),
]),
markdownTable([
[
t`bot.commands.player_info.body.nickname`,
strings.bot.commands.player_info.body.nickname,
`${accountInfo.nickname}`,
],
[
t`bot.commands.player_info.body.battles`,
strings.bot.commands.player_info.body.battles,
`${accountInfo.statistics.all.battles}`,
],
[
t`bot.commands.player_info.body.winrate`,
strings.bot.commands.player_info.body.winrate,
`${(
100 *
(accountInfo.statistics.all.wins /
@@ -44,17 +45,17 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
],
[],
[
t`bot.commands.player_info.body.account_id`,
strings.bot.commands.player_info.body.account_id,
`${accountInfo.account_id}`,
],
[
t`bot.commands.player_info.body.created`,
strings.bot.commands.player_info.body.created,
new Date(accountInfo.created_at * 1000).toLocaleDateString(
interaction.locale,
),
],
[
t`bot.commands.player_info.body.last_battle`,
strings.bot.commands.player_info.body.last_battle,
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
interaction.locale,
),
+8 -7
View File
@@ -9,6 +9,7 @@ import {
getRatingNeighbors,
imgur,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { SlashCommandSubcommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
@@ -53,7 +54,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const subcommand = interaction.options.getSubcommand(true) as
| 'season'
| 'today';
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
@@ -89,7 +90,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
if (statsB2Array === undefined) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -98,10 +99,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const league = getLeagueFromScore(statsB2.score);
if (!statsA) {
return t`bot.commands.rating.errors.unrecorded_stats`;
return strings.bot.commands.rating.errors.unrecorded_stats;
}
if (!statsB1) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -139,9 +140,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={accountInfo.nickname}
image={clanImage}
description={`${translate(
`bot.commands.rating.body.subtitle.${subcommand}`,
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${
strings.bot.commands.rating.body.subtitle[subcommand]
} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
<div
+67 -38
View File
@@ -14,6 +14,7 @@ import {
RatingLeaderboard,
Region,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -54,28 +55,32 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
);
function addOptions(option: SlashCommandSubcommandBuilder) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.addIntegerOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.limit`)
.setName(strings.bot.commands.rating_leaderboard.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.limit.description',
literals(
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -85,22 +90,26 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
)
.addStringOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.season`)
.setName(
strings.bot.commands.rating_leaderboard.options.season.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.season.description',
),
strings.bot.commands.rating_leaderboard.options.season
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season
.description,
),
)
.setRequired(false)
@@ -112,15 +121,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
return {
name: current
? translate(
'bot.commands.rating_leaderboard.options.season.choices.current',
? literals(
strings.bot.commands.rating_leaderboard.options.season
.choices.current,
[`${number}`],
)
: `${number}`,
value: `${number}`,
name_localizations: current
? localizationObject(
'bot.commands.rating_leaderboard.options.season.choices.current',
(strings) =>
strings.bot.commands.rating_leaderboard.options
.season.choices.current,
[`${number}`],
)
: undefined,
@@ -145,35 +157,45 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
modify(option: SlashCommandSubcommandBuilder) {
option
.addStringOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.rating_leaderboard.options.league`)
.setName(
strings.bot.commands.rating_leaderboard.options.league.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.league.description',
),
strings.bot.commands.rating_leaderboard.options.league
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.description,
),
)
.addChoices(
...LEAGUES.map(
({ name }, value) =>
({
name: translate(`common.leagues.${name}`),
name: (
strings.common.leagues as Record<string, string>
)[name],
value: `${value}`,
name_localizations: localizationObject(
`common.leagues.${name}`,
(strings) =>
(
strings.common.leagues as Record<string, string>
)[name],
),
}) satisfies APIApplicationCommandOptionChoice<string>,
),
@@ -189,7 +211,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
// BIG TODO: title seems to be in english
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
| 'neighbors';
@@ -223,7 +245,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const result = isCurrentSeason
@@ -268,8 +291,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
clan: clanData[player.id]?.clan?.tag,
nickname:
clanData[index]?.account_name ??
translate(
'bot.commands.rating_leaderboard.body.deleted_player',
literals(
strings.bot.commands.rating_leaderboard.body
.deleted_player,
[`${player.id}`],
),
}) satisfies SimplifiedPlayer,
@@ -289,12 +313,14 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
: regionRatingInfo.count) - result[result.length - 1].position;
titleName = `${
leagueInfo
? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`)
? (strings.common.leagues as Record<string, string>)[
LEAGUES[leagueInfo.index].name
]
: ''
} - ${translate(`common.regions.short.${region}`)}`;
} - ${strings.common.regions.short[region]}`;
titleImage = imgur(leagueInfo.icon);
titleDescription = translate(
'bot.commands.rating_leaderboard.body.top_players',
titleDescription = literals(
strings.bot.commands.rating_leaderboard.body.top_players,
[`${limit}`],
);
} else {
@@ -307,7 +333,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const accountInfo = await getAccountInfo(region, id);
@@ -334,8 +361,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
if (playerIndex === -1) {
throw new UserError(
translate(
'bot.commands.rating_leaderboard.body.no_participation',
literals(
strings.bot.commands.rating_leaderboard.body
.no_participation,
[markdownEscape(accountInfo.nickname), `${season}`],
),
);
@@ -373,7 +401,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined;
titleName = neighbors ? accountInfo.nickname : '';
titleDescription = t`bot.commands.rating_leaderboard.body.neighbors`;
titleDescription =
strings.bot.commands.rating_leaderboard.body.neighbors;
playersAfter =
(regionRatingInfo === null
? archivedLeaderboard.version!.value.entries.length
@@ -422,8 +451,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
{items}
{playersAfter > 0 && (
<Leaderboard.Gap
message={translate(
'bot.commands.rating_leaderboard.body.more',
message={literals(
strings.bot.commands.rating_leaderboard.body.more,
[`${playersAfter.toLocaleString(interaction.locale)}`],
)}
/>
+13 -3
View File
@@ -6,6 +6,7 @@ import {
tankIcon,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
import { clamp } from 'lodash-es';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -18,6 +19,7 @@ import { RenderConfiguration } from '../core/blitzkit/renderConfiguration';
import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
import { buttonLink } from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
@@ -218,11 +220,13 @@ function stat(value: string | number, flex = 2) {
}
async function playerListing(
locale: Locale,
player: WotInspectorReplayPlayerData,
protagonistTeam: boolean,
winningTeam: number,
recordingPlayer = false,
) {
const { unwrap } = translator(locale);
const awaitedTankAverages = await blitzStarsTankAverages;
const awaitedTankDefinitions = await tankDefinitions;
const tank = awaitedTankDefinitions.tanks[player.vehicle_descr];
@@ -275,7 +279,7 @@ async function playerListing(
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={await iconPng(tankIcon(tank.id))}
style={{
width: 32,
@@ -312,7 +316,7 @@ async function playerListing(
fontSize: 16,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
@@ -530,6 +534,7 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
)
.map((player) =>
playerListing(
interaction.locale,
player,
true,
replayData.winner_team,
@@ -553,7 +558,12 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
players
.filter((player) => player.team === antagonistTeamId)
.map((player) =>
playerListing(player, false, replayData.winner_team),
playerListing(
interaction.locale,
player,
false,
replayData.winner_team,
),
),
)}
</div>
+72 -52
View File
@@ -9,6 +9,7 @@ import {
TankType,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { escapeMarkdown, Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -28,26 +29,28 @@ import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('research')
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.target_tank`)
.setName(strings.bot.commands.research.options.target_tank.name)
.setNameLocalizations(
localizationObject(
'bot.commands.research.options.target_tank',
(strings) =>
strings.bot.commands.research.options.target_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.target_tank.description`,
strings.bot.commands.research.options.target_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.target_tank.description',
(strings) =>
strings.bot.commands.research.options.target_tank.description,
),
)
.setAutocomplete(true)
@@ -55,20 +58,22 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
)
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.starting_tank`)
.setName(strings.bot.commands.research.options.starting_tank.name)
.setNameLocalizations(
localizationObject(
'bot.commands.research.options.starting_tank',
(strings) =>
strings.bot.commands.research.options.starting_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.starting_tank.description`,
strings.bot.commands.research.options.starting_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.starting_tank.description',
(strings) =>
strings.bot.commands.research.options.starting_tank.description,
),
)
.setAutocomplete(true)
@@ -77,7 +82,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const targetTankId = await resolveTankId(
interaction.options.getString('target-tank', true),
@@ -98,50 +103,55 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
);
if (targetTankId === startingTankId) {
return translate('bot.commands.research.errors.start_end_equal', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.start_end_equal,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
if (startingTank.tier > targetTank.tier) {
return translate('bot.commands.research.errors.unordered', [
escapeMarkdown(startingTank.name),
escapeMarkdown(targetTank.name),
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.unordered, [
escapeMarkdown(unwrap(startingTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (!targetTankAncestry.includes(startingTankId)) {
return translate('bot.commands.research.errors.tanks_not_on_line', [
escapeMarkdown(targetTank.name),
escapeMarkdown(startingTank.name),
]);
return literals(
strings.bot.commands.research.errors.tanks_not_on_line,
[
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(startingTank.name)),
],
);
}
} else {
if (targetTank.type !== TankType.RESEARCHABLE) {
return translate('bot.commands.research.errors.non_tech_tree', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.non_tech_tree, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (targetTank.tier === 1) {
return translate('bot.commands.research.errors.tier_1', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.tier_1, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return translate('bot.commands.research.errors.already_researched', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.already_researched,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const foundAncestor = targetTankAncestry.find(
@@ -255,7 +265,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={nickname}
image={clanImage}
description={`${t`bot.commands.research.body.subtitle`} • ${
description={`${strings.bot.commands.research.body.subtitle} • ${
targetTank.name
}`}
/>
@@ -292,7 +302,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{t`bot.commands.research.body.total`}
{strings.bot.commands.research.body.total}
</span>
</div>
@@ -452,7 +462,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={TANK_ICONS[tank.class]}
width={16}
height={16}
@@ -463,7 +473,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
</div>
@@ -497,11 +507,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.research', [
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
])}
{literals(
strings.bot.commands.research.body.research,
[
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
],
)}
</span>
</div>
@@ -527,9 +540,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.upgrades', [
costs[index].upgrades.toLocaleString(),
])}
{literals(
strings.bot.commands.research.body.upgrades,
[costs[index].upgrades.toLocaleString()],
)}
</span>
</div>
)}
@@ -555,11 +569,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.purchase', [
costs[index].purchase.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.purchase,
[
costs[index].purchase.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
@@ -584,11 +601,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.equipment', [
costs[index].equipment.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.equipment,
[
costs[index].equipment.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
</div>
@@ -602,7 +622,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
return startingTankRaw
? image
: [t`bot.commands.research.body.estimation`, image];
: [strings.bot.commands.research.body.estimation, image];
},
autocomplete(interaction) {
+14 -11
View File
@@ -1,4 +1,5 @@
import { ClanList, fetchBlitz, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addClanChoices } from '../core/discord/addClanChoices';
@@ -19,25 +20,27 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
addClanChoices(option).setAutocomplete(false),
)
.addIntegerOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.search_clans.options.limit`)
.setName(strings.bot.commands.search_clans.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.search_clans.options.limit',
(strings) => strings.bot.commands.search_clans.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_clans.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_clans.options.limit.description',
(strings) =>
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -46,7 +49,7 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const clan = interaction.options.getString('clan')!;
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
@@ -56,12 +59,12 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
});
return embedInfo(
translate('bot.commands.search_clans.body.title', [
literals(strings.bot.commands.search_clans.body.title, [
markdownEscape(clan),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
clanList.length === 0
? translate('bot.commands.search_clans.body.no_results')
? strings.bot.commands.search_clans.body.no_results
: `\`\`\`\n${clanList
.map((clan) => `${clan.name} [${clan.tag}]`)
.join('\n')}\n\`\`\``,
+15 -11
View File
@@ -4,6 +4,7 @@ import {
Region,
usernamePattern,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addRegionChoices } from '../core/discord/addRegionChoices';
@@ -17,7 +18,7 @@ import { CommandRegistry } from '../events/interactionCreate';
const DEFAULT_LIMIT = 25;
export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('search-players')
@@ -27,22 +28,25 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
)
.addIntegerOption((option) =>
option
.setName(t`bot.commands.search_players.options.limit`)
.setName(strings.bot.commands.search_players.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.search_players.options.limit',
(strings) =>
strings.bot.commands.search_players.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_players.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_players.options.limit.description',
(strings) =>
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -51,7 +55,7 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const name = interaction.options.getString('username')!;
const limit = interaction.options.getInteger('limit') ?? 25;
@@ -64,13 +68,13 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
: [];
return embedInfo(
translate('bot.commands.search_players.body.title', [
literals(strings.bot.commands.search_players.body.title, [
markdownEscape(trimmedSearch),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
`\`\`\`${
players.length === 0
? translate('bot.commands.search_players.body.no_results')
? strings.bot.commands.search_players.body.no_results
: players.map((player) => player.nickname).join('\n')
}\`\`\``,
);
@@ -32,7 +32,7 @@ export function AllStatsOverview({
locale,
supplementaryStats,
}: AllStatsOverviewProps) {
const { t, translate } = translator(locale);
const { strings } = translator(locale);
const percentile =
typeof supplementaryStats.WN8 === 'number'
? getWN8Percentile(supplementaryStats.WN8)
@@ -63,18 +63,18 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.battles`}
label={strings.bot.common.hero_stats.battles}
value={stats.battles.toLocaleString()}
align={WN8SurroundingStatAlign.Right}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.winrate`}
label={strings.bot.common.hero_stats.winrate}
value={`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`}
align={WN8SurroundingStatAlign.Right}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage`}
label={strings.bot.common.hero_stats.damage}
value={Math.round(
stats.damage_dealt / stats.battles,
).toLocaleString()}
@@ -82,7 +82,7 @@ export function AllStatsOverview({
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage_ratio`}
label={strings.bot.common.hero_stats.damage_ratio}
value={(stats.damage_dealt / stats.damage_received).toFixed(2)}
align={WN8SurroundingStatAlign.Right}
/>
@@ -90,7 +90,7 @@ export function AllStatsOverview({
<HeroStat
value={heroStat}
subtitle={translate(`common.wn8_percentile.${percentile}`)}
subtitle={strings.common.wn8_percentile[percentile]}
color={color}
/>
@@ -103,25 +103,25 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.tier`}
label={strings.bot.common.hero_stats.tier}
value={
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.survival`}
label={strings.bot.common.hero_stats.survival}
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
2,
)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.accuracy`}
label={strings.bot.common.hero_stats.accuracy}
value={`${(100 * (stats.hits / stats.shots)).toFixed(2)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.kills`}
label={strings.bot.common.hero_stats.kills}
value={(stats.frags / stats.battles).toFixed(2)}
/>
</div>
@@ -21,7 +21,7 @@ export function GenericAllStats({
supplementaryStats,
locale,
}: GenericAllStatsProps) {
const { t } = translator(locale);
const { strings } = translator(locale);
const damageRatio = stats.damage_dealt / stats.damage_received;
const killsToDeathRatio =
stats.frags / (stats.battles - stats.survived_battles);
@@ -30,27 +30,28 @@ export function GenericAllStats({
<Root>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.games`}
title={strings.bot.commands.full_stats.body.cards.games.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.games.winrate`,
label: strings.bot.commands.full_stats.body.cards.games.winrate,
value: `${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.games.battles`,
label: strings.bot.commands.full_stats.body.cards.games.battles,
value: stats.battles,
},
{
label: t`bot.commands.full_stats.body.cards.games.wins_losses`,
label:
strings.bot.commands.full_stats.body.cards.games.wins_losses,
value: `${stats.wins} / ${stats.losses}`,
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.efficiency`}
title={strings.bot.commands.full_stats.body.cards.efficiency.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.efficiency.wn8`,
label: strings.bot.commands.full_stats.body.cards.efficiency.wn8,
value:
supplementaryStats && isNumber(supplementaryStats.WN8)
? supplementaryStats.WN8!.toFixed(0)
@@ -60,14 +61,16 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.survival`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.survival,
value: `${(
100 *
(stats.survived_battles / stats.battles)
).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.accuracy`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.accuracy,
value: `${((stats.hits / stats.shots) * 100).toFixed(2)}%`,
},
]}
@@ -75,14 +78,18 @@ export function GenericAllStats({
</Row>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.lethality`}
title={strings.bot.commands.full_stats.body.cards.lethality.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.lethality.average_damage`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_damage,
value: (stats.damage_dealt / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_per_tier`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_per_tier,
value: supplementaryStats?.tier
? (
stats.damage_dealt /
@@ -92,32 +99,44 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_ratio`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_ratio,
value: isFinite(damageRatio) ? damageRatio.toFixed(2) : undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.average_kills`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_kills,
value: (stats.frags / stats.battles).toFixed(0),
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.miscellaneous`}
title={strings.bot.commands.full_stats.body.cards.miscellaneous.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_tier`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_tier,
value: supplementaryStats?.tier?.toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_spots`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_spots,
value: (stats.spotted / stats.battles).toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_xp`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_xp,
value: (stats.xp / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.kills_to_death_ratio`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.kills_to_death_ratio,
value: isFinite(killsToDeathRatio)
? killsToDeathRatio.toFixed(2)
: undefined,
+2 -2
View File
@@ -12,7 +12,7 @@ interface NoDataProps {
}
export function NoData({ type, locale }: NoDataProps) {
const { translate } = translator(locale);
const { strings } = translator(locale);
return (
<div
@@ -23,7 +23,7 @@ export function NoData({ type, locale }: NoDataProps) {
}}
>
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
{translate(`bot.common.no_data.${type}`)}
{strings.bot.common.no_data[type]}
</span>
</div>
);
+6 -7
View File
@@ -1,3 +1,5 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
import markdownEscape from 'markdown-escape';
@@ -11,22 +13,19 @@ export async function resolveTankId(
locale: Locale,
techTreeOnly = false,
) {
const { translate } = translator(locale);
const { strings } = translator(locale);
const number = typeof tank === 'string' ? Number(tank) : tank;
if (Number.isNaN(number)) {
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 1,
},
{ keys: SEARCH_KEYS, limit: 1 },
);
if (searchResult.length === 0) {
throw new UserError(
translate('bot.common.errors.tank_name_not_found', [
literals(strings.bot.common.errors.tank_name_not_found, [
markdownEscape(`${tank}`),
]),
);
@@ -40,7 +39,7 @@ export async function resolveTankId(
return number;
} else {
throw new UserError(
translate('bot.common.errors.tank_id_not_found', [`${number}`]),
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
);
}
}
@@ -8,15 +8,15 @@ export async function searchClansAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchClansAcrossRegions(trimmed, limit);
@@ -8,15 +8,15 @@ export async function searchPlayersAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchPlayersAcrossRegions(trimmed, limit);
@@ -1,4 +1,5 @@
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { StatFilters } from '../blitzstars/filterStats';
import { translator } from '../localization/translator';
@@ -7,22 +8,21 @@ export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
) {
const { translate } = translator(locale);
const { strings } = 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 (nation)
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
info.push(
translate('bot.common.filters.tier', [TIER_ROMAN_NUMERALS[tier]]),
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
);
}
if (tankClass) info.push(translate(`common.tank_class_short.${tankClass}`));
if (type) info.push(translate(`common.tree_type.${type}`));
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
if (type) info.push(strings.common.tree_type[type]);
return info.length === 0
? translate('bot.common.filters.none')
: info.join(', ');
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
}
@@ -38,7 +38,7 @@ export async function getTankHistories(
locale: Locale,
options?: Partial<GetTankHistoriesOptions>,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
@@ -82,7 +82,7 @@ export async function getTankHistories(
const tankStats = await getTankStats(server, id);
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
tankStats.forEach((tankHistory) => {
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addClanChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('clan')
.setNameLocalizations(
localizationObject('bot.common.options.clan', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.clan.name,
undefined,
true,
),
)
.setDescription(translate('bot.common.options.clan.description'))
.setDescription(strings.bot.common.options.clan.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.clan.description'),
localizationObject(
(strings) => strings.bot.common.options.clan.description,
),
)
.setAutocomplete(true)
.setMinLength(2)
@@ -17,34 +17,40 @@ import { localizationObject } from './localizationObject';
export function addFilterOptions<
Option extends SlashCommandBuilder | SlashCommandSubcommandBuilder,
>(option: Option, nations: string[]) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return (
option
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.nation'))
.setName(strings.bot.common.options.filters.nation.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.nation',
(strings) => strings.bot.common.options.filters.nation.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.nation.description'),
)
.setDescription(strings.bot.common.options.filters.nation.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.nation.description'),
localizationObject(
(strings) =>
strings.bot.common.options.filters.nation.description,
),
)
.addChoices(
...nations.map(
(nation) =>
({
value: nation,
name: translate(`common.nations.${nation}`),
name: (strings.common.nations as Record<string, string>)[
nation
],
name_localizations: localizationObject(
`common.nations.${nation}`,
(strings) =>
(strings.common.nations as Record<string, string>)[
nation
],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -54,28 +60,28 @@ export function addFilterOptions<
.addStringOption((option) => addTierChoices(option).setRequired(false))
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.class'))
.setName(strings.bot.common.options.filters.class.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.class',
(strings) => strings.bot.common.options.filters.class.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.class.description'),
)
.setDescription(strings.bot.common.options.filters.class.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.class.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.class.description,
),
)
.addChoices(
...TANK_CLASSES.map(
(tankType) =>
({
value: `${tankType}`,
name: translate(`common.tank_class_short.${tankType}`),
name: strings.common.tank_class_short[tankType],
name_localizations: localizationObject(
`common.tank_class_short.${tankType}`,
(strings) => strings.common.tank_class_short[tankType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -84,28 +90,28 @@ export function addFilterOptions<
)
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.type'))
.setName(strings.bot.common.options.filters.type.name)
.setNameLocalizations(
localizationObject(
'bot.common.options.filters.type',
(strings) => strings.bot.common.options.filters.type.name,
undefined,
true,
),
)
.setDescription(
translate('bot.common.options.filters.type.description'),
)
.setDescription(strings.bot.common.options.filters.type.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.type.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.type.description,
),
)
.addChoices(
...TANK_TYPES.map(
(treeType) =>
({
value: TANK_TYPE_COMMAND_NAMES[treeType],
name: translate(`common.tree_type.${treeType}`),
name: strings.common.tree_type[treeType],
name_localizations: localizationObject(
`common.tree_type.${treeType}`,
(strings) => strings.common.tree_type[treeType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -16,39 +16,67 @@ export function addPeriodSubCommands(
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
const subcommandNameLocalizations = {
today: localizationObject('bot.common.subcommands.today', undefined, true),
30: localizationObject('bot.common.subcommands.30', undefined, true),
60: localizationObject('bot.common.subcommands.60', undefined, true),
90: localizationObject('bot.common.subcommands.90', undefined, true),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.name,
undefined,
true,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].name,
undefined,
true,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].name,
undefined,
true,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].name,
undefined,
true,
),
career: localizationObject(
'bot.common.subcommands.career',
(strings) => strings.bot.common.subcommands.career.name,
undefined,
true,
),
custom: localizationObject(
'bot.common.subcommands.custom',
(strings) => strings.bot.common.subcommands.custom.name,
undefined,
true,
),
};
const subcommandDescriptionLocalizations = {
today: localizationObject('bot.common.subcommands.today.description'),
30: localizationObject('bot.common.subcommands.30.description'),
60: localizationObject('bot.common.subcommands.60.description'),
90: localizationObject('bot.common.subcommands.90.description'),
career: localizationObject('bot.common.subcommands.career.description'),
custom: localizationObject('bot.common.subcommands.custom.description'),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.description,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].description,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].description,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].description,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.description,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.description,
),
};
option
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.today'))
.setName(strings.bot.common.subcommands.today.name)
.setNameLocalizations(subcommandNameLocalizations.today)
.setDescription(translate('bot.common.subcommands.today.description'))
.setDescription(strings.bot.common.subcommands.today.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.today,
),
@@ -57,38 +85,36 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.30'))
.setName(strings.bot.common.subcommands[30].name)
.setNameLocalizations(subcommandNameLocalizations[30])
.setDescription(translate('bot.common.subcommands.30.description'))
.setDescription(strings.bot.common.subcommands[30].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[30]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.60'))
.setName(strings.bot.common.subcommands[60].name)
.setNameLocalizations(subcommandNameLocalizations[60])
.setDescription(translate('bot.common.subcommands.60.description'))
.setDescription(strings.bot.common.subcommands[60].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[60]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.90'))
.setName(strings.bot.common.subcommands[90].name)
.setNameLocalizations(subcommandNameLocalizations[90])
.setDescription(translate('bot.common.subcommands.90.description'))
.setDescription(strings.bot.common.subcommands[90].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[90]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.career'))
.setName(strings.bot.common.subcommands.career.name)
.setNameLocalizations(subcommandNameLocalizations.career)
.setDescription(
translate('bot.common.subcommands.career.description'),
)
.setDescription(strings.bot.common.subcommands.career.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.career,
),
@@ -97,31 +123,25 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.custom'))
.setName(strings.bot.common.subcommands.custom.name)
.setNameLocalizations(subcommandNameLocalizations.custom)
.setDescription(
translate('bot.common.subcommands.custom.description'),
)
.setDescription(strings.bot.common.subcommands.custom.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.custom,
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.start'))
.setName(strings.bot.common.subcommands.custom.options.start.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.start.description',
),
strings.bot.common.subcommands.custom.options.start.description,
)
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.end'))
.setName(strings.bot.common.subcommands.custom.options.end.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.end.description',
),
strings.bot.common.subcommands.custom.options.end.description,
)
.setRequired(true),
),
@@ -8,25 +8,31 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addRegionChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.region`)
.setName(strings.bot.common.options.region.name)
.setNameLocalizations(
localizationObject('bot.common.options.region', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.region.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.region.description`)
.setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.region.description'),
localizationObject(
(strings) => strings.bot.common.options.region.description,
),
)
.addChoices(
...REGIONS.map(
(region) =>
({
value: region,
name: translate(`common.regions.normal.${region}`),
name: strings.common.regions.normal[region],
name_localizations: localizationObject(
`common.regions.normal.${region}`,
(strings) => strings.common.regions.normal[region],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTankChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(translate('bot.common.options.tank'))
.setName(strings.bot.common.options.tank.name)
.setNameLocalizations(
localizationObject('bot.common.options.tank', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tank.name,
undefined,
true,
),
)
.setDescription(translate('bot.common.options.tank.description'))
.setDescription(strings.bot.common.options.tank.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tank.description'),
localizationObject(
(strings) => strings.bot.common.options.tank.description,
),
)
.setAutocomplete(true)
.setRequired(true);
@@ -1,4 +1,5 @@
import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -9,28 +10,34 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTierChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.tier`)
.setName(strings.bot.common.options.tier.name)
.setNameLocalizations(
localizationObject('bot.common.options.tier', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tier.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.tier.description`)
.setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tier.description'),
localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
)
.setChoices(
...range(10, 0).map(
(tier) =>
({
name: translate('bot.common.options.tier.choices.tier', [
name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`,
numberToRomanNumeral(tier),
]),
value: `${tier}`,
name_localizations: localizationObject(
'bot.common.options.tier.choices.tier',
(strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)],
),
}) satisfies APIApplicationCommandOptionChoice,
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addUsernameChoices(option: SlashCommandStringOption) {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.username`)
.setName(strings.bot.common.options.username.name)
.setNameLocalizations(
localizationObject('bot.common.options.username', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.username.name,
undefined,
true,
),
)
.setDescription(t`bot.common.options.username.description`)
.setDescription(strings.bot.common.options.username.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.username.description'),
localizationObject(
(strings) => strings.bot.common.options.username.description,
),
)
.setAutocomplete(true);
}
@@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants';
export async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]);
@@ -19,9 +19,7 @@ export async function autocompleteClan(
await interaction.respond(
clans
? clans.map((clan) => {
let name = `${clan.name} (${translate(
`common.regions.short.${clan.region}`,
)})`;
let name = `${clan.name} (${strings.common.regions.short[clan.region]})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -29,9 +27,7 @@ export async function autocompleteClan(
name = `${clan.name.slice(
0,
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${clan.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${strings.common.regions.short[clan.region]})`;
}
return {
@@ -1,15 +1,16 @@
import { fetchTankNames, TankType } from '@blitzkit/core';
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { go } from 'fuzzysort';
import { tankNames } from '../blitzkit/nonBlockingPromises';
import { translator } from '../localization/translator';
import {
DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX,
} from './autocompleteClan/constants';
import { tankNames } from '../blitzkit/nonBlockingPromises';
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
@@ -20,6 +21,7 @@ export async function autocompleteTanks(
techTreeOnly = false,
fields = ['tank'],
) {
const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (!fields.includes(focusedOption.name)) return;
@@ -29,19 +31,16 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 10,
},
{ keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => {
let name = item.obj.searchableName;
let name = unwrap(item.obj.searchableName);
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
name = `${item.obj.searchableName.slice(
name = `${unwrap(item.obj.searchableName).slice(
0,
item.obj.searchableName.length -
unwrap(item.obj.searchableName).length -
overSize -
OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX}`;
@@ -13,7 +13,7 @@ import {
export async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value);
@@ -22,9 +22,9 @@ export async function autocompleteUsername(
await interaction.respond(
players
? players.map((player) => {
let name = `${player.nickname} (${translate(
`common.regions.short.${player.region}`,
)})`;
let name = `${player.nickname} (${
strings.common.regions.short[player.region]
})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -32,9 +32,9 @@ export async function autocompleteUsername(
name = `${player.nickname.slice(
0,
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${player.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${
strings.common.regions.short[player.region]
})`;
}
return {
@@ -6,13 +6,13 @@ export function buttonRefresh(
interaction: ChatInputCommandInteraction,
path: string,
) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (
interaction.appPermissions?.has('ReadMessageHistory') &&
interaction.appPermissions?.has('ViewChannel')
) {
return buttonPrimary(path, t`bot.common.buttons.refresh`);
return buttonPrimary(path, strings.bot.common.buttons.refresh);
}
return null;
@@ -1,3 +1,4 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import {
Locale,
SlashCommandBuilder,
@@ -23,28 +24,47 @@ export function createLocalizedCommand(
| CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[],
) {
const commandPathItem = command.replaceAll('-', '_');
const { translate } = translator(Locale.EnglishUS);
const commandPathItem = command.replaceAll(
'-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder()
.setName(command)
.setNameLocalizations(
localizationObject(`bot.commands.${commandPathItem}`, undefined, true),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].name,
undefined,
true,
),
)
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
.setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations(
localizationObject(`bot.commands.${commandPathItem}.description`),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
);
if (sub) {
sub.map((subItem) => {
if ('subcommand' in subItem) {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { name: string }>;
}
).subcommands[subItem.subcommand].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
);
slashCommand.addSubcommand((option) => {
@@ -54,20 +74,32 @@ export function createLocalizedCommand(
.setName(subItem.subcommand)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
} else {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { name: string }>;
}
).groups[subItem.group].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
);
slashCommand.addSubcommandGroup((option) => {
@@ -77,9 +109,11 @@ export function createLocalizedCommand(
.setName(subItem.group)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -1,10 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands';
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { translate } = translator(locale);
if (period === 'career') return translate('bot.common.periods.career');
if (period === 'today') return translate('bot.common.periods.today');
return translate('bot.common.periods.days', [period]);
const { strings } = translator(locale);
if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, [period]);
}
@@ -1,18 +1,19 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES } from '../localization/strings/constants';
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;
export function localizationObject(
path: string,
literals?: string[],
path: (strings: BlitzKitStrings) => string,
literals: string[] = [],
strict = false,
) {
return SUPPORTED_LOCALES.reduce<Partial<Record<Locale, string>>>(
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => {
const { translate } = translator(locale);
const translation = translate(path, literals);
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid = !strict || validNameRegex.test(translation);
if (!isValid) {
-6
View File
@@ -1,6 +0,0 @@
import { assertSecret } from '@blitzkit/core';
import { ShardingManager } from 'discord.js';
export const manager = new ShardingManager('dist/bot/workers/bot.js', {
token: assertSecret(import.meta.env.DISCORD_TOKEN),
});
@@ -1,4 +1,5 @@
import { Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
@@ -10,7 +11,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export async function resolveClanFromCommand(
interaction: ChatInputCommandInteraction,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const clan = interaction.options.getString('clan', true);
if (serverAndIdPattern.test(clan)) {
@@ -26,7 +27,9 @@ export async function resolveClanFromCommand(
return { region: accounts[0].region, id: accounts[0].clan_id };
} else {
throw new UserError(
translate('bot.common.errors.clan_not_found', [markdownEscape(clan)]),
literals(strings.bot.common.errors.clan_not_found, [
markdownEscape(clan),
]),
);
}
}
@@ -1,4 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { getPeriodNow } from '../blitzkit/getPeriodNow';
import { getPeriodStart } from '../blitzkit/getPeriodStart';
@@ -17,7 +18,7 @@ export function resolvePeriodFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
forcedPeriod?: PeriodType,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
let name: string;
let start: number;
let end: number;
@@ -31,7 +32,7 @@ export function resolvePeriodFromCommand(
const startDaysAgoMin = Math.min(startOption, endOption);
const endDaysAgoMax = Math.max(startOption, endOption);
name = translate('bot.common.periods.custom', [
name = literals(strings.bot.common.periods.custom, [
`${startDaysAgoMin}`,
`${endDaysAgoMax}`,
]);
@@ -1,4 +1,5 @@
import { idToRegion, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
@@ -14,7 +15,7 @@ export interface ResolvedPlayer {
export async function resolvePlayerFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const commandUsername = interaction.options.getString('username');
if (commandUsername !== null) {
@@ -36,7 +37,7 @@ export async function resolvePlayerFromCommand(
} satisfies ResolvedPlayer;
} else {
throw new UserError(
translate('bot.common.errors.player_not_found', [
literals(strings.bot.common.errors.player_not_found, [
markdownEscape(commandUsername),
]),
);
@@ -47,6 +48,6 @@ export async function resolvePlayerFromCommand(
if (account) return account;
throw new UserError(t`bot.common.errors.player_not_linked`);
throw new UserError(strings.bot.common.errors.player_not_linked);
}
}
@@ -1,10 +1,25 @@
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
export const SUPPORTED_LOCALES: Locale[] = [
Locale.EnglishUS,
Locale.SpanishES,
Locale.Croatian,
Locale.Russian,
Locale.Ukrainian,
Locale.ChineseCN,
];
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
en: Locale.EnglishUS,
es: Locale.SpanishES,
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(
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k]),
) as Record<Locale, SupportedLocale>;
export const SUPPORTED_LOCALES_DISCORD = Object.values(
SUPPORTED_LOCALES_DISCORD_MAP,
);
export const DEFAULT_LOCALE_DISCORD =
SUPPORTED_LOCALES_DISCORD_MAP[DEFAULT_LOCALE];
@@ -1,19 +0,0 @@
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES } from './constants';
export type TranslationFragmentTree = {
[key: string]: TranslationFragment;
} & {
$?: string;
};
export type TranslationFragment = string | TranslationFragmentTree;
export const translations = SUPPORTED_LOCALES.reduce<
Partial<Record<Locale, TranslationFragment>>
>(
(table, locale) => ({
...table,
[locale]: require(`../../../../../core/lang/${locale}.json`),
}),
{},
);
@@ -1,74 +1,19 @@
import { STRINGS, unwrapper } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translations } from './strings';
import { SUPPORTED_LOCALES } from './strings/constants';
import {
DEFAULT_LOCALE_DISCORD,
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './strings/constants';
export function translator(localeRaw: Locale) {
const locale =
localeRaw !== Locale.EnglishUS && SUPPORTED_LOCALES.includes(localeRaw)
localeRaw !== DEFAULT_LOCALE_DISCORD &&
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: Locale.EnglishUS;
const strings = translations[locale]!;
: DEFAULT_LOCALE_DISCORD;
const strings = STRINGS[SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]];
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
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 === Locale.EnglishUS) {
throw new Error(message);
} else {
console.warn(`${message} falling back to en-US`);
return translator(Locale.EnglishUS).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 };
}
@@ -8,13 +8,13 @@ import {
InteractionReplyOptions,
} from 'discord.js';
import { InteractionRawReturnable, commands } from '..';
import { UserError } from '../../../core/blitzkit/userError';
import { buttonLink } from '../../../core/discord/buttonLink';
import { embedWarning } from '../../../core/discord/embedWarning';
import { normalizeInteractionReturnable } from '../../../core/discord/normalizeInteractionReturnable';
import { psa } from '../../../core/discord/psa';
import { translator } from '../../../core/localization/translator';
import { Writeable } from '../../../types/writable';
import { UserError } from '../../../core/blitzkit/userError';
export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
@@ -126,16 +126,16 @@ export async function handleChatInputCommand(
index++;
}
} catch (error) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const components = [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel(t`bot.common.errors.get_help`)
.setLabel(strings.bot.common.errors.get_help)
.setURL('https://discord.gg/nDt7AjGJQH')
.setStyle(ButtonStyle.Link),
),
];
if (error instanceof UserError) {
interaction.editReply({
content: error.message,
@@ -143,9 +143,8 @@ export async function handleChatInputCommand(
});
} else {
console.error(interaction.commandName, error);
interaction.editReply({
content: t`bot.common.errors.uncaught_error`,
content: strings.bot.common.errors.uncaught_error,
components,
});
}
@@ -64,16 +64,19 @@ interface CommandRegistryBase {
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
}
interface CommandRegistryImplicit extends CommandRegistryBase {
handlesInteraction: true;
handler: (interaction: ChatInputCommandInteraction<CacheType>) => void;
}
interface CommandRegistryExplicit extends CommandRegistryBase {
handlesInteraction?: false;
handler: (
interaction: ChatInputCommandInteraction<CacheType>,
) => InteractionReturnable;
}
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
@@ -107,10 +110,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 };
},
{},
+30 -62
View File
@@ -1,77 +1,50 @@
import { ActivityType } from 'discord.js';
import { assertSecret } from '@blitzkit/core';
import { ActivityType, ShardingManager } from 'discord.js';
import {
discoveredIdsDefinitions,
tankDefinitions,
} from './core/blitzkit/nonBlockingPromises';
import { manager } from './core/discord/manager';
async function setupShardManager() {
console.log('Pre-manager setup starting...');
let iteration = 0;
const interval = setInterval(async () => {
iteration++;
const readyShards = new Set();
const totalShards = manager.totalShards;
console.log(`Attempting launch on iteration ${iteration}`);
const allShardsReady = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Shard initialization timed out after 60 seconds'));
}, 60000);
manager.on('shardCreate', (shard) => {
console.log(`🟡 Launching shard ${shard.id}`);
shard.on('ready', () => {
console.log(`🟢 Launched shard ${shard.id}`);
readyShards.add(shard.id);
if (readyShards.size === totalShards) {
clearTimeout(timeout);
resolve(true);
}
});
shard.on('error', (error) => {
console.error(`❌ Shard ${shard.id} encountered an error:`, error);
});
shard.on('disconnect', () => {
console.warn(`⚠️ Shard ${shard.id} disconnected`);
});
});
const manager = new ShardingManager('dist/bot/workers/bot.js', {
token: assertSecret(import.meta.env.DISCORD_TOKEN),
});
const shards = await manager
.on('shardCreate', (shard) => {
clearInterval(interval);
console.log(`🟡 Launching shard ${shard.id}`);
shard.on('ready', () => console.log(`🟢 Launched shard ${shard.id}`));
})
.spawn();
try {
console.log('Spawning shards...');
const shards = await manager.spawn();
console.log('Shards spawned, waiting for ready state...');
await allShardsReady;
console.log('All shards ready, setting up presence...');
let servers = 0;
let channels = 0;
let users = 0;
for (const [, shard] of shards) {
try {
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
servers += guilds.length;
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
for (const guild of guilds) {
channels += guild.channels.length;
users += guild.memberCount;
}
} catch (error) {
console.error(`Failed to fetch statistics from shard:`, error);
servers += guilds.length;
for (const guild of guilds) {
channels += guild.channels.length;
users += guild.memberCount;
}
}
const [awaitedTankDefinitions, awaitedDiscoveredIdsDefinitions] =
await Promise.all([tankDefinitions, discoveredIdsDefinitions]);
const awaitedTankDefinitions = await tankDefinitions;
const tanks = Object.values(awaitedTankDefinitions.tanks).length;
const awaitedDiscoveredIdsDefinitions = await discoveredIdsDefinitions;
const usernames = awaitedDiscoveredIdsDefinitions.count;
await manager.broadcastEval(
manager.broadcastEval(
(client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
@@ -92,15 +65,10 @@ async function setupShardManager() {
},
{ context: { servers, channels, users, tanks, usernames } },
);
console.log('Bot startup complete!');
} catch (error) {
console.error('Critical error during startup:', error);
throw error;
console.warn(
'An error occurred when setting up presence, ignoring to keep the process alive',
error,
);
}
}
setupShardManager().catch((error) => {
console.error('Failed to start the bot:', error);
process.exit(1);
});
}, 5000);
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"]
"include": ["src/**/*", "../core/src/blitzkit/supportedLocales.ts", "../i18n/src/unwrapper.ts"]
}
-683
View File
@@ -1,683 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Muy malo",
"1": "Malo",
"2": "Inferior al promedio",
"3": "Promedio",
"4": "Superior al promedio",
"5": "Bueno",
"6": "Muy bueno",
"7": "Genial",
"8": "Unicum",
"9": "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": "URSS",
"germany": "Alemania",
"usa": "EE. UU.",
"china": "China",
"uk": "Reino Unido",
"france": "Francia",
"japan": "Japón",
"european": "Nación europea",
"other": "Nación híbrida"
},
"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": "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": "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": "# 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": "# No sé quién eres\nUsa el comando `/vincular` para comenzar.",
"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": "# 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": "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": "Super 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": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"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": {
"$": "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": {
"$": "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": {
"$": "credits",
"description": "People who have helped 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": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "vecinos",
"description": "Neighboring players on the leaderboard"
},
"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 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": "%s eliminado",
"neighbors": "Rating neighbors",
"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": "Ping 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": {
"$": "rating-session",
"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": "# Estadísticas no disponibles actualmente\n\nWargaming podría estar sufriendo una caída del servidor. Si hay alguna temporada en curso, las estadísticas deberían estar disponibles en 24 horas."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-603
View File
@@ -1,603 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "非常差",
"1": "差",
"2": "较差",
"3": "平均",
"4": "高于平均",
"5": "好",
"6": "非常好",
"7": "很好",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "差",
"4": "较差",
"5": "平均",
"6": "高于平均",
"7": "好",
"8": "非常好",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "苏联 (S系)",
"germany": "德国 (D系)",
"usa": "美国 (M系)",
"china": "中国 (C系)",
"uk": "英国 (Y系)",
"france": "法国 (F系)",
"japan": "日本 (R系)",
"european": "欧盟 (E系)",
"other": "X系"
},
"nations_adjectives": {
"ussr": "苏联 (S系)",
"germany": "德国 (D系)",
"usa": "美国 (M系)",
"china": "中国 (C系)",
"uk": "英国 (Y系)",
"france": "法国 (F系)",
"japan": "日本 (R系)",
"european": "欧盟 (E系)",
"other": "X系"
},
"tank_class_short": {
"-1": "无法识别",
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tank_class_short_plural_lowercase": {
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tank_class_medium": {
"-1": "无法识别",
"0": "轻型坦克",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "战损比",
"cumulative_losses": "失败",
"cumulative_misses": "总射击失误数",
"cumulative_shots": "总射击数",
"cumulative_spotted": "场均发现敌人",
"cumulative_survived_battles": "幸存场次",
"cumulative_win_and_survived": "胜利和幸存场次",
"cumulative_wins": "胜利",
"cumulative_xp": "总经验",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "总战斗时间"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# 找不到军团\n找不到\"%s\"。请尝试从搜索结果中选择一个军团。",
"player_not_found": "# 找不到用户\n无法找到用户 \"%s\"。 尝试从搜索结果中挑选用户,输入一个有效的用户名或使用 \"/link\" 命令。",
"player_not_linked": "# 使用 `/link` 来开始\n我不知道你是谁,但你今天可以告诉我!",
"tank_name_not_found": "# 坦克找不到\n无法找到名为 \"%s \"的坦克。",
"tank_id_not_found": "# 坦克找不到\n无法找到ID \"%s\"的坦克。 试着输入坦克的名称而不是数字。",
"no_tank_stats": "# 没有这个坦克的数据\n这个玩家没有数据。你可能找错人了。",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit 崩溃了!\n非常抱歉。请加入[官方Discord](https://discord.gg/nDt7AjGJQH),以方便帮助你的问题。",
"preview": "# 此命令处于预览\n\n您只能在[官方Discord服务器](https://discord.gg/nDt7AjGJQH)访问此命令。该命令将很快可用。",
"query_too_small": "# 您的搜索太短了\n您的搜索必须至少 3 个字符长。",
"query_too_long": "# 您的搜索太长了\n您的搜索必须最长为 100 个字符。"
},
"wn8": {
"$": "WN8",
"very_bad": "非常差",
"bad": "差",
"below_average": "较差",
"average": "平均",
"above_average": "高于平均",
"good": "好",
"very_good": "非常好",
"great": "很好",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "今日:",
"description": "今日:"
},
"30": {
"$": "30",
"description": "30天"
},
"60": {
"$": "60",
"description": "60天"
},
"90": {
"$": "90",
"description": "90天"
},
"career": {
"$": "玩家资料",
"description": "玩家资料"
},
"custom": {
"$": "自定义",
"description": "自定义范围",
"options": {
"start": {
"$": "开始时间",
"description": "从今天开始于数天前"
},
"end": {
"$": "结束时间",
"description": "从今天开始于数天前"
}
}
}
},
"options": {
"clan": {
"$": "军团",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
@@ -1,11 +1,5 @@
import { times } from 'lodash-es';
import {
CrewType,
GunDefinition,
ShellType,
TankClass,
TankType,
} from '../../protos';
import { CrewType, ShellType, TankClass, TankType } from '../../protos';
export const SHELL_NAMES: Record<ShellType, string> = {
[ShellType.AP]: 'AP',
@@ -13,21 +7,14 @@ export const SHELL_NAMES: Record<ShellType, string> = {
[ShellType.HEAT]: 'HEAT',
[ShellType.HE]: 'HE',
};
export const CREW_MEMBER_NAMES: Record<CrewType, string> = {
[CrewType.COMMANDER]: 'Commander',
[CrewType.DRIVER]: 'Driver',
[CrewType.GUNNER]: 'Gunner',
[CrewType.LOADER]: 'Loader',
[CrewType.RADIOMAN]: 'Radioman',
};
export const GUN_TYPE_NAMES: Record<
Exclude<GunDefinition['gun_type'], undefined>['$case'],
string
> = {
auto_loader: 'Auto loader',
auto_reloader: 'Auto reloader',
regular: 'Regular',
};
export const CREW_MEMBER_NAMES = {
[CrewType.COMMANDER]: 'commander',
[CrewType.DRIVER]: 'driver',
[CrewType.GUNNER]: 'gunner',
[CrewType.LOADER]: 'loader',
[CrewType.RADIOMAN]: 'radioman',
} as const;
/**
* @deprecated use svg
*/
+17 -1
View File
@@ -1,4 +1,6 @@
import { deburr } from 'lodash-es';
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
import { I18nString } from '../protos';
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
import { fetchTankDefinitions } from './tankDefinitions';
@@ -11,12 +13,18 @@ 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)
.filter(Boolean)
@@ -26,3 +34,11 @@ export async function fetchTankNames() {
}),
);
}
export const SEARCH_KEYS = [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.locales.${locale}`,
`searchableNameDeburr.locales.${locale}`,
]).flat(),
'camouflages',
];
+2 -3
View File
@@ -1,5 +1,4 @@
const numberFormat = Intl.NumberFormat('en', { notation: 'compact' });
export function formatCompact(number: number) {
export function formatCompact(locale: string, number: number) {
const numberFormat = Intl.NumberFormat(locale, { notation: 'compact' });
return numberFormat.format(number);
}
@@ -251,7 +251,7 @@ export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_Ave
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? AverageDefinitionsEntry.fromPartial(object.value)
: createBaseAverageDefinitionsEntry();
: undefined;
return message;
},
};
@@ -2,13 +2,15 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message CamouflageDefinitions {
map<uint32, Camouflage> camouflages = 1;
}
message Camouflage {
required uint32 id = 1;
required string name = 2;
optional string tank_name = 3;
optional string tank_name_full = 4;
required I18nString name = 2;
optional I18nString tank_name = 3;
optional I18nString tank_name_full = 4;
}
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -20,9 +21,9 @@ export interface CamouflageDefinitions_CamouflagesEntry {
export interface Camouflage {
id: number;
name: string;
tank_name?: string | undefined;
tank_name_full?: string | undefined;
name: I18nString;
tank_name?: I18nString | undefined;
tank_name_full?: I18nString | undefined;
}
function createBaseCamouflageDefinitions(): CamouflageDefinitions {
@@ -184,13 +185,13 @@ export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefini
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Camouflage.fromPartial(object.value)
: createBaseCamouflage();
: undefined;
return message;
},
};
function createBaseCamouflage(): Camouflage {
return { id: 0, name: "", tank_name: undefined, tank_name_full: undefined };
return { id: 0, name: createBaseI18nString(), tank_name: undefined, tank_name_full: undefined };
}
export const Camouflage: MessageFns<Camouflage> = {
@@ -198,14 +199,14 @@ export const Camouflage: MessageFns<Camouflage> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.tank_name !== undefined && message.tank_name !== undefined) {
writer.uint32(26).string(message.tank_name);
if (message.tank_name !== undefined) {
I18nString.encode(message.tank_name, writer.uint32(26).fork()).join();
}
if (message.tank_name_full !== undefined && message.tank_name_full !== undefined) {
writer.uint32(34).string(message.tank_name_full);
if (message.tank_name_full !== undefined) {
I18nString.encode(message.tank_name_full, writer.uint32(34).fork()).join();
}
return writer;
},
@@ -230,7 +231,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -238,7 +239,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.tank_name = reader.string();
message.tank_name = I18nString.decode(reader, reader.uint32());
continue;
}
case 4: {
@@ -246,7 +247,7 @@ export const Camouflage: MessageFns<Camouflage> = {
break;
}
message.tank_name_full = reader.string();
message.tank_name_full = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -261,9 +262,9 @@ export const Camouflage: MessageFns<Camouflage> = {
fromJSON(object: any): Camouflage {
return {
id: globalThis.Number(assertSet("Camouflage.id", object.id)),
name: globalThis.String(assertSet("Camouflage.name", object.name)),
tank_name: isSet(object.tank_name) ? globalThis.String(object.tank_name) : undefined,
tank_name_full: isSet(object.tank_name_full) ? globalThis.String(object.tank_name_full) : undefined,
name: I18nString.fromJSON(assertSet("Camouflage.name", object.name)),
tank_name: isSet(object.tank_name) ? I18nString.fromJSON(object.tank_name) : undefined,
tank_name_full: isSet(object.tank_name_full) ? I18nString.fromJSON(object.tank_name_full) : undefined,
};
},
@@ -272,14 +273,14 @@ export const Camouflage: MessageFns<Camouflage> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tank_name !== undefined && message.tank_name !== undefined) {
obj.tank_name = message.tank_name;
if (message.tank_name !== undefined) {
obj.tank_name = I18nString.toJSON(message.tank_name);
}
if (message.tank_name_full !== undefined && message.tank_name_full !== undefined) {
obj.tank_name_full = message.tank_name_full;
if (message.tank_name_full !== undefined) {
obj.tank_name_full = I18nString.toJSON(message.tank_name_full);
}
return obj;
},
@@ -290,9 +291,15 @@ export const Camouflage: MessageFns<Camouflage> = {
fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>(object: I): Camouflage {
const message = createBaseCamouflage();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.tank_name = object.tank_name ?? undefined;
message.tank_name_full = object.tank_name_full ?? undefined;
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tank_name = (object.tank_name !== undefined && object.tank_name !== null)
? I18nString.fromPartial(object.tank_name)
: undefined;
message.tank_name_full = (object.tank_name_full !== undefined && object.tank_name_full !== null)
? I18nString.fromPartial(object.tank_name_full)
: undefined;
return message;
},
};
@@ -2,13 +2,15 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message ConsumableDefinitions {
map<uint32, Consumable> consumables = 1;
}
message Consumable {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
required uint32 cooldown = 3;
optional float duration = 4;
required bool game_mode_exclusive = 5;
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -47,7 +48,7 @@ export interface ConsumableDefinitions_ConsumablesEntry {
export interface Consumable {
id: number;
name: string;
name: I18nString;
cooldown: number;
duration?: number | undefined;
game_mode_exclusive: boolean;
@@ -240,13 +241,21 @@ export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefini
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Consumable.fromPartial(object.value)
: createBaseConsumable();
: undefined;
return message;
},
};
function createBaseConsumable(): Consumable {
return { id: 0, name: "", cooldown: 0, duration: undefined, game_mode_exclusive: false, include: [], exclude: [] };
return {
id: 0,
name: createBaseI18nString(),
cooldown: 0,
duration: undefined,
game_mode_exclusive: false,
include: [],
exclude: [],
};
}
export const Consumable: MessageFns<Consumable> = {
@@ -254,8 +263,8 @@ export const Consumable: MessageFns<Consumable> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.cooldown !== 0) {
writer.uint32(24).uint32(message.cooldown);
@@ -295,7 +304,7 @@ export const Consumable: MessageFns<Consumable> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -350,7 +359,7 @@ export const Consumable: MessageFns<Consumable> = {
fromJSON(object: any): Consumable {
return {
id: globalThis.Number(assertSet("Consumable.id", object.id)),
name: globalThis.String(assertSet("Consumable.name", object.name)),
name: I18nString.fromJSON(assertSet("Consumable.name", object.name)),
cooldown: globalThis.Number(assertSet("Consumable.cooldown", object.cooldown)),
duration: isSet(object.duration) ? globalThis.Number(object.duration) : undefined,
game_mode_exclusive: globalThis.Boolean(assertSet("Consumable.game_mode_exclusive", object.game_mode_exclusive)),
@@ -368,8 +377,8 @@ export const Consumable: MessageFns<Consumable> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.cooldown !== 0) {
obj.cooldown = Math.round(message.cooldown);
@@ -395,7 +404,9 @@ export const Consumable: MessageFns<Consumable> = {
fromPartial<I extends Exact<DeepPartial<Consumable>, I>>(object: I): Consumable {
const message = createBaseConsumable();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.cooldown = object.cooldown ?? 0;
message.duration = object.duration ?? undefined;
message.game_mode_exclusive = object.game_mode_exclusive ?? false;
@@ -2,14 +2,16 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message EquipmentDefinitions {
map<string, EquipmentPreset> presets = 1;
map<uint32, Equipment> equipments = 2;
}
message Equipment {
required string name = 1;
required string description = 2;
required I18nString name = 1;
required I18nString description = 2;
}
message EquipmentPreset {
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -25,8 +26,8 @@ export interface EquipmentDefinitions_EquipmentsEntry {
}
export interface Equipment {
name: string;
description: string;
name: I18nString;
description: I18nString;
}
export interface EquipmentPreset {
@@ -235,7 +236,7 @@ export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_
message.key = object.key ?? "";
message.value = (object.value !== undefined && object.value !== null)
? EquipmentPreset.fromPartial(object.value)
: createBaseEquipmentPreset();
: undefined;
return message;
},
};
@@ -317,22 +318,22 @@ export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitio
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Equipment.fromPartial(object.value)
: createBaseEquipment();
: undefined;
return message;
},
};
function createBaseEquipment(): Equipment {
return { name: "", description: "" };
return { name: createBaseI18nString(), description: createBaseI18nString() };
}
export const Equipment: MessageFns<Equipment> = {
encode(message: Equipment, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.name !== "") {
writer.uint32(10).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(10).fork()).join();
}
if (message.description !== "") {
writer.uint32(18).string(message.description);
if (message.description !== undefined) {
I18nString.encode(message.description, writer.uint32(18).fork()).join();
}
return writer;
},
@@ -349,7 +350,7 @@ export const Equipment: MessageFns<Equipment> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 2: {
@@ -357,7 +358,7 @@ export const Equipment: MessageFns<Equipment> = {
break;
}
message.description = reader.string();
message.description = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -371,18 +372,18 @@ export const Equipment: MessageFns<Equipment> = {
fromJSON(object: any): Equipment {
return {
name: globalThis.String(assertSet("Equipment.name", object.name)),
description: globalThis.String(assertSet("Equipment.description", object.description)),
name: I18nString.fromJSON(assertSet("Equipment.name", object.name)),
description: I18nString.fromJSON(assertSet("Equipment.description", object.description)),
};
},
toJSON(message: Equipment): unknown {
const obj: any = {};
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.description !== "") {
obj.description = message.description;
if (message.description !== undefined) {
obj.description = I18nString.toJSON(message.description);
}
return obj;
},
@@ -392,8 +393,12 @@ export const Equipment: MessageFns<Equipment> = {
},
fromPartial<I extends Exact<DeepPartial<Equipment>, I>>(object: I): Equipment {
const message = createBaseEquipment();
message.name = object.name ?? "";
message.description = object.description ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.description = (object.description !== undefined && object.description !== null)
? I18nString.fromPartial(object.description)
: createBaseI18nString();
return message;
},
};
@@ -2,6 +2,8 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message GameDefinitions {
required string version = 1;
repeated string nations = 2;
@@ -10,7 +12,7 @@ message GameDefinitions {
}
message GameMode {
required string name = 1;
required I18nString name = 1;
}
message GameModeRole {
+14 -11
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -27,7 +28,7 @@ export interface GameDefinitions_RolesEntry {
}
export interface GameMode {
name: string;
name: I18nString;
}
export interface GameModeRole {
@@ -259,7 +260,7 @@ export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameMode
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GameMode.fromPartial(object.value)
: createBaseGameMode();
: undefined;
return message;
},
};
@@ -337,19 +338,19 @@ export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry>
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GameModeRole.fromPartial(object.value)
: createBaseGameModeRole();
: undefined;
return message;
},
};
function createBaseGameMode(): GameMode {
return { name: "" };
return { name: createBaseI18nString() };
}
export const GameMode: MessageFns<GameMode> = {
encode(message: GameMode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.name !== "") {
writer.uint32(10).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(10).fork()).join();
}
return writer;
},
@@ -366,7 +367,7 @@ export const GameMode: MessageFns<GameMode> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -379,13 +380,13 @@ export const GameMode: MessageFns<GameMode> = {
},
fromJSON(object: any): GameMode {
return { name: globalThis.String(assertSet("GameMode.name", object.name)) };
return { name: I18nString.fromJSON(assertSet("GameMode.name", object.name)) };
},
toJSON(message: GameMode): unknown {
const obj: any = {};
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
return obj;
},
@@ -395,7 +396,9 @@ export const GameMode: MessageFns<GameMode> = {
},
fromPartial<I extends Exact<DeepPartial<GameMode>, I>>(object: I): GameMode {
const message = createBaseGameMode();
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
return message;
},
};
+7
View File
@@ -0,0 +1,7 @@
syntax = "proto2";
package blitzkit;
message I18nString {
map<string, string> locales = 1;
}
+212
View File
@@ -0,0 +1,212 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.2.2
// protoc v5.28.2
// source: packages/core/src/protos/i18n.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "blitzkit";
export interface I18nString {
locales: { [key: string]: string };
}
export interface I18nString_LocalesEntry {
key: string;
value: string;
}
export function createBaseI18nString(): I18nString {
return { locales: {} };
}
export const I18nString: MessageFns<I18nString> = {
encode(message: I18nString, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.locales).forEach(([key, value]) => {
I18nString_LocalesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
});
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): I18nString {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
const entry1 = I18nString_LocalesEntry.decode(reader, reader.uint32());
if (entry1.value !== undefined) {
message.locales[entry1.key] = entry1.value;
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): I18nString {
return {
locales: isObject(object.locales)
? Object.entries(object.locales).reduce<{ [key: string]: string }>((acc, [key, value]) => {
acc[key] = String(value);
return acc;
}, {})
: {},
};
},
toJSON(message: I18nString): unknown {
const obj: any = {};
if (message.locales) {
const entries = Object.entries(message.locales);
if (entries.length > 0) {
obj.locales = {};
entries.forEach(([k, v]) => {
obj.locales[k] = v;
});
}
}
return obj;
},
create<I extends Exact<DeepPartial<I18nString>, I>>(base?: I): I18nString {
return I18nString.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<I18nString>, I>>(object: I): I18nString {
const message = createBaseI18nString();
message.locales = Object.entries(object.locales ?? {}).reduce<{ [key: string]: string }>((acc, [key, value]) => {
if (value !== undefined) {
acc[key] = globalThis.String(value);
}
return acc;
}, {});
return message;
},
};
function createBaseI18nString_LocalesEntry(): I18nString_LocalesEntry {
return { key: "", value: "" };
}
export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = {
encode(message: I18nString_LocalesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) {
writer.uint32(10).string(message.key);
}
if (message.value !== undefined) {
writer.uint32(18).string(message.value);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): I18nString_LocalesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString_LocalesEntry();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.key = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.value = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): I18nString_LocalesEntry {
return {
key: globalThis.String(assertSet("I18nString_LocalesEntry.key", object.key)),
value: globalThis.String(assertSet("I18nString_LocalesEntry.value", object.value)),
};
},
toJSON(message: I18nString_LocalesEntry): unknown {
const obj: any = {};
if (message.key !== undefined) {
obj.key = message.key;
}
if (message.value !== undefined) {
obj.value = message.value;
}
return obj;
},
create<I extends Exact<DeepPartial<I18nString_LocalesEntry>, I>>(base?: I): I18nString_LocalesEntry {
return I18nString_LocalesEntry.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<I18nString_LocalesEntry>, I>>(object: I): I18nString_LocalesEntry {
const message = createBaseI18nString_LocalesEntry();
message.key = object.key ?? "";
message.value = object.value ?? "";
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial<T["value"]> }
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin ? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
function isObject(value: any): boolean {
return typeof value === "object" && value !== null;
}
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}
+1
View File
@@ -4,6 +4,7 @@ export * from './camouflage_definitions.ts';
export * from './consumable_definitions.ts';
export * from './equipment_definitions.ts';
export * from './game_definitions.ts';
export * from './i18n.ts';
export * from './map_definitions.ts';
export * from './model_definitions.ts';
export * from './provision_definitions.ts';
@@ -2,11 +2,13 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message MapDefinitions {
map<int32, MapDefinition> maps = 1;
}
message MapDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
}
+13 -10
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -20,7 +21,7 @@ export interface MapDefinitions_MapsEntry {
export interface MapDefinition {
id: number;
name: string;
name: I18nString;
}
function createBaseMapDefinitions(): MapDefinitions {
@@ -175,13 +176,13 @@ export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? MapDefinition.fromPartial(object.value)
: createBaseMapDefinition();
: undefined;
return message;
},
};
function createBaseMapDefinition(): MapDefinition {
return { id: 0, name: "" };
return { id: 0, name: createBaseI18nString() };
}
export const MapDefinition: MessageFns<MapDefinition> = {
@@ -189,8 +190,8 @@ export const MapDefinition: MessageFns<MapDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
return writer;
},
@@ -215,7 +216,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
}
@@ -230,7 +231,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
fromJSON(object: any): MapDefinition {
return {
id: globalThis.Number(assertSet("MapDefinition.id", object.id)),
name: globalThis.String(assertSet("MapDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("MapDefinition.name", object.name)),
};
},
@@ -239,8 +240,8 @@ export const MapDefinition: MessageFns<MapDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
return obj;
},
@@ -251,7 +252,9 @@ export const MapDefinition: MessageFns<MapDefinition> = {
fromPartial<I extends Exact<DeepPartial<MapDefinition>, I>>(object: I): MapDefinition {
const message = createBaseMapDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
return message;
},
};
@@ -265,7 +265,7 @@ export const ModelDefinitions_ModelsEntry: MessageFns<ModelDefinitions_ModelsEnt
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? ModelDefinition.fromPartial(object.value)
: createBaseModelDefinition();
: undefined;
return message;
},
};
@@ -545,7 +545,7 @@ export const ModelDefinition_TurretsEntry: MessageFns<ModelDefinition_TurretsEnt
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TurretModelDefinition.fromPartial(object.value)
: createBaseTurretModelDefinition();
: undefined;
return message;
},
};
@@ -623,7 +623,7 @@ export const ModelDefinition_TracksEntry: MessageFns<ModelDefinition_TracksEntry
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TrackModelDefinition.fromPartial(object.value)
: createBaseTrackModelDefinition();
: undefined;
return message;
},
};
@@ -1248,7 +1248,7 @@ export const TurretModelDefinition_GunsEntry: MessageFns<TurretModelDefinition_G
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? GunModelDefinition.fromPartial(object.value)
: createBaseGunModelDefinition();
: undefined;
return message;
},
};
@@ -3,6 +3,7 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/consumable_definitions.proto";
import "packages/core/src/protos/i18n.proto";
message ProvisionDefinitions {
map<uint32, Provision> provisions = 1;
@@ -10,7 +11,7 @@ message ProvisionDefinitions {
message Provision {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
optional float crew = 3;
required bool game_mode_exclusive = 5;
repeated ConsumableTankFilter include = 6;
@@ -7,6 +7,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { ConsumableTankFilter } from "./consumable_definitions";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -21,7 +22,7 @@ export interface ProvisionDefinitions_ProvisionsEntry {
export interface Provision {
id: number;
name: string;
name: I18nString;
crew?: number | undefined;
game_mode_exclusive: boolean;
include: ConsumableTankFilter[];
@@ -187,13 +188,13 @@ export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitio
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Provision.fromPartial(object.value)
: createBaseProvision();
: undefined;
return message;
},
};
function createBaseProvision(): Provision {
return { id: 0, name: "", crew: undefined, game_mode_exclusive: false, include: [], exclude: [] };
return { id: 0, name: createBaseI18nString(), crew: undefined, game_mode_exclusive: false, include: [], exclude: [] };
}
export const Provision: MessageFns<Provision> = {
@@ -201,8 +202,8 @@ export const Provision: MessageFns<Provision> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.crew !== undefined && message.crew !== undefined) {
writer.uint32(29).float(message.crew);
@@ -239,7 +240,7 @@ export const Provision: MessageFns<Provision> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -286,7 +287,7 @@ export const Provision: MessageFns<Provision> = {
fromJSON(object: any): Provision {
return {
id: globalThis.Number(assertSet("Provision.id", object.id)),
name: globalThis.String(assertSet("Provision.name", object.name)),
name: I18nString.fromJSON(assertSet("Provision.name", object.name)),
crew: isSet(object.crew) ? globalThis.Number(object.crew) : undefined,
game_mode_exclusive: globalThis.Boolean(assertSet("Provision.game_mode_exclusive", object.game_mode_exclusive)),
include: globalThis.Array.isArray(object?.include)
@@ -303,8 +304,8 @@ export const Provision: MessageFns<Provision> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.crew !== undefined && message.crew !== undefined) {
obj.crew = message.crew;
@@ -327,7 +328,9 @@ export const Provision: MessageFns<Provision> = {
fromPartial<I extends Exact<DeepPartial<Provision>, I>>(object: I): Provision {
const message = createBaseProvision();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.crew = object.crew ?? undefined;
message.game_mode_exclusive = object.game_mode_exclusive ?? false;
message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
+1 -1
View File
@@ -180,7 +180,7 @@ export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Review.fromPartial(object.value)
: createBaseReview();
: undefined;
return message;
},
};
@@ -175,9 +175,7 @@ export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesE
): SkillDefinitions_ClassesEntry {
const message = createBaseSkillDefinitions_ClassesEntry();
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Skill.fromPartial(object.value)
: createBaseSkill();
message.value = (object.value !== undefined && object.value !== null) ? Skill.fromPartial(object.value) : undefined;
return message;
},
};
@@ -2,6 +2,8 @@ syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message TankDefinitions {
map<uint32, TankDefinition> tanks = 1;
}
@@ -9,7 +11,6 @@ message TankDefinitions {
message TankDefinition {
required uint32 id = 1;
map<uint32, uint32> roles = 2;
optional string description = 3;
required bool fixed_camouflage = 4;
repeated int32 camouflages = 5;
repeated int32 ancestors = 6;
@@ -17,8 +18,8 @@ message TankDefinition {
repeated Crew crew = 8;
required uint32 health = 9;
required string nation = 10;
required string name = 11;
optional string name_full = 12;
required I18nString name = 11;
optional I18nString name_full = 12;
required TankType type = 13;
required uint32 max_consumables = 14;
required uint32 max_provisions = 15;
@@ -65,7 +66,7 @@ message TankPrice {
message TrackDefinition {
required uint32 id = 1;
required uint32 tier = 2;
required string name = 3;
required I18nString name = 3;
required uint32 weight = 4;
required float traverse_speed = 5;
optional ResearchCost research_cost = 6;
@@ -79,7 +80,7 @@ message TrackDefinition {
message EngineDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
optional ResearchCost research_cost = 3;
required uint32 tier = 4;
required float fire_chance = 5;
@@ -94,7 +95,7 @@ message TurretDefinition {
required uint32 view_range = 3;
required float traverse_speed = 4;
optional ResearchCost research_cost = 5;
required string name = 6;
required I18nString name = 6;
required uint32 tier = 7;
required uint32 weight = 8;
repeated GunDefinition guns = 9;
@@ -145,7 +146,7 @@ message GunDefinitionBase {
required float rotation_speed = 2;
optional ResearchCost research_cost = 3;
required uint32 weight = 4;
required string name = 5;
required I18nString name = 5;
required uint32 tier = 6;
repeated ShellDefinition shells = 7;
required float camouflage_loss = 8;
@@ -160,7 +161,7 @@ message GunDefinitionBase {
message ShellDefinition {
required uint32 id = 1;
required string name = 2;
required I18nString name = 2;
required uint32 velocity = 3;
required uint32 armor_damage = 4;
required uint32 module_damage = 5;
+87 -81
View File
@@ -6,6 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { I18nString, createBaseI18nString } from "./i18n";
export const protobufPackage = "blitzkit";
@@ -261,7 +262,6 @@ export interface TankDefinitions_TanksEntry {
export interface TankDefinition {
id: number;
roles: { [key: number]: number };
description?: string | undefined;
fixed_camouflage: boolean;
camouflages: number[];
ancestors: number[];
@@ -269,8 +269,8 @@ export interface TankDefinition {
crew: Crew[];
health: number;
nation: string;
name: string;
name_full?: string | undefined;
name: I18nString;
name_full?: I18nString | undefined;
type: TankType;
max_consumables: number;
max_provisions: number;
@@ -317,7 +317,7 @@ export interface TankPrice {
export interface TrackDefinition {
id: number;
tier: number;
name: string;
name: I18nString;
weight: number;
traverse_speed: number;
research_cost?: ResearchCost | undefined;
@@ -331,7 +331,7 @@ export interface TrackDefinition {
export interface EngineDefinition {
id: number;
name: string;
name: I18nString;
research_cost?: ResearchCost | undefined;
tier: number;
fire_chance: number;
@@ -346,7 +346,7 @@ export interface TurretDefinition {
view_range: number;
traverse_speed: number;
research_cost?: ResearchCost | undefined;
name: string;
name: I18nString;
tier: number;
weight: number;
guns: GunDefinition[];
@@ -397,7 +397,7 @@ export interface GunDefinitionBase {
rotation_speed: number;
research_cost?: ResearchCost | undefined;
weight: number;
name: string;
name: I18nString;
tier: number;
shells: ShellDefinition[];
camouflage_loss: number;
@@ -412,7 +412,7 @@ export interface GunDefinitionBase {
export interface ShellDefinition {
id: number;
name: string;
name: I18nString;
velocity: number;
armor_damage: number;
module_damage: number;
@@ -603,7 +603,7 @@ export const TankDefinitions_TanksEntry: MessageFns<TankDefinitions_TanksEntry>
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? TankDefinition.fromPartial(object.value)
: createBaseTankDefinition();
: undefined;
return message;
},
};
@@ -612,7 +612,6 @@ function createBaseTankDefinition(): TankDefinition {
return {
id: 0,
roles: {},
description: undefined,
fixed_camouflage: false,
camouflages: [],
ancestors: [],
@@ -620,7 +619,7 @@ function createBaseTankDefinition(): TankDefinition {
crew: [],
health: 0,
nation: "",
name: "",
name: createBaseI18nString(),
name_full: undefined,
type: 0,
max_consumables: 0,
@@ -652,9 +651,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
Object.entries(message.roles).forEach(([key, value]) => {
TankDefinition_RolesEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join();
});
if (message.description !== undefined && message.description !== undefined) {
writer.uint32(26).string(message.description);
}
if (message.fixed_camouflage !== false) {
writer.uint32(32).bool(message.fixed_camouflage);
}
@@ -682,11 +678,11 @@ export const TankDefinition: MessageFns<TankDefinition> = {
if (message.nation !== "") {
writer.uint32(82).string(message.nation);
}
if (message.name !== "") {
writer.uint32(90).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(90).fork()).join();
}
if (message.name_full !== undefined && message.name_full !== undefined) {
writer.uint32(98).string(message.name_full);
if (message.name_full !== undefined) {
I18nString.encode(message.name_full, writer.uint32(98).fork()).join();
}
if (message.type !== 0) {
writer.uint32(104).int32(message.type);
@@ -774,14 +770,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
}
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.description = reader.string();
continue;
}
case 4: {
if (tag !== 32) {
break;
@@ -873,7 +861,7 @@ export const TankDefinition: MessageFns<TankDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 12: {
@@ -881,7 +869,7 @@ export const TankDefinition: MessageFns<TankDefinition> = {
break;
}
message.name_full = reader.string();
message.name_full = I18nString.decode(reader, reader.uint32());
continue;
}
case 13: {
@@ -1054,7 +1042,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
return acc;
}, {})
: {},
description: isSet(object.description) ? globalThis.String(object.description) : undefined,
fixed_camouflage: globalThis.Boolean(assertSet("TankDefinition.fixed_camouflage", object.fixed_camouflage)),
camouflages: globalThis.Array.isArray(object?.camouflages)
? object.camouflages.map((e: any) => globalThis.Number(e))
@@ -1068,8 +1055,8 @@ export const TankDefinition: MessageFns<TankDefinition> = {
crew: globalThis.Array.isArray(object?.crew) ? object.crew.map((e: any) => Crew.fromJSON(e)) : [],
health: globalThis.Number(assertSet("TankDefinition.health", object.health)),
nation: globalThis.String(assertSet("TankDefinition.nation", object.nation)),
name: globalThis.String(assertSet("TankDefinition.name", object.name)),
name_full: isSet(object.name_full) ? globalThis.String(object.name_full) : undefined,
name: I18nString.fromJSON(assertSet("TankDefinition.name", object.name)),
name_full: isSet(object.name_full) ? I18nString.fromJSON(object.name_full) : undefined,
type: tankTypeFromJSON(assertSet("TankDefinition.type", object.type)),
max_consumables: globalThis.Number(assertSet("TankDefinition.max_consumables", object.max_consumables)),
max_provisions: globalThis.Number(assertSet("TankDefinition.max_provisions", object.max_provisions)),
@@ -1112,9 +1099,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
});
}
}
if (message.description !== undefined && message.description !== undefined) {
obj.description = message.description;
}
if (message.fixed_camouflage !== false) {
obj.fixed_camouflage = message.fixed_camouflage;
}
@@ -1136,11 +1120,11 @@ export const TankDefinition: MessageFns<TankDefinition> = {
if (message.nation !== "") {
obj.nation = message.nation;
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.name_full !== undefined && message.name_full !== undefined) {
obj.name_full = message.name_full;
if (message.name_full !== undefined) {
obj.name_full = I18nString.toJSON(message.name_full);
}
if (message.type !== 0) {
obj.type = tankTypeToJSON(message.type);
@@ -1214,7 +1198,6 @@ export const TankDefinition: MessageFns<TankDefinition> = {
}
return acc;
}, {});
message.description = object.description ?? undefined;
message.fixed_camouflage = object.fixed_camouflage ?? false;
message.camouflages = object.camouflages?.map((e) => e) || [];
message.ancestors = object.ancestors?.map((e) => e) || [];
@@ -1222,8 +1205,12 @@ export const TankDefinition: MessageFns<TankDefinition> = {
message.crew = object.crew?.map((e) => Crew.fromPartial(e)) || [];
message.health = object.health ?? 0;
message.nation = object.nation ?? "";
message.name = object.name ?? "";
message.name_full = object.name_full ?? undefined;
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.name_full = (object.name_full !== undefined && object.name_full !== null)
? I18nString.fromPartial(object.name_full)
: undefined;
message.type = object.type ?? 0;
message.max_consumables = object.max_consumables ?? 0;
message.max_provisions = object.max_provisions ?? 0;
@@ -1582,7 +1569,7 @@ function createBaseTrackDefinition(): TrackDefinition {
return {
id: 0,
tier: 0,
name: "",
name: createBaseI18nString(),
weight: 0,
traverse_speed: 0,
research_cost: undefined,
@@ -1603,8 +1590,8 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
if (message.tier !== 0) {
writer.uint32(16).uint32(message.tier);
}
if (message.name !== "") {
writer.uint32(26).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(26).fork()).join();
}
if (message.weight !== 0) {
writer.uint32(32).uint32(message.weight);
@@ -1664,7 +1651,7 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 4: {
@@ -1752,7 +1739,7 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
return {
id: globalThis.Number(assertSet("TrackDefinition.id", object.id)),
tier: globalThis.Number(assertSet("TrackDefinition.tier", object.tier)),
name: globalThis.String(assertSet("TrackDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("TrackDefinition.name", object.name)),
weight: globalThis.Number(assertSet("TrackDefinition.weight", object.weight)),
traverse_speed: globalThis.Number(assertSet("TrackDefinition.traverse_speed", object.traverse_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
@@ -1775,8 +1762,8 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.weight !== 0) {
obj.weight = Math.round(message.weight);
@@ -1815,7 +1802,9 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
const message = createBaseTrackDefinition();
message.id = object.id ?? 0;
message.tier = object.tier ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.weight = object.weight ?? 0;
message.traverse_speed = object.traverse_speed ?? 0;
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
@@ -1832,7 +1821,16 @@ export const TrackDefinition: MessageFns<TrackDefinition> = {
};
function createBaseEngineDefinition(): EngineDefinition {
return { id: 0, name: "", research_cost: undefined, tier: 0, fire_chance: 0, power: 0, weight: 0, unlocks: [] };
return {
id: 0,
name: createBaseI18nString(),
research_cost: undefined,
tier: 0,
fire_chance: 0,
power: 0,
weight: 0,
unlocks: [],
};
}
export const EngineDefinition: MessageFns<EngineDefinition> = {
@@ -1840,8 +1838,8 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.research_cost !== undefined) {
ResearchCost.encode(message.research_cost, writer.uint32(26).fork()).join();
@@ -1884,7 +1882,7 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -1947,7 +1945,7 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
fromJSON(object: any): EngineDefinition {
return {
id: globalThis.Number(assertSet("EngineDefinition.id", object.id)),
name: globalThis.String(assertSet("EngineDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("EngineDefinition.name", object.name)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
tier: globalThis.Number(assertSet("EngineDefinition.tier", object.tier)),
fire_chance: globalThis.Number(assertSet("EngineDefinition.fire_chance", object.fire_chance)),
@@ -1962,8 +1960,8 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.research_cost !== undefined) {
obj.research_cost = ResearchCost.toJSON(message.research_cost);
@@ -1992,7 +1990,9 @@ export const EngineDefinition: MessageFns<EngineDefinition> = {
fromPartial<I extends Exact<DeepPartial<EngineDefinition>, I>>(object: I): EngineDefinition {
const message = createBaseEngineDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
? ResearchCost.fromPartial(object.research_cost)
: undefined;
@@ -2012,7 +2012,7 @@ function createBaseTurretDefinition(): TurretDefinition {
view_range: 0,
traverse_speed: 0,
research_cost: undefined,
name: "",
name: createBaseI18nString(),
tier: 0,
weight: 0,
guns: [],
@@ -2037,8 +2037,8 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
if (message.research_cost !== undefined) {
ResearchCost.encode(message.research_cost, writer.uint32(42).fork()).join();
}
if (message.name !== "") {
writer.uint32(50).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(50).fork()).join();
}
if (message.tier !== 0) {
writer.uint32(56).uint32(message.tier);
@@ -2107,7 +2107,7 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 7: {
@@ -2158,7 +2158,7 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
view_range: globalThis.Number(assertSet("TurretDefinition.view_range", object.view_range)),
traverse_speed: globalThis.Number(assertSet("TurretDefinition.traverse_speed", object.traverse_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
name: globalThis.String(assertSet("TurretDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("TurretDefinition.name", object.name)),
tier: globalThis.Number(assertSet("TurretDefinition.tier", object.tier)),
weight: globalThis.Number(assertSet("TurretDefinition.weight", object.weight)),
guns: globalThis.Array.isArray(object?.guns) ? object.guns.map((e: any) => GunDefinition.fromJSON(e)) : [],
@@ -2183,8 +2183,8 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
if (message.research_cost !== undefined) {
obj.research_cost = ResearchCost.toJSON(message.research_cost);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
@@ -2213,7 +2213,9 @@ export const TurretDefinition: MessageFns<TurretDefinition> = {
message.research_cost = (object.research_cost !== undefined && object.research_cost !== null)
? ResearchCost.fromPartial(object.research_cost)
: undefined;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tier = object.tier ?? 0;
message.weight = object.weight ?? 0;
message.guns = object.guns?.map((e) => GunDefinition.fromPartial(e)) || [];
@@ -2858,7 +2860,7 @@ function createBaseGunDefinitionBase(): GunDefinitionBase {
rotation_speed: 0,
research_cost: undefined,
weight: 0,
name: "",
name: createBaseI18nString(),
tier: 0,
shells: [],
camouflage_loss: 0,
@@ -2886,8 +2888,8 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
if (message.weight !== 0) {
writer.uint32(32).uint32(message.weight);
}
if (message.name !== "") {
writer.uint32(42).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(42).fork()).join();
}
if (message.tier !== 0) {
writer.uint32(48).uint32(message.tier);
@@ -2966,7 +2968,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 6: {
@@ -3064,7 +3066,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
rotation_speed: globalThis.Number(assertSet("GunDefinitionBase.rotation_speed", object.rotation_speed)),
research_cost: isSet(object.research_cost) ? ResearchCost.fromJSON(object.research_cost) : undefined,
weight: globalThis.Number(assertSet("GunDefinitionBase.weight", object.weight)),
name: globalThis.String(assertSet("GunDefinitionBase.name", object.name)),
name: I18nString.fromJSON(assertSet("GunDefinitionBase.name", object.name)),
tier: globalThis.Number(assertSet("GunDefinitionBase.tier", object.tier)),
shells: globalThis.Array.isArray(object?.shells)
? object.shells.map((e: any) => ShellDefinition.fromJSON(e))
@@ -3098,8 +3100,8 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
if (message.weight !== 0) {
obj.weight = Math.round(message.weight);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.tier !== 0) {
obj.tier = Math.round(message.tier);
@@ -3145,7 +3147,9 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
? ResearchCost.fromPartial(object.research_cost)
: undefined;
message.weight = object.weight ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.tier = object.tier ?? 0;
message.shells = object.shells?.map((e) => ShellDefinition.fromPartial(e)) || [];
message.camouflage_loss = object.camouflage_loss ?? 0;
@@ -3163,7 +3167,7 @@ export const GunDefinitionBase: MessageFns<GunDefinitionBase> = {
function createBaseShellDefinition(): ShellDefinition {
return {
id: 0,
name: "",
name: createBaseI18nString(),
velocity: 0,
armor_damage: 0,
module_damage: 0,
@@ -3183,8 +3187,8 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.name !== "") {
writer.uint32(18).string(message.name);
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.velocity !== 0) {
writer.uint32(24).uint32(message.velocity);
@@ -3242,7 +3246,7 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
break;
}
message.name = reader.string();
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
@@ -3345,7 +3349,7 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
fromJSON(object: any): ShellDefinition {
return {
id: globalThis.Number(assertSet("ShellDefinition.id", object.id)),
name: globalThis.String(assertSet("ShellDefinition.name", object.name)),
name: I18nString.fromJSON(assertSet("ShellDefinition.name", object.name)),
velocity: globalThis.Number(assertSet("ShellDefinition.velocity", object.velocity)),
armor_damage: globalThis.Number(assertSet("ShellDefinition.armor_damage", object.armor_damage)),
module_damage: globalThis.Number(assertSet("ShellDefinition.module_damage", object.module_damage)),
@@ -3365,8 +3369,8 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.name !== "") {
obj.name = message.name;
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.velocity !== 0) {
obj.velocity = Math.round(message.velocity);
@@ -3410,7 +3414,9 @@ export const ShellDefinition: MessageFns<ShellDefinition> = {
fromPartial<I extends Exact<DeepPartial<ShellDefinition>, I>>(object: I): ShellDefinition {
const message = createBaseShellDefinition();
message.id = object.id ?? 0;
message.name = object.name ?? "";
message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.velocity = object.velocity ?? 0;
message.armor_damage = object.armor_damage ?? 0;
message.module_damage = object.module_damage ?? 0;
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"]
"include": ["src/**/*", "../i18n/src/strings.ts"]
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "@blitzkit/i18n",
"private": true,
"type": "module",
"main": "src/index.ts",
"dependencies": {
"ts-deepmerge": "^7.0.2"
},
"devDependencies": {}
}
+3
View File
@@ -0,0 +1,3 @@
export * from './literals';
export * from './strings';
export * from './unwrapper';
+5
View File
@@ -0,0 +1,5 @@
export function literals(string: string, literals: string[]) {
return literals.reduce((accumulator, literal, index) => {
return accumulator.replace(`%s${index + 1}`, literal);
}, string);
}
+100
View File
@@ -0,0 +1,100 @@
import { merge } from 'ts-deepmerge';
import en from '../strings/en.json';
import es from '../strings/es.json';
import fr from '../strings/fr.json';
import ja from '../strings/ja.json';
import pl from '../strings/pl.json';
import pt from '../strings/pt.json';
import ru from '../strings/ru.json';
import uk from '../strings/uk.json';
import zh from '../strings/zh.json';
export type BlitzKitStrings = typeof en;
const stringsRaw = { en, es, fr, ja, pl, pt, ru, uk, zh };
export const SUPPORTED_LOCALES = Object.keys(stringsRaw) as SupportedLocale[];
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
function redact(string: string) {
const matches = [...string.matchAll(/%s\d+/g)];
const characters = string
.split('')
.map((char) => (char === ' ' ? ' ' : '█')) as string[];
matches.forEach((match) => {
const start = match.index;
const matchText = match[0];
for (let i = 0; i < matchText.length; i++) {
characters[start + i] = matchText[i];
}
});
return characters.join('');
}
function nuke(object: TranslationTree) {
for (const key in object) {
const typedKey = key as keyof typeof object;
if (typeof object[typedKey] === 'string') {
const value = object[typedKey];
object[typedKey] = redact(value as string);
} else nuke(object[typedKey] as TranslationTree);
}
}
const stringsPartial: Partial<Record<SupportedLocale, TranslationTree>> = {};
for (const locale in stringsRaw) {
if (locale === DEFAULT_LOCALE) {
stringsPartial[DEFAULT_LOCALE] = stringsRaw[DEFAULT_LOCALE];
continue;
}
const localeStrings = stringsRaw[locale as SupportedLocale];
stringsPartial[locale as SupportedLocale] = merge(
stringsRaw[DEFAULT_LOCALE],
localeStrings,
);
if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') {
nuke(stringsPartial[locale as SupportedLocale]!);
}
}
export const STRINGS = stringsPartial as Record<
SupportedLocale,
BlitzKitStrings
>;
export type SupportedLocale = keyof typeof stringsRaw;
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
en: 'en',
es: 'es',
fr: 'fr',
ja: 'ja',
pt: 'pt',
ru: 'ru',
uk: 'uk',
zh: 'zh-Hans',
pl: 'pl',
};
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
en: '🇺🇸',
es: '🇪🇸',
fr: '🇫🇷',
ja: '🇯🇵',
pt: '🇵🇹',
ru: '🇷🇺',
uk: '🇺🇦',
zh: '🇨🇳',
pl: '🇵🇱',
};
export type TranslationTree = { [key: string]: TranslationNode };
export type TranslationNode = string | TranslationTree;
+12
View File
@@ -0,0 +1,12 @@
import { I18nString } from '@blitzkit/core';
import { DEFAULT_LOCALE } from './strings';
export function unwrapper(locale: string) {
return function (i18nString: I18nString) {
if (locale in i18nString.locales) {
return i18nString.locales[locale];
}
return i18nString.locales[DEFAULT_LOCALE];
};
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Loaded 100 of 291 files, more files were not shown because too many files have changed in this diff. Show more