mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-10 19:26:08 +01:00
Compare commits
292
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6b006c2ad | ||
|
|
3ec2425597 | ||
|
|
7785e50783 | ||
|
|
5a171daa10 | ||
|
|
9843713dad | ||
|
|
ef14d7a28f | ||
|
|
fcc2d49859 | ||
|
|
d9e90cdd06 | ||
|
|
a1e45c2af7 | ||
|
|
4744eebf3b | ||
|
|
1e996cb970 | ||
|
|
8639b31b8f | ||
|
|
889a92e98b | ||
|
|
e7fe3a4baf | ||
|
|
ab10343704 | ||
|
|
9412be2d7c | ||
|
|
66cf590f1c | ||
|
|
7c18549c00 | ||
|
|
9b97d3e27d | ||
|
|
aadc252e47 | ||
|
|
320a764d45 | ||
|
|
5594c0ec3b | ||
|
|
3d69d464f5 | ||
|
|
dd0b2bade7 | ||
|
|
99c90cfa9e | ||
|
|
dd3ce21508 | ||
|
|
b512cb2292 | ||
|
|
907e71ce03 | ||
|
|
10942879a6 | ||
|
|
d60551d30a | ||
|
|
b8c8528e1c | ||
|
|
0c9643016a | ||
|
|
61d1ebbad6 | ||
|
|
e9b7059cc0 | ||
|
|
a359897a2a | ||
|
|
e5f7d72df3 | ||
|
|
57092d8df0 | ||
|
|
86508d5413 | ||
|
|
33373c286f | ||
|
|
7443f75347 | ||
|
|
d09f934b80 | ||
|
|
7f919ebb87 | ||
|
|
0929825555 | ||
|
|
4de0fc5ec1 | ||
|
|
e66610f86e | ||
|
|
674abc4923 | ||
|
|
25cee8a36b | ||
|
|
dcbac5e017 | ||
|
|
0c94d42f3f | ||
|
|
7ab5e94323 | ||
|
|
f9ea5ba74a | ||
|
|
1e7162d2da | ||
|
|
cfba53a655 | ||
|
|
77a85641f0 | ||
|
|
5330a27e96 | ||
|
|
a791aff4a0 | ||
|
|
df8a5967f0 | ||
|
|
6261dfc9cd | ||
|
|
766801826c | ||
|
|
7b728596ca | ||
|
|
0bf6ac33a5 | ||
|
|
9a33b84b1c | ||
|
|
c14efe86cc | ||
|
|
4ff9a2ec7f | ||
|
|
75d7b4554b | ||
|
|
27c8b348bb | ||
|
|
a55ab5e7ee | ||
|
|
12e90cc6ee | ||
|
|
01755a2a64 | ||
|
|
080f0a0eb7 | ||
|
|
a646cc1e57 | ||
|
|
d107813c37 | ||
|
|
27d3bbf346 | ||
|
|
675a613a0e | ||
|
|
50c77e0ae1 | ||
|
|
6e0429332d | ||
|
|
7afce6cbcb | ||
|
|
17c7a5c7f5 | ||
|
|
f5aa2ff9de | ||
|
|
96a8f225fc | ||
|
|
d15a5aee69 | ||
|
|
84dc5e4960 | ||
|
|
58b5ee7f83 | ||
|
|
8a99d34af9 | ||
|
|
7cd7bd83dc | ||
|
|
c29d36a4b7 | ||
|
|
7d3b6221d7 | ||
|
|
fd66f9ab17 | ||
|
|
6fbb0e81cb | ||
|
|
6dc82588c1 | ||
|
|
c6eae9ad51 | ||
|
|
ebdd7629ff | ||
|
|
646bccf370 | ||
|
|
86206f2d89 | ||
|
|
bc0dd87e31 | ||
|
|
e26bb4efe0 | ||
|
|
66cf1fd223 | ||
|
|
335cd4d699 | ||
|
|
95fa5bc1a5 | ||
|
|
50eac9eba6 | ||
|
|
1598ae973f | ||
|
|
69f3a95379 | ||
|
|
3846cbab4a | ||
|
|
30e87743fc | ||
|
|
fe4afdcbf0 | ||
|
|
ef2a47e756 | ||
|
|
c2d625e1ed | ||
|
|
d33990bdb4 | ||
|
|
96482d4268 | ||
|
|
8b527f2333 | ||
|
|
f56c31fb48 | ||
|
|
7d164e415a | ||
|
|
b3a9f0889e | ||
|
|
6d24ed92d0 | ||
|
|
d287e66265 | ||
|
|
ac29d1ead0 | ||
|
|
b6c3fdc4e9 | ||
|
|
4efdaaf7c9 | ||
|
|
87b6a36520 | ||
|
|
cf38cb3a6e | ||
|
|
97fc386bc7 | ||
|
|
abfb3f8ddb | ||
|
|
9cb5113f6e | ||
|
|
7dd1c0c32a | ||
|
|
03233f3fdc | ||
|
|
377159c298 | ||
|
|
d8431fb620 | ||
|
|
0546ddefbd | ||
|
|
26f6889012 | ||
|
|
3ad6088565 | ||
|
|
b6fd568cc6 | ||
|
|
3861b07aef | ||
|
|
2bf57e2c72 | ||
|
|
744c6654cf | ||
|
|
b82e905757 | ||
|
|
c42e5b7dc3 | ||
|
|
b716218ecd | ||
|
|
511346e4bb | ||
|
|
5c2e49453e | ||
|
|
5d397de766 | ||
|
|
aa763f65d1 | ||
|
|
94cbd95509 | ||
|
|
f3762e356f | ||
|
|
5747d7549f | ||
|
|
aea4905d20 | ||
|
|
1558b69931 | ||
|
|
a560a3d3ae | ||
|
|
b620110d0f | ||
|
|
23ea72bf40 | ||
|
|
aaee0b7965 | ||
|
|
347523f9e7 | ||
|
|
3205630a0f | ||
|
|
8446e3fbbd | ||
|
|
6d25a97fc8 | ||
|
|
bdbfce0e18 | ||
|
|
8033e8f7cc | ||
|
|
f98c801d6c | ||
|
|
339663adaf | ||
|
|
2b15357dee | ||
|
|
1e05717daf | ||
|
|
c20f8c5040 | ||
|
|
f22f4eeccd | ||
|
|
79c450798c | ||
|
|
d247e67690 | ||
|
|
69b2035cfc | ||
|
|
62fed63310 | ||
|
|
7c29046390 | ||
|
|
b65c3c1d93 | ||
|
|
87f5d8fe8f | ||
|
|
54c7b6b476 | ||
|
|
7bdaf9ca93 | ||
|
|
525b9377cc | ||
|
|
94cba0c9ba | ||
|
|
4ddf77fc7f | ||
|
|
ae6dc259dc | ||
|
|
c2e95f6ef6 | ||
|
|
868812e578 | ||
|
|
b5064f9e8b | ||
|
|
a0eb0d54f0 | ||
|
|
e47b1315a9 | ||
|
|
60025365c4 | ||
|
|
cde6904513 | ||
|
|
997d1df8f9 | ||
|
|
9f15ded0c9 | ||
|
|
b2ee97b3d4 | ||
|
|
03d819a6c1 | ||
|
|
8ff26d57f6 | ||
|
|
c243ebd19a | ||
|
|
40f5042896 | ||
|
|
21130b29f8 | ||
|
|
d50b054243 | ||
|
|
6b7f8049fe | ||
|
|
e6029157aa | ||
|
|
ba656eb603 | ||
|
|
e8dd26b2ba | ||
|
|
897da9479d | ||
|
|
291395718f | ||
|
|
42e860b171 | ||
|
|
3b6d339310 | ||
|
|
728fd5b824 | ||
|
|
34217b2754 | ||
|
|
1e7acc2775 | ||
|
|
0b5c88531f | ||
|
|
e145d7c6e8 | ||
|
|
af13e3f184 | ||
|
|
19a2492555 | ||
|
|
3c51f4f94a | ||
|
|
86f4712b87 | ||
|
|
ea90ca21b1 | ||
|
|
b3046d8655 | ||
|
|
8f9e537a5e | ||
|
|
9368d058f2 | ||
|
|
31b628049a | ||
|
|
072e913433 | ||
|
|
e3f131cdfd | ||
|
|
5cab394ced | ||
|
|
2033fab21a | ||
|
|
7471567147 | ||
|
|
43aee373ea | ||
|
|
993083665b | ||
|
|
f2c25424d9 | ||
|
|
bbe53deb98 | ||
|
|
1f07c2be74 | ||
|
|
ea744b673a | ||
|
|
d8a6cac52f | ||
|
|
1524b9a35d | ||
|
|
36738f57a9 | ||
|
|
fca6cb23d1 | ||
|
|
a704b291fd | ||
|
|
e1b5b21fae | ||
|
|
097f2792ae | ||
|
|
089d0e9682 | ||
|
|
6a92b3ae80 | ||
|
|
1e91065033 | ||
|
|
b20bb97ea3 | ||
|
|
ba97a9f28b | ||
|
|
81e480f983 | ||
|
|
40e4e0a274 | ||
|
|
83b7de77de | ||
|
|
20f7119854 | ||
|
|
275ddcc7a6 | ||
|
|
4858f31da4 | ||
|
|
4bc255a624 | ||
|
|
17755641a1 | ||
|
|
0e6b0087ce | ||
|
|
fcd084a86f | ||
|
|
af699ff5b7 | ||
|
|
93665df223 | ||
|
|
f8b4f9372a | ||
|
|
e84e15daf4 | ||
|
|
d7af7e19dd | ||
|
|
c5cf172e42 | ||
|
|
2e8cac885c | ||
|
|
9ed4a856bf | ||
|
|
eedd4b3f27 | ||
|
|
3b7fe0b114 | ||
|
|
a6d66b94f5 | ||
|
|
c7a7bbb809 | ||
|
|
02ce6ba8d9 | ||
|
|
9dd057d594 | ||
|
|
64ec981496 | ||
|
|
8afafdeece | ||
|
|
6b9fd1d392 | ||
|
|
c1efc50134 | ||
|
|
b4b1f4e916 | ||
|
|
3de0c40ce3 | ||
|
|
893bfa8393 | ||
|
|
cc25535c34 | ||
|
|
a4cdf16fcd | ||
|
|
36588b11e6 | ||
|
|
e927838c14 | ||
|
|
5197ebb6a7 | ||
|
|
51089a3e91 | ||
|
|
c6121fd4bd | ||
|
|
7dca9d3244 | ||
|
|
abebfcf6c6 | ||
|
|
470869418c | ||
|
|
cd2bad3b27 | ||
|
|
5e9555aa57 | ||
|
|
084d55b853 | ||
|
|
a3879f3fa7 | ||
|
|
135a2d74c4 | ||
|
|
233f866c9e | ||
|
|
9e880ff24f | ||
|
|
36039563e0 | ||
|
|
022e970537 | ||
|
|
51e99ec442 | ||
|
|
caeba34ba1 | ||
|
|
6d6a634973 | ||
|
|
55e6599eaa | ||
|
|
e146366385 | ||
|
|
b1dd319c1e |
No files matched your search
@@ -29,7 +29,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.ASSET_BOT }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
|
||||
@@ -37,8 +37,8 @@ env:
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }}
|
||||
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 }}
|
||||
PUBLIC_ASSET_REPO: blitzkit/assets
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
@@ -51,8 +51,9 @@ env:
|
||||
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
|
||||
PUBLIC_DEBUG_MISSING_I18N: false
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_ASSET_FAST_USER: blitzkit
|
||||
PUBLIC_ASSET_FAST_REPO: assets
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
|
||||
jobs:
|
||||
download_client:
|
||||
|
||||
@@ -14,6 +14,17 @@ env:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
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 }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -22,6 +22,19 @@ env:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: ${{ secrets.ALLOW_ROBOTS }}
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
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 }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||
SERVICING: ${{ secrets.SERVICING }}
|
||||
|
||||
jobs:
|
||||
host:
|
||||
@@ -34,7 +47,7 @@ jobs:
|
||||
|
||||
- name: Download release assets
|
||||
run: |
|
||||
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/tresabhi/blitzkit/releases/latest \
|
||||
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/blitzkit/blitzkit/releases/latest \
|
||||
| grep "browser_download_url.*bot.zip" \
|
||||
| cut -d : -f 2,3 \
|
||||
| tr -d \")
|
||||
|
||||
@@ -27,4 +27,15 @@ jobs:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
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 }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=asia
|
||||
@@ -27,4 +27,15 @@ jobs:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
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 }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=eu
|
||||
@@ -27,4 +27,15 @@ jobs:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
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 }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=com
|
||||
@@ -18,10 +18,8 @@ concurrency:
|
||||
|
||||
env:
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
ALLOW_ROBOTS: ${{ secrets.ALLOW_ROBOTS }}
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
@@ -29,8 +27,10 @@ env:
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||
SERVICING: ${{ secrets.SERVICING }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
+28
-3
@@ -1,7 +1,7 @@
|
||||
node_modules
|
||||
_node_modules
|
||||
package-lock.json
|
||||
.env
|
||||
*.env
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
@@ -24,7 +24,32 @@ public/sitemap*
|
||||
target
|
||||
# packages/core/src/protos/*.ts
|
||||
/.vercel
|
||||
/.astro
|
||||
.astro
|
||||
.vercel
|
||||
/src
|
||||
/packages/bot/src/prisma
|
||||
/packages/bot/src/prisma
|
||||
dev-dist
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -4,3 +4,6 @@
|
||||
[submodule "submodules/CUE4Parse"]
|
||||
path = submodules/CUE4Parse
|
||||
url = https://github.com/FabianFG/CUE4Parse
|
||||
[submodule "submodules/blitzkit-internal"]
|
||||
path = submodules/blitzkit-internal
|
||||
url = https://github.com/blitzkit/internal
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80
|
||||
}
|
||||
Vendored
+11
-6
@@ -21,6 +21,7 @@
|
||||
"AYUV",
|
||||
"battleresults",
|
||||
"bbox",
|
||||
"beforeinstallprompt",
|
||||
"bepaint",
|
||||
"betakrieg",
|
||||
"BINORMAL",
|
||||
@@ -76,16 +77,20 @@
|
||||
"echarts",
|
||||
"Everytime",
|
||||
"FASTNAME",
|
||||
"favicons",
|
||||
"flexdirection",
|
||||
"fontsource",
|
||||
"FOURCC",
|
||||
"frameloop",
|
||||
"fsformat",
|
||||
"Fullscreen",
|
||||
"fullstats",
|
||||
"fuzzysort",
|
||||
"gamemode",
|
||||
"gameplay",
|
||||
"glsl",
|
||||
"GLTF",
|
||||
"gtag",
|
||||
"haha",
|
||||
"highp",
|
||||
"iife",
|
||||
@@ -108,6 +113,7 @@
|
||||
"Maddoxkkm",
|
||||
"malum",
|
||||
"mantlets",
|
||||
"mediump",
|
||||
"metallicness",
|
||||
"metalness",
|
||||
"minitelrose",
|
||||
@@ -152,6 +158,7 @@
|
||||
"Pyogenics",
|
||||
"ratingleaderboard",
|
||||
"raycaster",
|
||||
"Redirector",
|
||||
"researchable",
|
||||
"Resvg",
|
||||
"rizz",
|
||||
@@ -165,6 +172,7 @@
|
||||
"searchtanks",
|
||||
"SHAREDEXP",
|
||||
"SKLLD",
|
||||
"smoothstep",
|
||||
"Smri",
|
||||
"Specularity",
|
||||
"SSAO",
|
||||
@@ -184,6 +192,7 @@
|
||||
"tankopedia",
|
||||
"tankopedias",
|
||||
"tankstats",
|
||||
"tauri",
|
||||
"texconv",
|
||||
"TEXCOORD",
|
||||
"tresabhi",
|
||||
@@ -220,11 +229,7 @@
|
||||
"yolo",
|
||||
"youtuber",
|
||||
"youtubers",
|
||||
"zustand"
|
||||
"zoomable"
|
||||
],
|
||||
"deno.enable": false,
|
||||
"deno.unstable": false,
|
||||
"cSpell.enableFiletypes": ["glsl", "proto3"],
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"liveServer.settings.multiRootWorkspaceName": "blitzkit"
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
}
|
||||
@@ -1,661 +0,0 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# BlitzKit License
|
||||
|
||||
Version 1.1, August 5th, 2025. Copyright (c) TrèsAbhi.
|
||||
|
||||
Permission is hereby granted to anyone to:
|
||||
|
||||
- Fork, clone, and download the contents of this repository.
|
||||
- Modify the code locally in private.
|
||||
- Use and run the code **only for personal, non-public testing, or educational purposes**.
|
||||
|
||||
The following restrictions apply:
|
||||
|
||||
- You may **not** deploy, host, or make this project or any derivative publicly accessible.
|
||||
- You may **not** share screenshots, recordings, or any form of media from this project unless you intend to merge your changes back into BlitzKit.
|
||||
- You may **not** use this project or its contents in any public or commercial product.
|
||||
- You may **not** redistribute modified or unmodified versions of this project.
|
||||
|
||||
Contributions:
|
||||
|
||||
- You are encouraged to propose changes or improvements via pull requests to the original BlitzKit repository.
|
||||
- Any such contributions will be considered under the same license terms unless explicitly stated otherwise.
|
||||
|
||||
Violations of this license may result in revocation of usage rights and takedown requests.
|
||||
|
||||
For questions or licensing inquiries, contact support@blitzkit.app.
|
||||
|
||||
## Notes on Existing Forks
|
||||
|
||||
All forks fall under these categories:
|
||||
|
||||
- If your fork was made from commit `8462901` (Apr 17, 2023) or between commits `2958124` (Jul 3, 2023) and `85e483e` (Jul 13, 2024), your fork falls under the **MIT license**.
|
||||
- If your fork was made from commits between `5646093` (Jul 13, 2024) and `e3f131c` (Aug 5, 2025), your fork falls under the **AGPL-3.0 license**.
|
||||
- If your fork was made between commit `f7c356d` (Apr 17, 2023) and `2958124` (Jul 3, 2023), your fork falls under **a default exclusive copyright**.
|
||||
- If your fork was made from commits between `e3f131c` (Aug 5, 2025) and today, your fork falls under **this license**.
|
||||
|
||||
MIT and AGPL-3.0 licensed fork owners:
|
||||
|
||||
- You may continue to use your fork of BlitzKit under the corresponding license.
|
||||
- You may upgrade your fork up to commit `5cab394` (Aug 4, 2025) and keep the AGPL-3.0 license.
|
||||
- You may upgrade to commit `e3f131c` (Aug 5, 2025) or beyond but have to **switch to this license** where all rules apply.
|
||||
- If you upgrade to commit `e3f131c` (Aug 5, 2025) or beyond, you must retract all public deployments of BlitzKit.
|
||||
- Existing screenshots, recordings, or any form of media can remain.
|
||||
|
||||
Default exclusive copyright fork owners:
|
||||
|
||||
- You may continue to use their forks under the AGPL-3.0 license as no license was applied to BlitzKit at the time of forking.
|
||||
|
||||
BlitzKit licensed fork owners:
|
||||
|
||||
- You may continue to upgrade to the latest commits only under the conditions of this license.
|
||||
@@ -1,13 +1,49 @@
|
||||
## BlitzKit
|
||||
# BlitzKit
|
||||
|
||||
Your premier source for all things Blitz.
|
||||
Tools for everything and anything, World of Tanks Blitz.
|
||||
|
||||
### https://blitzkit.app/
|
||||
This GitHub repository contains the code for BlitzKit and all its services. If you are here, looking to use BlitzKit, go to https://blitzkit.app/.
|
||||
|
||||

|
||||
If you intend to contribute to the code, note that you may need to check out sensitive, closed-source submodules to locally develop certain aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or discuss sensitive content, such as environment variables or submodules.
|
||||
|
||||
## Invite the bot
|
||||
## Packages
|
||||
|
||||
Go to [application directory for BlitzKit](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go.
|
||||
| Package | Title | Source | Memo |
|
||||
| ------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [packages/app](packages/app) | BlitzKit | [Play Store](https://play.google.com/store/apps/details?id=com.tresabhi.blitzkit), [App Store](https://apps.apple.com/app/4321), [Microsoft Store](https://apps.microsoft.com/detail/zyxw) | The native app of BlitzKit for all platforms. Unavailable from stores at this time. |
|
||||
| [packages/bot](packages/bot) | BlitzKit Discord Bot | [Discord Application Directory](https://discord.com/discovery/applications/1097673957865443370) | The one-in-all Discord bot for statistics. |
|
||||
| [packages/core](packages/core) | | | Mutual internal code shared between all other packages. |
|
||||
| [packages/i18n](packages/i18n) | | [Crowdin](https://crowdin.com/project/blitzkrieg) | Translations served as JSON shared across all other packages, including some utility code. |
|
||||
| [packages/scripts](packages/scripts) | | | CLI utilities for the development and deployment of BlitzKit services. |
|
||||
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro-oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
|
||||
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website provides a wide range of tools, APIs, and interconnects all other services. |
|
||||
|
||||

|
||||
## Submodules
|
||||
|
||||
| Submodule | Title | Source | Memo |
|
||||
| ------------------------------------------------------------ | ----------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [submodules/blitzkit-closed](submodules/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
|
||||
| [submodules/blitzkit-internal](submodules/blitzkit-internal) | InternalKit | [blitzkit/internal](https://github.com/blitzkit/internal) | Closed source, sensitive internal documentation hidden from the public to prevent bad actors from abusing the game. Access to this submodule is **not negotiable**. |
|
||||
| [submodules/CUE4Parse](submodules/CUE4Parse) | CUE4Parse | [FabianFG/CUE4Parse](https://github.com/FabianFG/CUE4Parse) | An Unreal Engine archives and packages parsing library in C#. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Name | Source | BlitzKit Default | Memo |
|
||||
| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PUBLIC_WARGAMING_APPLICATION_ID` | [Wargaming Developer's Room](https://developers.wargaming.net/) | | Application ID needed to communicate with Wargaming APIs. |
|
||||
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating whether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
|
||||
| `ALLOW_ROBOTS` | | `true` | Toggles between `Allow: /` and `Disallow: /` in the website's `/robots.txt`. This is usually `false` for all BlitzKit's subdomains. |
|
||||
| `WOTB_GLOSSARY`, `WOTB_GLOSSARY_DOMAIN`, `WOTB_GLOSSARY_PATH` | | | |
|
||||
| `PUBLIC_GOOGLE_TAG_MANAGER_ID` | [Google Tag Manager](https://tagmanager.google.com/) | `G-GL2JVHCGPQ` | Google Tag Manager's unique identifier for analytical purposes. |
|
||||
| `PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID` | [Google Analytics](https://analytics.google.com/) | `430711767` | Google Analytics' unique property ID for [BlitzKit's apex domain](https://blitzkit.app/) and subdomains. |
|
||||
| `GOOGLE_APPLICATION_CREDENTIALS` | [Google Cloud](https://cloud.google.com/) | | Google Cloud's API key, primarily used for ranking popular tanks and finding relevant tank review videos in Tankopedia. |
|
||||
| `PUBLIC_DISCORD_SERVER_ID` | [BlitzKit Discord Server](https://discord.gg/nDt7AjGJQH) | `734786591205359697` | BlitzKit Discord Server's id. |
|
||||
| `PUBLIC_ASSET_BASE` | [blitzkit/assets](https://github.com/blitzkit/assets) | `https://blitzkit.github.io/assets` | The root domain and path that serves the external assets like tank models and icons. |
|
||||
| `PUBLIC_BRANCH` | | `main` | The branch that the website is operating on. This is completely nominal. |
|
||||
| `PUBLIC_GUIDE_WEBSITE` | [GuidesBlitz](https://guidesblitz.com/) | `https://guidesblitz.com` | The website that serves the encyclopedia-like content in BlitzKit. |
|
||||
| `SERVICING` | | `false` | Wether the website is down for servicing. |
|
||||
| `DISCORD_CLIENT_ID` | [Discord Developer Portal](https://discord.com/developers/applications) | `1097673957865443370` | The BlitzKit Discord Bot's user ID. |
|
||||
| `GH_TOKEN` | [GitHub Fine-Grained Tokens](https://github.com/settings/personal-access-tokens) | | The GitHub API key used for checking out private submodules and pushing content to the asset repository. |
|
||||
| `DISCORD_TOKEN` | [Discord Developer Portal](https://discord.com/developers/applications) | | The Discord API Key for the BlitzKit Discord Bot. |
|
||||
| `DATABASE_URL` | [CockroachDB](https://www.cockroachlabs.com/) | | The CockroachDB-SQL database URL used to link Wargaming and Discord users. |
|
||||
| `WOTB_DLC_CDN` | | | |
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.27.12
|
||||
|
||||
This update only affects the website.
|
||||
|
||||
## Features
|
||||
|
||||
- Added the ability to sort by HE shell penetration
|
||||
- The Tankopedia compare bars now account for equipment and selected shells
|
||||
- Made the hero section of the Tankopedia a bit shorter to show users that the page is scrollable
|
||||
|
||||
## Fixes
|
||||
|
||||
- The "Clear filters" button no longer shows up after clearing the search bar
|
||||
- Guns for some tanks no longer go transparent when far away from the camera
|
||||
- The dynamic armor tab no longer lags and flickers when rotating the turret of a tank
|
||||
- Reimplemented the missing tool backgrounds in the navigation bar hamburger menu
|
||||
- The navigation bar now properly goes transparent for pages that request like the Tankopedia pages
|
||||
- Removed the tank name text outline because it doesn't work on most devices
|
||||
@@ -0,0 +1,19 @@
|
||||
# BlitzKit 1.27.13
|
||||
|
||||
This update adds a feature for a future version of WoTB 👁️👃👁️
|
||||
|
||||
## Features
|
||||
|
||||
- Added compatibility for the new mechanic for the upcoming Polish Tank Destroyers
|
||||
- This code will remain dormant until the update for WoTB is released
|
||||
- Added a progress bar to the hero section of the Tankopedia to show that the model is loading
|
||||
- Reordered some Tankopedia statistics to bring important statistics like Damage to the very top
|
||||
|
||||
## Fixes
|
||||
|
||||
- The second column in the footer is now aligned on mobile devices
|
||||
- Fixed an issue that stopped the armor highlighting from working on the Kiwi browser and on older devices
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Removed permissions for robots to crawl non-blitzkit.app domains
|
||||
@@ -0,0 +1,8 @@
|
||||
# BlitzKit 1.27.14
|
||||
|
||||
This is the 14th patch for `1.27`, making it the new longest lasting version beating the previous record of `1.15.13`!
|
||||
|
||||
## Fixes
|
||||
|
||||
- Optimized the rendering of the shells in the Tankopedia search
|
||||
- The Assault Shells cursor now works on iOS devices
|
||||
@@ -0,0 +1,7 @@
|
||||
# BlitzKit 1.27.15
|
||||
|
||||
I did not see this coming
|
||||
|
||||
## Fixes
|
||||
|
||||
- The Assault Shell graph now supports damage factors of above 100% now
|
||||
@@ -0,0 +1,25 @@
|
||||
# BlitzKit 1.27.16
|
||||
|
||||
## Features
|
||||
|
||||
- Added links to GuidesBlitz to Tankopedia to aid users understand tanks beyond just numbers and stats
|
||||
- Added a gun depression and yaw visualizer
|
||||
|
||||
## Changes
|
||||
|
||||
- Made the colors of the advanced highlighting mode more subtle
|
||||
- Made the Tankopedia stat lines more condensed
|
||||
- You can now scroll the page even with your cursor on the Tankopedia sandbox
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixed a bug where the range of increased gun depression was incorrectly doubled
|
||||
- Removed an unintended space before the "x" in the "Gun damaged" stat
|
||||
- Fixed an issue causing the 3D renderer to render frames even when off screen
|
||||
- Fixed a differential issue causing excessive rerendering of the stat lines
|
||||
- Images no longer exceed the width of the device in the docs
|
||||
- Assault shells no longer scale the module damage with distance
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Removed all code relating to the canceled "Shooting Range" mode
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.27.17
|
||||
|
||||
This update was released early for the website. Today, it'll be officially announced and released for the bot
|
||||
|
||||
## Features
|
||||
|
||||
- Added the shell reload visualizer to aid people in getting a feel for how long shell reloads really are
|
||||
- Added lines of stats for the burst shells
|
||||
|
||||
## Changes
|
||||
|
||||
- Removed the redundant word "Shell" from "Shell reload" and "Shell reloads"
|
||||
- Removed the penetration over distance slider
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixed the DPM of tanks with burst shells like the ATAC
|
||||
- The DPM will still be different from the game due to a bug in World of Tanks Blitz; I am trying to get Wargaming to fix it; trust BlitzKit!
|
||||
@@ -0,0 +1,11 @@
|
||||
# BlitzKit 1.27.18
|
||||
|
||||
## Features
|
||||
|
||||
- Added a mini-game to BlitzKit similar to "Guess Who's That Pokemon" but with tanks: http://blitzkit.vercel.app/guess
|
||||
- Switched to a new renderer for better lighting for the Tankopedia
|
||||
|
||||
## Fixes
|
||||
|
||||
- The loading bar for the Tankopedia sandbox now shows up again
|
||||
- Removed a lot of dead code, improving performance
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.27.19
|
||||
|
||||
Two updates in a day? I just got a little excited by the new Tankopedia lighting.
|
||||
|
||||
## Features
|
||||
|
||||
- The camera is now lower down in the Tankopedia, giving the tank a more heroic feel
|
||||
- Tanks are better framed across all devices, minimizing whitespace
|
||||
- Added a skip button for the reload visualizer to skip to the next event
|
||||
|
||||
## Fixes
|
||||
|
||||
- Removed the useless "show grid" setting
|
||||
- Taking screenshots now no longer includes the scene props like the floor
|
||||
- The subtler color contrast for the advanced highlighting has now been reverted back to the higher contrast
|
||||
- The shot display card is now properly aligned as a proper table again
|
||||
- Tank modules are once again considered in the penetration tester
|
||||
- Tank filters in the antagonist selector in the Tankopedia sandbox are now properly aligned
|
||||
@@ -0,0 +1,13 @@
|
||||
# BlitzKit 1.27.20
|
||||
|
||||
It's all technical updates! The HD tanks will be rolled out over the span of few days.
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Shadows are disabled if your framerate drops below 30fps
|
||||
- Pages that redirect to other pages (like the old /tools/tankopedia) now load a few hundred times faster
|
||||
- Replaced Zustand with a custom state manager called Varuna with the goal to rewrite a significant portion of the website to not require any React for rendering increasing performance
|
||||
- The transition wireframe in the Tankopedia sandbox now uses a lower quality model for performance reasons
|
||||
- Tanks with PBR now use the correct maximum resolution textures
|
||||
- This is a significant update as you can now literally see individual splats of dust on tanks and scratches haha
|
||||
- Baked the ambient occlusion maps into the textures, hence reducing the overall model load times even with higher resolution textures
|
||||
@@ -0,0 +1,16 @@
|
||||
# BlitzKit 1.28.0
|
||||
|
||||
BlitzKit, as an app and the new Mixer tool!
|
||||
|
||||
## Features
|
||||
|
||||
- Created the BlitzKit app that can be downloaded on most devices
|
||||
- Re-did the BlitzKit logo to be a bit warmer
|
||||
- Added the Mixer tool which lets you combine tank models into Frankensteins
|
||||
- Updated all dependencies to the latest version for a decent performance boost
|
||||
|
||||
## Fixes
|
||||
|
||||
- BlitzKit no longer forgets persistent data if the app crashes without properly closing
|
||||
- The tools list in the navbar no longer overflows into the neighboring elements on certain devices
|
||||
- Added keying to a few places, speeding up re-rendering
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.28.1
|
||||
|
||||
## Fixes
|
||||
|
||||
- The lights no longer crash the Mixer tool
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.28.2
|
||||
|
||||
## Fixes
|
||||
|
||||
- BlitzKit no longer tries to redirect translated pages to the same page, over and over again
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.28.3
|
||||
|
||||
## Features
|
||||
|
||||
- Turrets and guns can now be rotated in Mixer without any pitch or yaw limits
|
||||
- Added nation icons to the search results for Guesser
|
||||
- The table now occupies the whole screen in Compare
|
||||
- Added the ability to log in with a Wargaming account in the navbar
|
||||
- Added owned/unowned filters in the Tankopedia filters
|
||||
- Revamped Tankopedia quick controls to be more accessible and obvious
|
||||
|
||||
## Fixes
|
||||
|
||||
- Missing tank icons across the website have been fixed
|
||||
- Events of starting and stopping of the transform tanks now use an asynchronous event instead of a reactive state, making them faster
|
||||
- Removed a hook call on root context, speeding up load times
|
||||
- Tankopedia's pitch input now follows the same sign convention as the pitch and yaw visualizer
|
||||
- Shadows are no longer disabled when the scene is unloaded when the user scrolls away from the canvas
|
||||
+17
-9
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "blitzkit",
|
||||
"private": true,
|
||||
"version": "1.27.11",
|
||||
"version": "1.28.3",
|
||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||
"type": "module",
|
||||
"types": "./types.d.ts",
|
||||
@@ -16,11 +16,15 @@
|
||||
"build:prisma": "prisma generate",
|
||||
"build:sitemap": "bun scripts/sitemap.ts",
|
||||
"build:docs": "vitepress build docs",
|
||||
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
|
||||
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
|
||||
"build:app": "cd packages/app && bun run build",
|
||||
"dev:bot": "bun run build:bot --dev && bun start:bot",
|
||||
"start:bot": "bun dist/bot/main.js",
|
||||
"dev:docs": "vitepress dev docs",
|
||||
"dev:website": "cd packages/website && bun run dev",
|
||||
"dev:app": "cd packages/app && bun run dev",
|
||||
"dev": "bun run dev:website",
|
||||
"dev:api": "bun --filter=@blitzkit/api run dev",
|
||||
"setup:depotdownloader": "bun --filter=@blitzkit/scripts run setup:depotdownloader",
|
||||
"setup:patches": "bun --filter=@blitzkit/scripts run setup:patches",
|
||||
@@ -40,18 +44,22 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"googleapis": "^144.0.0",
|
||||
"@types/xmpp__client": "^0.13.3",
|
||||
"@xmpp/client": "^0.13.6",
|
||||
"astro-favicons": "^3.1.5",
|
||||
"cheerio": "^1.1.2",
|
||||
"googleapis": "^160.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"protobufjs": "^7.5.2",
|
||||
"protobufjs": "^7.5.4",
|
||||
"quicklime": "^1.2.0",
|
||||
"sharp": "^0.33.5"
|
||||
"sharp": "^0.34.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/bun": "^1.2.22",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.14.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prisma": "^6.6.0",
|
||||
"typescript": "^5.8.3"
|
||||
"@types/node": "^24.5.2",
|
||||
"prettier": "^3.6.2",
|
||||
"prisma": "^6.16.2",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@blitzkit/app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"vite": "^6.0.3",
|
||||
"typescript": "~5.6.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
||||
Generated
+5286
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "blitzkit"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||
name = "blitzkit_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle();
|
||||
|
||||
let entry = app_handle
|
||||
.path_resolver()
|
||||
.resolve_resource("../../dist/website/server/entry.mjs")
|
||||
.expect("failed to resolve server.ts");
|
||||
|
||||
Command::new("bun")
|
||||
.arg("run")
|
||||
.arg(entry)
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.expect("failed to start Bun server");
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running Tauri app");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "blitzkit",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.tresabhi.blitzkit",
|
||||
"build": {
|
||||
"beforeDevCommand": "cd ../website && bun run dev",
|
||||
"_beforeBuildCommand": "cd ../website && bun run build",
|
||||
"frontendDist": "../../../dist/website/client",
|
||||
"devUrl": "http://localhost:4321"
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
"windows": [
|
||||
{
|
||||
"url": "http://localhost:4321",
|
||||
"title": "BlitzKit",
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"maximized": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/[email protected]",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": ["../../tsconfig.json"],
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,14 @@
|
||||
"dependencies": {
|
||||
"@blitzkit/core": "workspace:*",
|
||||
"@blitzkit/i18n": "workspace:*",
|
||||
"satori": "^0.10.14",
|
||||
"satori": "^0.18.3",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"discord.js": "^14.16.3",
|
||||
"react": "^18.3.1",
|
||||
"discord.js": "^14.22.1",
|
||||
"react": "^19.1.1",
|
||||
"@radix-ui/colors": "^3.0.0",
|
||||
"fuzzysort": "^2.0.4",
|
||||
"fuzzysort": "^3.1.0",
|
||||
"bepaint": "^0.1.0-beta.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv": "^17.2.2",
|
||||
"markdown-escape": "^2.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import {
|
||||
BlitzTankClass,
|
||||
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';
|
||||
|
||||
type Auction = null | {
|
||||
count: number;
|
||||
has_next: boolean;
|
||||
results: Result[];
|
||||
};
|
||||
|
||||
type Result =
|
||||
| {
|
||||
id?: number;
|
||||
type: 'vehicle';
|
||||
countable?: boolean;
|
||||
entity?: Entity;
|
||||
initial_count: number;
|
||||
current_count: number;
|
||||
saleable?: boolean;
|
||||
available_from?: Date;
|
||||
available_before?: Date;
|
||||
price?: Price;
|
||||
next_price: Price | null;
|
||||
available: true;
|
||||
display?: boolean;
|
||||
next_price_datetime: Date;
|
||||
next_price_timestamp: number;
|
||||
}
|
||||
| {
|
||||
available: false;
|
||||
type: 'vehicle';
|
||||
};
|
||||
|
||||
interface Entity {
|
||||
id: number;
|
||||
name: string;
|
||||
nation: string;
|
||||
subnation: string;
|
||||
use_subnation_flag: boolean;
|
||||
type_slug: BlitzTankClass;
|
||||
level: number;
|
||||
roman_level: string;
|
||||
user_string: string;
|
||||
image_url: string;
|
||||
preview_image_url: string;
|
||||
is_premium: boolean;
|
||||
is_collectible: boolean;
|
||||
}
|
||||
|
||||
interface Price {
|
||||
currency: Currency;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface Currency {
|
||||
name: 'gold';
|
||||
count: number;
|
||||
title: 'Gold';
|
||||
image_url: string;
|
||||
sizes: unknown;
|
||||
type: 'currency';
|
||||
}
|
||||
|
||||
const WARNING_COUNT = 500;
|
||||
|
||||
export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('auction'),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const region: Region = 'com';
|
||||
const pageSize = 80;
|
||||
const saleable = true;
|
||||
const data = await fetch(
|
||||
`https://${regionToRegionSubdomain(
|
||||
region,
|
||||
)}.wotblitz.com/en/api/events/items/auction/?page_size=${
|
||||
pageSize
|
||||
}&type[]=vehicle&saleable=${saleable}`,
|
||||
).then((response) => response.json() as Promise<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 = `# ${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/>',
|
||||
])}`;
|
||||
const body = saleableTanks.map((data, index) => {
|
||||
const tank = awaitedTankDefinitions.tanks[data.entity!.id];
|
||||
|
||||
if (!tank) return '';
|
||||
|
||||
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
|
||||
tank.slug
|
||||
}>)`;
|
||||
const next =
|
||||
data.next_price === null
|
||||
? ''
|
||||
: `\n-# ${literals(strings.bot.commands.auction.body.next, [
|
||||
`<:gold:1317173197082333244> ${data.next_price.value.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;
|
||||
|
||||
return `${index + 1}. ${
|
||||
outString
|
||||
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
|
||||
interaction.locale,
|
||||
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`;
|
||||
});
|
||||
const header = `${title}\n${subtitle}`;
|
||||
const lines = [header, ...body];
|
||||
|
||||
return chunkLines(lines, '\n\n');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -6,23 +6,23 @@ import {
|
||||
getTankStats,
|
||||
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';
|
||||
import { CommandWrapper } from '../../components/CommandWrapper';
|
||||
import { NoData } from '../../components/NoData';
|
||||
import { TitleBar } from '../../components/TitleBar';
|
||||
import { filtersToDescription } from '../../core/blitzkit/filtersToDescription';
|
||||
import { tankDefinitions } from '../../core/blitzkit/nonBlockingPromises';
|
||||
import { UserError } from '../../core/blitzkit/userError';
|
||||
import { filterStats, StatFilters } from '../../core/blitzstars/filterStats';
|
||||
import { getStatsInPeriod } from '../../core/blitzstars/getStatsInPeriod';
|
||||
import { blitzStarsTankAverages } from '../../core/blitzstars/tankAverages';
|
||||
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand';
|
||||
import { ResolvedPlayer } from '../../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../../core/localization/translator';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { chunk } from "lodash-es";
|
||||
import * as Breakdown from "../../components/Breakdown";
|
||||
import { CommandWrapper } from "../../components/CommandWrapper";
|
||||
import { NoData } from "../../components/NoData";
|
||||
import { TitleBar } from "../../components/TitleBar";
|
||||
import { filtersToDescription } from "../../core/blitzkit/filtersToDescription";
|
||||
import { tankDefinitions } from "../../core/blitzkit/nonBlockingPromises";
|
||||
import { UserError } from "../../core/blitzkit/userError";
|
||||
import { filterStats, StatFilters } from "../../core/blitzstars/filterStats";
|
||||
import { getStatsInPeriod } from "../../core/blitzstars/getStatsInPeriod";
|
||||
import { blitzStarsTankAverages } from "../../core/blitzstars/tankAverages";
|
||||
import { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
|
||||
import { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../../core/localization/translator";
|
||||
|
||||
const ROWS_PER_PAGE = 8;
|
||||
const MAX_PAGES = 9;
|
||||
@@ -31,7 +31,7 @@ export async function renderBreakdown(
|
||||
{ region, id }: ResolvedPlayer,
|
||||
{ start, end, name }: ResolvedPeriod,
|
||||
filters: StatFilters,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const { strings, unwrap } = translator(locale);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
@@ -39,7 +39,7 @@ export async function renderBreakdown(
|
||||
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
|
||||
const { filteredOrder } = await filterStats(statsInPeriod, filters);
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const clanData = await getClanAccountInfo(region, id, ['clan']);
|
||||
const clanData = await getClanAccountInfo(region, id, ["clan"]);
|
||||
const tankStats = await getTankStats(region, id);
|
||||
const filterDescriptions = await filtersToDescription(filters, locale);
|
||||
const orderedCurrentStats: BlitzStats[] = [];
|
||||
@@ -74,63 +74,63 @@ export async function renderBreakdown(
|
||||
orderedCareerWN8Full.push(
|
||||
tankAveragesEntry
|
||||
? calculateWN8(tankAveragesEntry.all, tank.all)
|
||||
: undefined,
|
||||
: undefined
|
||||
);
|
||||
});
|
||||
|
||||
const currentBattles = orderedCurrentStats.reduce(
|
||||
(accumulator, current) => accumulator + current.battles,
|
||||
0,
|
||||
0
|
||||
);
|
||||
const careerBattles = tankStats.reduce(
|
||||
(accumulator, current) => accumulator + current.all.battles,
|
||||
0,
|
||||
0
|
||||
);
|
||||
const currentWinrate =
|
||||
orderedCurrentStats.reduce(
|
||||
(accumulator, current) => accumulator + current.wins,
|
||||
0,
|
||||
0
|
||||
) / currentBattles;
|
||||
const careerWinrate =
|
||||
tankStats.reduce(
|
||||
(accumulator, current) => accumulator + current.all.wins,
|
||||
0,
|
||||
0
|
||||
) / careerBattles;
|
||||
const currentWN8 =
|
||||
orderedCurrentWN8.reduce<number>(
|
||||
(accumulator, current, index) =>
|
||||
accumulator + (current ?? 0) * orderedCurrentStats[index].battles,
|
||||
0,
|
||||
0
|
||||
) /
|
||||
orderedCurrentStats.reduce(
|
||||
(accumulator, current, index) =>
|
||||
accumulator +
|
||||
(orderedCurrentWN8[index] === undefined ? 0 : current.battles),
|
||||
0,
|
||||
0
|
||||
);
|
||||
const careerWN8 =
|
||||
orderedCareerWN8Full.reduce<number>(
|
||||
(accumulator, current) => accumulator + (current ?? 0),
|
||||
0,
|
||||
0
|
||||
) /
|
||||
tankStats.reduce(
|
||||
(accumulator, current, index) =>
|
||||
accumulator +
|
||||
(orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles),
|
||||
0,
|
||||
0
|
||||
);
|
||||
const currentDamage =
|
||||
orderedCurrentStats.reduce(
|
||||
(accumulator, current) => accumulator + current.damage_dealt,
|
||||
0,
|
||||
0
|
||||
) / currentBattles;
|
||||
const careerDamage =
|
||||
tankStats.reduce(
|
||||
(accumulator, current) => accumulator + current.all.damage_dealt,
|
||||
0,
|
||||
0
|
||||
) / careerBattles;
|
||||
|
||||
const children: JSX.Element[] = [];
|
||||
const children: React.JSX.Element[] = [];
|
||||
|
||||
if (filteredOrder.length > 0) {
|
||||
children.push(
|
||||
@@ -172,7 +172,7 @@ export async function renderBreakdown(
|
||||
delta: currentDamage - careerDamage,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
/>
|
||||
);
|
||||
|
||||
filteredOrder.forEach((id, index) => {
|
||||
@@ -193,9 +193,9 @@ export async function renderBreakdown(
|
||||
title={
|
||||
tankDefinition
|
||||
? unwrap(tankDefinition.name)
|
||||
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
|
||||
`${id}`,
|
||||
])
|
||||
: literals(strings.bot.commands.breakdown.body.unknown_tank, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
stats={[
|
||||
{
|
||||
@@ -206,7 +206,7 @@ export async function renderBreakdown(
|
||||
{
|
||||
title: strings.bot.commands.breakdown.body.winrate,
|
||||
current: `${(100 * (current.wins / current.battles)).toFixed(
|
||||
2,
|
||||
2
|
||||
)}%`,
|
||||
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
|
||||
delta:
|
||||
@@ -230,24 +230,24 @@ export async function renderBreakdown(
|
||||
{
|
||||
title: strings.bot.commands.breakdown.body.damage,
|
||||
current: Math.round(
|
||||
current.damage_dealt / current.battles,
|
||||
current.damage_dealt / current.battles
|
||||
).toLocaleString(),
|
||||
career: Math.round(
|
||||
career.damage_dealt / career.battles,
|
||||
career.damage_dealt / career.battles
|
||||
).toLocaleString(),
|
||||
delta:
|
||||
current.damage_dealt / current.battles -
|
||||
career.damage_dealt / career.battles,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const pages = chunk(
|
||||
children,
|
||||
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE),
|
||||
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE)
|
||||
);
|
||||
|
||||
if (filteredOrder.length > 0) {
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
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';
|
||||
import { NoData } from '../components/NoData';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { addClanChoices } from '../core/discord/addClanChoices';
|
||||
import { autocompleteClan } from '../core/discord/autocompleteClan';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolveClanFromCommand } from '../core/discord/resolveClanFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
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";
|
||||
import { NoData } from "../components/NoData";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { addClanChoices } from "../core/discord/addClanChoices";
|
||||
import { autocompleteClan } from "../core/discord/autocompleteClan";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolveClanFromCommand } from "../core/discord/resolveClanFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_THRESHOLD = 7;
|
||||
|
||||
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('inactive')
|
||||
command: createLocalizedCommand("inactive")
|
||||
.addStringOption(addClanChoices)
|
||||
.addNumberOption((option) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
return option
|
||||
.setName('threshold')
|
||||
.setName("threshold")
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands.inactive.options.threshold.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.inactive.options.threshold.description,
|
||||
strings.bot.commands.inactive.options.threshold.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.inactive.options.threshold.description,
|
||||
),
|
||||
strings.bot.commands.inactive.options.threshold.description
|
||||
)
|
||||
)
|
||||
.setMinValue(0);
|
||||
}),
|
||||
@@ -47,7 +47,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const { region, id } = await resolveClanFromCommand(interaction);
|
||||
const threshold =
|
||||
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
|
||||
interaction.options.getNumber("threshold")! ?? DEFAULT_THRESHOLD;
|
||||
const time = new Date().getTime() / 1000;
|
||||
const clanInfo = await getClanInfo(region, id);
|
||||
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
|
||||
@@ -57,7 +57,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
[
|
||||
account.nickname,
|
||||
(time - account.last_battle_time) / 60 / 60 / 24,
|
||||
] as [string, number],
|
||||
] as [string, number]
|
||||
)
|
||||
.filter(([, inactiveDays]) => inactiveDays >= threshold)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
@@ -65,10 +65,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
([name, days]) =>
|
||||
[
|
||||
name,
|
||||
literals(strings.bot.commands.inactive.body.listing, [
|
||||
days.toFixed(0),
|
||||
]),
|
||||
] as [string, string],
|
||||
literals(strings.bot.commands.inactive.body.listing, {
|
||||
days: days.toFixed(0),
|
||||
}),
|
||||
] as [string, string]
|
||||
);
|
||||
const hasInactiveMembers = inactive.length > 0;
|
||||
|
||||
@@ -79,7 +79,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
|
||||
description={`${literals(
|
||||
strings.bot.commands.inactive.body.subtitle,
|
||||
[`${threshold}`],
|
||||
{ days: threshold }
|
||||
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -5,31 +5,31 @@ import {
|
||||
flags,
|
||||
getAccountInfo,
|
||||
getTankStats,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import {
|
||||
gameDefinitions,
|
||||
tankDefinitions,
|
||||
} from '../core/blitzkit/nonBlockingPromises';
|
||||
import { addTierChoices } from '../core/discord/addTierChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { chunkLines } from '../core/discord/chunkLines';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "../core/blitzkit/nonBlockingPromises";
|
||||
import { addTierChoices } from "../core/discord/addTierChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { chunkLines } from "../core/discord/chunkLines";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('owned-tanks')
|
||||
command: createLocalizedCommand("owned-tanks")
|
||||
.addStringOption(addTierChoices)
|
||||
.addStringOption(addUsernameChoices),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings, unwrap } = translator(interaction.locale);
|
||||
const tier = Number(interaction.options.getString('tier'));
|
||||
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);
|
||||
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tankStats.map(async (tankData) => ({
|
||||
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
|
||||
id: tankData.tank_id,
|
||||
})),
|
||||
}))
|
||||
)
|
||||
).filter((tank) => tank.tankDefinitions?.tier === tier);
|
||||
const groupedTanks: Record<string, TankDefinition[]> = {};
|
||||
@@ -60,25 +60,25 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const awaitedGameDefinitions = await gameDefinitions;
|
||||
const title = literals(strings.bot.commands.owned_tanks.body.title, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
TIER_ROMAN_NUMERALS[tier],
|
||||
]);
|
||||
const title = literals(strings.bot.commands.owned_tanks.body.title, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
tier: TIER_ROMAN_NUMERALS[tier],
|
||||
});
|
||||
const tankList = tankStats
|
||||
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
|
||||
.filter((tank) => tank.tier === tier)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
|
||||
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class)
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
awaitedGameDefinitions.nations.indexOf(a.nation) -
|
||||
awaitedGameDefinitions.nations.indexOf(b.nation),
|
||||
awaitedGameDefinitions.nations.indexOf(b.nation)
|
||||
)
|
||||
.map(
|
||||
(tank) =>
|
||||
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
|
||||
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`
|
||||
);
|
||||
const lines = [title, ...tankList];
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('permissions'),
|
||||
command: createLocalizedCommand("permissions"),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
@@ -14,33 +14,33 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
new Date().getDay() === 5 && new Date().getDate() === 13;
|
||||
const permissions = [
|
||||
[
|
||||
interaction.appPermissions?.has('ViewChannel'),
|
||||
interaction.appPermissions?.has("ViewChannel"),
|
||||
strings.bot.commands.permissions.body.view_channels,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('ReadMessageHistory'),
|
||||
interaction.appPermissions?.has("ReadMessageHistory"),
|
||||
strings.bot.commands.permissions.body.read_message_history,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('AttachFiles'),
|
||||
interaction.appPermissions?.has("AttachFiles"),
|
||||
strings.bot.commands.permissions.body.attach_files,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('UseExternalEmojis'),
|
||||
interaction.appPermissions?.has("UseExternalEmojis"),
|
||||
strings.bot.commands.permissions.body.external_emojis,
|
||||
],
|
||||
];
|
||||
const body = permissions
|
||||
.map(([hasPermission, description]) => {
|
||||
if (hasPermission) {
|
||||
return `${isFridayThe13th ? '😇' : '✅'} ${description}`;
|
||||
return `${isFridayThe13th ? "😇" : "✅"} ${description}`;
|
||||
} else {
|
||||
return `${isFridayThe13th ? '💀' : '❌'} ${description}`;
|
||||
return `${isFridayThe13th ? "💀" : "❌"} ${description}`;
|
||||
}
|
||||
})
|
||||
.join('\n');
|
||||
.join("\n");
|
||||
|
||||
return literals(strings.bot.commands.permissions.body.title, [body]);
|
||||
return literals(strings.bot.commands.permissions.body.title, { body });
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,15 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
handlesInteraction: true,
|
||||
|
||||
command: createLocalizedCommand('ping', [
|
||||
{ subcommand: 'blitzkit' },
|
||||
{ subcommand: 'wotb' },
|
||||
command: createLocalizedCommand("ping", [
|
||||
{ subcommand: "blitzkit" },
|
||||
{ subcommand: "wotb" },
|
||||
]),
|
||||
|
||||
async handler(interaction) {
|
||||
@@ -17,17 +17,17 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const executionStart = Date.now();
|
||||
|
||||
if (subcommand === 'blitzkit') {
|
||||
if (subcommand === "blitzkit") {
|
||||
await interaction.editReply(strings.bot.commands.ping.body.pong);
|
||||
} else {
|
||||
await fetch('https://api.wotblitz.com/');
|
||||
await fetch("https://api.wotblitz.com/");
|
||||
}
|
||||
|
||||
const executionTime = Date.now() - executionStart;
|
||||
interaction.editReply(
|
||||
literals(strings.bot.commands.ping.body.pong_with_time, [
|
||||
`${Math.round(executionTime)}`,
|
||||
]),
|
||||
literals(strings.bot.commands.ping.body.pong_with_time, {
|
||||
time: executionTime.toFixed(0),
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
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';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { markdownTable } from '../core/discord/markdownTable';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
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";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { markdownTable } from "../core/discord/markdownTable";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command:
|
||||
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
|
||||
createLocalizedCommand("player-info").addStringOption(addUsernameChoices),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
@@ -22,9 +22,9 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.player_info.body.title, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]),
|
||||
literals(strings.bot.commands.player_info.body.title, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
}),
|
||||
|
||||
markdownTable([
|
||||
[
|
||||
@@ -51,16 +51,16 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
[
|
||||
strings.bot.commands.player_info.body.created,
|
||||
new Date(accountInfo.created_at * 1000).toLocaleDateString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
[
|
||||
strings.bot.commands.player_info.body.last_battle,
|
||||
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
]),
|
||||
])
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -8,42 +8,42 @@ import {
|
||||
getRatingInfo,
|
||||
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';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import { DeltaCaret } from '../components/DeltaCaret';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
||||
import { iconPng } from '../core/blitzkit/iconPng';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
} 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";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { DeltaCaret } from "../components/DeltaCaret";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { normalizeLeagueIcon } from "../core/blitz/normalizeLeagueIcon";
|
||||
import { iconPng } from "../core/blitzkit/iconPng";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
const LEAGUE_ACCENT = [
|
||||
'_purple',
|
||||
'_sand',
|
||||
'_amber',
|
||||
'_mauve',
|
||||
'_brown',
|
||||
"_purple",
|
||||
"_sand",
|
||||
"_amber",
|
||||
"_mauve",
|
||||
"_brown",
|
||||
] as const;
|
||||
|
||||
export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('rating', [
|
||||
command: createLocalizedCommand("rating", [
|
||||
{
|
||||
subcommand: 'today',
|
||||
subcommand: "today",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
},
|
||||
},
|
||||
{
|
||||
subcommand: 'season',
|
||||
subcommand: "season",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
},
|
||||
@@ -52,11 +52,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
async handler(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand(true) as
|
||||
| 'season'
|
||||
| 'today';
|
||||
| "season"
|
||||
| "today";
|
||||
const { strings } = translator(interaction.locale);
|
||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
|
||||
const ratingInfo = await getRatingInfo(region);
|
||||
@@ -67,20 +67,20 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const leaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
ratingInfo.current_season - (subcommand === 'today' ? 0 : 1),
|
||||
ratingInfo.current_season - (subcommand === "today" ? 0 : 1)
|
||||
);
|
||||
|
||||
if (leaderboard.version!.$case === 'v1') {
|
||||
if (leaderboard.version!.$case === "v1") {
|
||||
throw new Error(
|
||||
'Encountered bkrl minimal format in latest season. Wait till next cron?',
|
||||
"Encountered bkrl minimal format in latest season. Wait till next cron?"
|
||||
);
|
||||
}
|
||||
|
||||
const accountInfo = await getAccountInfo(region, id, [
|
||||
'statistics.rating',
|
||||
"statistics.rating",
|
||||
]);
|
||||
const statsIndex = leaderboard.version!.value.entries.findIndex(
|
||||
(entry) => entry.id === id,
|
||||
(entry) => entry.id === id
|
||||
);
|
||||
const statsA = leaderboard
|
||||
? leaderboard.version!.value.entries[statsIndex!]
|
||||
@@ -90,9 +90,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
|
||||
|
||||
if (statsB2Array === undefined) {
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]);
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
});
|
||||
}
|
||||
|
||||
const [statsB2] = statsB2Array;
|
||||
@@ -102,15 +102,15 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
return strings.bot.commands.rating.errors.unrecorded_stats;
|
||||
}
|
||||
if (!statsB1) {
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]);
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
});
|
||||
}
|
||||
|
||||
const reward = ratingInfo.rewards.find(
|
||||
(reward) =>
|
||||
statsB2.number >= reward.from_position &&
|
||||
statsB2.number <= reward.to_position,
|
||||
statsB2.number <= reward.to_position
|
||||
);
|
||||
const positionDelta = leaderboard
|
||||
? statsB2.number - positionA!
|
||||
@@ -118,11 +118,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const delta = deltaBkrlBlitzStats(
|
||||
statsA,
|
||||
accountInfo.statistics.rating!,
|
||||
statsB2,
|
||||
statsB2
|
||||
);
|
||||
const leagueTheme = LEAGUE_ACCENT[league.index];
|
||||
let rewardImage = reward
|
||||
? reward.type === 'vehicle'
|
||||
? reward.type === "vehicle"
|
||||
? reward.vehicle.preview_image_url
|
||||
: reward.stuff.image_url
|
||||
: undefined;
|
||||
@@ -131,7 +131,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { ok } = await fetch(rewardImage);
|
||||
|
||||
if (!ok) {
|
||||
rewardImage = imgur('kP88vqr');
|
||||
rewardImage = imgur("kP88vqr");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,24 +147,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
position: "relative",
|
||||
padding: 16,
|
||||
backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
|
||||
borderRadius: 4,
|
||||
border: `1px solid ${
|
||||
theme.colors[`componentInteractive${leagueTheme}`]
|
||||
}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{reward === undefined && (
|
||||
@@ -172,17 +172,17 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
endOpacity={3 / 4}
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
transform: 'translateX(-16px) rotate(90deg)',
|
||||
transform: "translateX(-16px) rotate(90deg)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -190,24 +190,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
objectFit: 'contain',
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={normalizeLeagueIcon(
|
||||
ratingInfo.leagues[league.index].big_icon,
|
||||
ratingInfo.leagues[league.index].big_icon
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -232,7 +232,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
{Math.abs(delta.score).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
@@ -241,9 +241,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -267,7 +267,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
{Math.abs(positionDelta).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
@@ -279,23 +279,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
{reward && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -303,13 +303,13 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors[`textHighContrast${leagueTheme}`],
|
||||
fontSize: 16,
|
||||
maxWidth: 136,
|
||||
textAlign: 'right',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
textAlign: "right",
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{reward.type === 'vehicle'
|
||||
{reward.type === "vehicle"
|
||||
? reward.vehicle.user_string
|
||||
: reward.stuff.title}
|
||||
</span>
|
||||
@@ -317,9 +317,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -329,7 +329,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
Reward
|
||||
{reward.type === 'stuff' ? ` x${reward.count}` : ''}
|
||||
{reward.type === "stuff" ? ` x${reward.count}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -338,7 +338,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
objectFit: 'cover',
|
||||
objectFit: "cover",
|
||||
}}
|
||||
src={await iconPng(rewardImage!)}
|
||||
/>
|
||||
@@ -350,7 +350,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
endOpacity={3 / 4}
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
transform: 'translateX(16px) rotate(-90deg)',
|
||||
transform: "translateX(16px) rotate(-90deg)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -362,10 +362,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
direction="reverse"
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
top: -40,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -373,32 +373,32 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<Glow
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
bottom: -40,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{delta.battles > 0 && (
|
||||
<div style={{ padding: '8px 0', display: 'flex' }}>
|
||||
<div style={{ padding: "8px 0", display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{delta.battles.toLocaleString(interaction.locale)}
|
||||
@@ -414,19 +414,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(100 * (delta.wins / delta.battles)).toFixed(0)}%
|
||||
@@ -442,23 +442,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{Math.round(delta.damage / delta.battles).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
@@ -472,19 +472,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(100 * (delta.survived / delta.battles)).toFixed(0)}%
|
||||
@@ -500,19 +500,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(delta.kills / delta.battles).toFixed(2)}
|
||||
|
||||
@@ -13,27 +13,27 @@ import {
|
||||
RatingInfo,
|
||||
RatingLeaderboard,
|
||||
Region,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandSubcommandBuilder,
|
||||
} from 'discord.js';
|
||||
import { range } from 'lodash-es';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import * as Leaderboard from '../components/Leaderboard';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { UserError } from '../core/blitzkit/userError';
|
||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "discord.js";
|
||||
import { range } from "lodash-es";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import * as Leaderboard from "../components/Leaderboard";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { UserError } from "../core/blitzkit/userError";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
interface SimplifiedPlayer {
|
||||
id: number;
|
||||
@@ -51,7 +51,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
const onGoingSeason = await isOnGoingRatingSeason();
|
||||
const seasonNumbers = range(
|
||||
FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
|
||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
|
||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1
|
||||
);
|
||||
|
||||
function addOptions(option: SlashCommandSubcommandBuilder) {
|
||||
@@ -66,51 +66,50 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.options.limit
|
||||
.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ count: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.limit
|
||||
.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ count: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setRequired(false)
|
||||
.setMinValue(5)
|
||||
.setMaxValue(30),
|
||||
.setMaxValue(30)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName(
|
||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
||||
strings.bot.commands.rating_leaderboard.options.season.name
|
||||
)
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.description,
|
||||
strings.bot.commands.rating_leaderboard.options.season.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.description,
|
||||
),
|
||||
.description
|
||||
)
|
||||
)
|
||||
.setRequired(false)
|
||||
.addChoices(
|
||||
@@ -124,7 +123,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? literals(
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.choices.current,
|
||||
[`${number}`],
|
||||
{ season: number }
|
||||
)
|
||||
: `${number}`,
|
||||
value: `${number}`,
|
||||
@@ -133,27 +132,27 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options
|
||||
.season.choices.current,
|
||||
[`${number}`],
|
||||
{ season: number }
|
||||
)
|
||||
: undefined,
|
||||
} satisfies APIApplicationCommandOptionChoice<string>;
|
||||
})
|
||||
.reverse(),
|
||||
),
|
||||
.reverse()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('rating-leaderboard', [
|
||||
command: createLocalizedCommand("rating-leaderboard", [
|
||||
{
|
||||
subcommand: 'neighbors',
|
||||
subcommand: "neighbors",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
addOptions(option);
|
||||
},
|
||||
},
|
||||
{
|
||||
subcommand: 'league',
|
||||
subcommand: "league",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option
|
||||
.addStringOption((option) => {
|
||||
@@ -161,7 +160,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
|
||||
return option
|
||||
.setName(
|
||||
strings.bot.commands.rating_leaderboard.options.league.name,
|
||||
strings.bot.commands.rating_leaderboard.options.league.name
|
||||
)
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
@@ -169,19 +168,19 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.description,
|
||||
.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.description,
|
||||
),
|
||||
.description
|
||||
)
|
||||
)
|
||||
.addChoices(
|
||||
...LEAGUES.map(
|
||||
@@ -195,10 +194,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
(
|
||||
strings.common.leagues as Record<string, string>
|
||||
)[name],
|
||||
)[name]
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice<string>
|
||||
)
|
||||
)
|
||||
.setRequired(true);
|
||||
})
|
||||
@@ -213,10 +212,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const subcommand = interaction.options.getSubcommand(true) as
|
||||
| 'league'
|
||||
| 'neighbors';
|
||||
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
||||
const seasonOption = interaction.options.getString('season');
|
||||
| "league"
|
||||
| "neighbors";
|
||||
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||
const seasonOption = interaction.options.getString("season");
|
||||
const season = seasonOption
|
||||
? parseInt(seasonOption)
|
||||
: onGoingSeason
|
||||
@@ -232,11 +231,11 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
let playerId: number | undefined;
|
||||
let regionRatingInfo: RatingInfo | null;
|
||||
|
||||
if (subcommand === 'league') {
|
||||
if (subcommand === "league") {
|
||||
const leagueIndex = parseInt(
|
||||
interaction.options.getString('league')!,
|
||||
interaction.options.getString("league")!
|
||||
);
|
||||
const region = interaction.options.getString('region') as Region;
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
regionRatingInfo = await (isCurrentSeason
|
||||
? getRatingInfo(region)
|
||||
: null);
|
||||
@@ -259,8 +258,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
position: player.number,
|
||||
clan: player.clan_tag,
|
||||
nickname: player.nickname,
|
||||
}) satisfies SimplifiedPlayer,
|
||||
),
|
||||
}) satisfies SimplifiedPlayer
|
||||
)
|
||||
)
|
||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||
async (players) => {
|
||||
@@ -269,17 +268,17 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? 0
|
||||
: players.version!.value.entries.findIndex(
|
||||
(player) =>
|
||||
player.score < LEAGUES[leagueIndex - 1].minScore,
|
||||
player.score < LEAGUES[leagueIndex - 1].minScore
|
||||
);
|
||||
const lastPlayerIndex = firstPlayerIndex + limit - 1;
|
||||
const trimmed = players.version!.value.entries.slice(
|
||||
firstPlayerIndex,
|
||||
lastPlayerIndex + 1,
|
||||
lastPlayerIndex + 1
|
||||
);
|
||||
const clanData = await getClanAccountInfo(
|
||||
region,
|
||||
trimmed.map(({ id }) => id),
|
||||
['clan'],
|
||||
["clan"]
|
||||
);
|
||||
|
||||
return trimmed.map(
|
||||
@@ -294,16 +293,16 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.body
|
||||
.deleted_player,
|
||||
[`${player.id}`],
|
||||
{ id: player.id }
|
||||
),
|
||||
}) satisfies SimplifiedPlayer,
|
||||
}) satisfies SimplifiedPlayer
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
const leagueInfo = LEAGUES[leagueIndex];
|
||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
season,
|
||||
season
|
||||
);
|
||||
|
||||
players = result;
|
||||
@@ -316,12 +315,12 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? (strings.common.leagues as Record<string, string>)[
|
||||
LEAGUES[leagueInfo.index].name
|
||||
]
|
||||
: ''
|
||||
: ""
|
||||
} - ${strings.common.regions.short[region]}`;
|
||||
titleImage = imgur(leagueInfo.icon);
|
||||
titleDescription = literals(
|
||||
strings.bot.commands.rating_leaderboard.body.top_players,
|
||||
[`${limit}`],
|
||||
{ count: limit }
|
||||
);
|
||||
} else {
|
||||
const { region, id } = await resolvePlayerFromCommand(interaction);
|
||||
@@ -338,7 +337,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
}
|
||||
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const neighbors = isCurrentSeason
|
||||
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
|
||||
(players) =>
|
||||
@@ -350,13 +349,13 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
score: player.score,
|
||||
clan: player.clan_tag,
|
||||
nickname: player.nickname,
|
||||
}) satisfies SimplifiedPlayer,
|
||||
),
|
||||
}) satisfies SimplifiedPlayer
|
||||
)
|
||||
)
|
||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||
async (players) => {
|
||||
const playerIndex = players.version!.value.entries.findIndex(
|
||||
(player) => player.id === id,
|
||||
(player) => player.id === id
|
||||
);
|
||||
|
||||
if (playerIndex === -1) {
|
||||
@@ -364,20 +363,20 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.body
|
||||
.no_participation,
|
||||
[markdownEscape(accountInfo.nickname), `${season}`],
|
||||
),
|
||||
{ player: markdownEscape(accountInfo.nickname), season }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const halfRange = Math.round(limit / 2);
|
||||
const trimmed = players.version!.value.entries.slice(
|
||||
playerIndex - halfRange,
|
||||
playerIndex + halfRange + 1,
|
||||
playerIndex + halfRange + 1
|
||||
);
|
||||
const clanData = await getClanAccountInfo(
|
||||
region,
|
||||
trimmed.map((player) => player.id),
|
||||
['clan'],
|
||||
["clan"]
|
||||
);
|
||||
|
||||
return trimmed.map((player, index) => ({
|
||||
@@ -389,18 +388,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? clanData[index]!.account_name
|
||||
: `Deleted ${player.id}`,
|
||||
}));
|
||||
},
|
||||
}
|
||||
);
|
||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
season,
|
||||
season
|
||||
);
|
||||
|
||||
players = neighbors;
|
||||
titleImage = clan
|
||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||
: undefined;
|
||||
titleName = neighbors ? accountInfo.nickname : '';
|
||||
titleName = neighbors ? accountInfo.nickname : "";
|
||||
titleDescription =
|
||||
strings.bot.commands.rating_leaderboard.body.neighbors;
|
||||
playersAfter =
|
||||
@@ -414,7 +413,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
const items = players.map((player) => {
|
||||
const midnightIndex =
|
||||
archivedLeaderboard?.version!.value.entries.findIndex(
|
||||
(item) => item.id === player.id,
|
||||
(item) => item.id === player.id
|
||||
);
|
||||
const midnightScore =
|
||||
midnightIndex === undefined
|
||||
@@ -453,7 +452,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
<Leaderboard.Gap
|
||||
message={literals(
|
||||
strings.bot.commands.rating_leaderboard.body.more,
|
||||
[`${playersAfter.toLocaleString(interaction.locale)}`],
|
||||
{ count: playersAfter.toLocaleString(interaction.locale) }
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -465,5 +464,5 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
|
||||
autocomplete: autocompleteUsername,
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -8,31 +8,31 @@ import {
|
||||
TANK_ICONS,
|
||||
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';
|
||||
import { resolveTankId } from '../core/blitz/resolveTankId';
|
||||
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
|
||||
import { iconPng } from '../core/blitzkit/iconPng';
|
||||
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
|
||||
import { resolveAncestry } from '../core/blitzkit/resolveAncestry';
|
||||
import { tankIconPng } from '../core/blitzkit/tankIconPng';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { escapeMarkdown, Locale } from "discord.js";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { resolveTankId } from "../core/blitz/resolveTankId";
|
||||
import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
|
||||
import { iconPng } from "../core/blitzkit/iconPng";
|
||||
import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
|
||||
import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
|
||||
import { tankIconPng } from "../core/blitzkit/tankIconPng";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('research')
|
||||
command: createLocalizedCommand("research")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName(strings.bot.commands.research.options.target_tank.name)
|
||||
@@ -41,20 +41,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.target_tank.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.research.options.target_tank.description,
|
||||
strings.bot.commands.research.options.target_tank.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.target_tank.description,
|
||||
),
|
||||
strings.bot.commands.research.options.target_tank.description
|
||||
)
|
||||
)
|
||||
.setAutocomplete(true)
|
||||
.setRequired(true),
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
@@ -64,20 +64,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.starting_tank.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.research.options.starting_tank.description,
|
||||
strings.bot.commands.research.options.starting_tank.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.starting_tank.description,
|
||||
),
|
||||
strings.bot.commands.research.options.starting_tank.description
|
||||
)
|
||||
)
|
||||
.setAutocomplete(true)
|
||||
.setRequired(false),
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(addUsernameChoices),
|
||||
|
||||
@@ -85,11 +85,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings, unwrap } = translator(interaction.locale);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const targetTankId = await resolveTankId(
|
||||
interaction.options.getString('target-tank', true),
|
||||
interaction.options.getString("target-tank", true),
|
||||
interaction.locale,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const startingTankRaw = interaction.options.getString('starting-tank');
|
||||
const startingTankRaw = interaction.options.getString("starting-tank");
|
||||
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
|
||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||
let startingTankId: number;
|
||||
@@ -99,46 +99,45 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
startingTankId = await resolveTankId(
|
||||
startingTankRaw,
|
||||
interaction.locale,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
if (targetTankId === startingTankId) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.start_end_equal,
|
||||
[escapeMarkdown(unwrap(targetTank.name))],
|
||||
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||
);
|
||||
}
|
||||
|
||||
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
|
||||
|
||||
if (startingTank.tier > targetTank.tier) {
|
||||
return literals(strings.bot.commands.research.errors.unordered, [
|
||||
escapeMarkdown(unwrap(startingTank.name)),
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.unordered, {
|
||||
tank_1: escapeMarkdown(unwrap(startingTank.name)),
|
||||
tank_2: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
if (!targetTankAncestry.includes(startingTankId)) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.tanks_not_on_line,
|
||||
[
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
escapeMarkdown(unwrap(startingTank.name)),
|
||||
],
|
||||
{
|
||||
tank_1: escapeMarkdown(unwrap(targetTank.name)),
|
||||
tank_2: escapeMarkdown(unwrap(startingTank.name)),
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (targetTank.type !== TankType.RESEARCHABLE) {
|
||||
return literals(strings.bot.commands.research.errors.non_tech_tree, [
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.non_tech_tree, {
|
||||
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
if (targetTank.tier === 1) {
|
||||
return literals(strings.bot.commands.research.errors.tier_1, [
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.tier_1, {
|
||||
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
const tankStats = await getTankStats(region, id);
|
||||
@@ -150,18 +149,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.already_researched,
|
||||
[escapeMarkdown(unwrap(targetTank.name))],
|
||||
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||
);
|
||||
}
|
||||
|
||||
const foundAncestor = targetTankAncestry.find(
|
||||
(id) =>
|
||||
awaitedTankDefinitions.tanks[id].tier === 1 ||
|
||||
tankStats.some(({ tank_id }) => tank_id === id),
|
||||
tankStats.some(({ tank_id }) => tank_id === id)
|
||||
);
|
||||
|
||||
if (foundAncestor === undefined) {
|
||||
throw new Error('No first owned tech tree found.');
|
||||
throw new Error("No first owned tech tree found.");
|
||||
}
|
||||
|
||||
startingTankId = foundAncestor;
|
||||
@@ -169,12 +168,12 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const techTreeLine = await buildTechTreeLine(
|
||||
startingTankId,
|
||||
targetTankId,
|
||||
targetTankId
|
||||
);
|
||||
|
||||
const line = [...techTreeLine, startingTankId].reverse();
|
||||
const { nickname } = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
const costs = await Promise.all(
|
||||
line.map(async (id) => {
|
||||
@@ -187,25 +186,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.turrets.forEach((turret) => {
|
||||
if (
|
||||
turret.research_cost !== undefined &&
|
||||
typeof turret.research_cost.research_cost_type!.value === 'number'
|
||||
typeof turret.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
turretXps.set(
|
||||
turret.id,
|
||||
turret.research_cost.research_cost_type!.value,
|
||||
turret.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
|
||||
turret.guns.forEach((gun) => {
|
||||
if (
|
||||
gun.gun_type!.value.base.research_cost !== undefined &&
|
||||
typeof gun.gun_type!.value.base.research_cost
|
||||
.research_cost_type!.value === 'number'
|
||||
gun.research_cost !== undefined &&
|
||||
typeof gun.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
gunXps.set(
|
||||
gun.gun_type!.value.base.id,
|
||||
gun.gun_type!.value.base.research_cost.research_cost_type!
|
||||
.value,
|
||||
);
|
||||
gunXps.set(gun.id, gun.research_cost.research_cost_type!.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -213,11 +207,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.engines.forEach((engine) => {
|
||||
if (
|
||||
engine.research_cost !== undefined &&
|
||||
typeof engine.research_cost.research_cost_type!.value === 'number'
|
||||
typeof engine.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
engineXps.set(
|
||||
engine.id,
|
||||
engine.research_cost.research_cost_type!.value,
|
||||
engine.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -225,11 +219,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.tracks.forEach((track) => {
|
||||
if (
|
||||
track.research_cost !== undefined &&
|
||||
typeof track.research_cost.research_cost_type!.value === 'number'
|
||||
typeof track.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
trackXps.set(
|
||||
track.id,
|
||||
track.research_cost.research_cost_type!.value,
|
||||
track.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -254,10 +248,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
purchase: tank.price.value,
|
||||
equipment: equipmentPriceMatrix[tank.tier].reduce(
|
||||
(a, b) => a + 3 * b,
|
||||
0,
|
||||
0
|
||||
),
|
||||
};
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const image = (
|
||||
@@ -272,14 +266,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
borderRadius: 4,
|
||||
backgroundColor: theme.colors.appBackground2,
|
||||
border: theme.borderStyles.subtle,
|
||||
@@ -290,10 +284,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
right: 24,
|
||||
}}
|
||||
>
|
||||
@@ -309,23 +303,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
padding: '16px 0',
|
||||
padding: "16px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="XP icon"
|
||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -335,7 +329,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Experience:{' '}
|
||||
Experience:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -344,14 +338,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -361,7 +355,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Purchase:{' '}
|
||||
Purchase:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + b.purchase, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -370,14 +364,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -387,7 +381,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Equipment:{' '}
|
||||
Equipment:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + b.equipment, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -404,7 +398,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
key={id}
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
borderRadius: 4,
|
||||
backgroundColor: theme.colors.appBackground2,
|
||||
border: theme.borderStyles.subtle,
|
||||
@@ -413,9 +407,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -431,9 +425,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
right: '5%',
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
right: "5%",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -442,23 +436,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
height: 100,
|
||||
width: 100,
|
||||
objectFit: 'contain',
|
||||
position: 'absolute',
|
||||
left: '25%',
|
||||
objectFit: "contain",
|
||||
position: "absolute",
|
||||
left: "25%",
|
||||
top: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "absolute",
|
||||
bottom: 8,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -481,23 +475,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
padding: '16px 0',
|
||||
padding: "16px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="XP icon"
|
||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -509,11 +503,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.research,
|
||||
[
|
||||
`${costs[index].research?.toLocaleString(
|
||||
interaction.locale,
|
||||
)}`,
|
||||
],
|
||||
{
|
||||
cost: costs[index].research?.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -521,15 +515,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
{costs[index].upgrades > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Free XP icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/free-xp.webp'),
|
||||
asset("icons/currencies/free-xp.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -542,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.upgrades,
|
||||
[costs[index].upgrades.toLocaleString()],
|
||||
{ cost: costs[index].upgrades.toLocaleString() }
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -550,15 +544,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/silver.webp'),
|
||||
asset("icons/currencies/silver.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -571,26 +565,26 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.purchase,
|
||||
[
|
||||
costs[index].purchase.toLocaleString(
|
||||
interaction.locale,
|
||||
{
|
||||
cost: costs[index].purchase.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/silver.webp'),
|
||||
asset("icons/currencies/silver.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -603,18 +597,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.equipment,
|
||||
[
|
||||
costs[index].equipment.toLocaleString(
|
||||
interaction.locale,
|
||||
{
|
||||
cost: costs[index].equipment.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</CommandWrapper>
|
||||
@@ -626,7 +620,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
},
|
||||
|
||||
autocomplete(interaction) {
|
||||
autocompleteTanks(interaction, true, ['target-tank', 'starting-tank']);
|
||||
autocompleteTanks(interaction, true, ["target-tank", "starting-tank"]);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,23 @@
|
||||
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';
|
||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
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";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_LIMIT = 25;
|
||||
|
||||
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('search-clans')
|
||||
command: createLocalizedCommand("search-clans")
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) =>
|
||||
addClanChoices(option).setAutocomplete(false),
|
||||
addClanChoices(option).setAutocomplete(false)
|
||||
)
|
||||
.addIntegerOption((option) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -28,21 +28,21 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands.search_clans.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.search_clans.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.search_clans.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(100);
|
||||
@@ -50,24 +50,24 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
async handler(interaction) {
|
||||
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;
|
||||
const clanList = await fetchBlitz<ClanList>(region, 'clans/list', {
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
const clan = interaction.options.getString("clan")!;
|
||||
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||
const clanList = await fetchBlitz<ClanList>(region, "clans/list", {
|
||||
search: clan,
|
||||
limit,
|
||||
});
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.search_clans.body.title, [
|
||||
markdownEscape(clan),
|
||||
strings.common.regions.normal[region],
|
||||
]),
|
||||
literals(strings.bot.commands.search_clans.body.title, {
|
||||
search: markdownEscape(clan),
|
||||
region: strings.common.regions.normal[region],
|
||||
}),
|
||||
clanList.length === 0
|
||||
? strings.bot.commands.search_clans.body.no_results
|
||||
: `\`\`\`\n${clanList
|
||||
.map((clan) => `${clan.name} [${clan.tag}]`)
|
||||
.join('\n')}\n\`\`\``,
|
||||
.join("\n")}\n\`\`\``
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,17 +3,17 @@ import {
|
||||
fetchBlitz,
|
||||
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';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_LIMIT = 25;
|
||||
|
||||
@@ -21,10 +21,10 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('search-players')
|
||||
command: createLocalizedCommand("search-players")
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) =>
|
||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true)
|
||||
)
|
||||
.addIntegerOption((option) =>
|
||||
option
|
||||
@@ -34,49 +34,49 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.search_players.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.search_players.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.search_players.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(100),
|
||||
.setMaxValue(100)
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
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;
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
const name = interaction.options.getString("username")!;
|
||||
const limit = interaction.options.getInteger("limit") ?? 25;
|
||||
const trimmedSearch = name.trim();
|
||||
const players = usernamePattern.test(trimmedSearch)
|
||||
? await fetchBlitz<AccountList>(region, 'account/list', {
|
||||
? await fetchBlitz<AccountList>(region, "account/list", {
|
||||
search: trimmedSearch,
|
||||
limit,
|
||||
})
|
||||
: [];
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.search_players.body.title, [
|
||||
markdownEscape(trimmedSearch),
|
||||
strings.common.regions.normal[region],
|
||||
]),
|
||||
literals(strings.bot.commands.search_players.body.title, {
|
||||
search: markdownEscape(trimmedSearch),
|
||||
region: strings.common.regions.normal[region],
|
||||
}),
|
||||
`\`\`\`${
|
||||
players.length === 0
|
||||
? strings.bot.commands.search_players.body.no_results
|
||||
: players.map((player) => player.nickname).join('\n')
|
||||
}\`\`\``,
|
||||
: players.map((player) => player.nickname).join("\n")
|
||||
}\`\`\``
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
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';
|
||||
import { tankDefinitions, tankNames } from '../blitzkit/nonBlockingPromises';
|
||||
import { UserError } from '../blitzkit/userError';
|
||||
import { tankNamesTechTreeOnly } from '../discord/autocompleteTanks';
|
||||
import { translator } from '../localization/translator';
|
||||
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";
|
||||
import { tankDefinitions, tankNames } from "../blitzkit/nonBlockingPromises";
|
||||
import { UserError } from "../blitzkit/userError";
|
||||
import { tankNamesTechTreeOnly } from "../discord/autocompleteTanks";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export async function resolveTankId(
|
||||
tank: string | number,
|
||||
locale: Locale,
|
||||
techTreeOnly = false,
|
||||
techTreeOnly = false
|
||||
) {
|
||||
const { strings } = translator(locale);
|
||||
const number = typeof tank === 'string' ? Number(tank) : tank;
|
||||
const number = typeof tank === "string" ? Number(tank) : tank;
|
||||
|
||||
if (Number.isNaN(number)) {
|
||||
const searchResult = go(
|
||||
`${tank}`,
|
||||
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
||||
{ keys: SEARCH_KEYS, limit: 1 },
|
||||
{ keys: SEARCH_KEYS, limit: 1 }
|
||||
);
|
||||
|
||||
if (searchResult.length === 0) {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.tank_name_not_found, [
|
||||
markdownEscape(`${tank}`),
|
||||
]),
|
||||
literals(strings.bot.common.errors.tank_name_not_found, {
|
||||
search: markdownEscape(`${tank}`),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return searchResult[0].obj.id;
|
||||
@@ -39,7 +39,9 @@ export async function resolveTankId(
|
||||
return number;
|
||||
} else {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
|
||||
literals(strings.bot.common.errors.tank_id_not_found, {
|
||||
search: number,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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';
|
||||
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";
|
||||
|
||||
export async function filtersToDescription(
|
||||
{ nation, tier, type, class: tankClass, tank }: StatFilters,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const { strings, bkLocale } = translator(locale);
|
||||
const awaitedTankDefinitions = await fetchTankDefinitions();
|
||||
@@ -18,11 +18,13 @@ export async function filtersToDescription(
|
||||
info.push((strings.common.nations as Record<string, string>)[nation]);
|
||||
if (tier) {
|
||||
info.push(
|
||||
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
|
||||
literals(strings.bot.common.filters.tier, {
|
||||
tier: TIER_ROMAN_NUMERALS[tier],
|
||||
})
|
||||
);
|
||||
}
|
||||
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
|
||||
if (type) info.push(strings.common.tree_type[type]);
|
||||
|
||||
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
|
||||
return info.length === 0 ? strings.bot.common.filters.none : info.join(", ");
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { jsxToSvg } from './jsxToSvg';
|
||||
import { svgToPng } from './svgToPng';
|
||||
import { jsxToSvg } from "./jsxToSvg";
|
||||
import { svgToPng } from "./svgToPng";
|
||||
|
||||
export async function jsxToPng(jsx: JSX.Element) {
|
||||
export async function jsxToPng(jsx: React.JSX.Element) {
|
||||
const svg = await jsxToSvg(jsx);
|
||||
const png = svgToPng(svg);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { jsxToSvg } from './jsxToSvg';
|
||||
import { RenderConfiguration } from './renderConfiguration';
|
||||
import { svgToPngThreaded } from './svgToPngThreaded';
|
||||
import { jsxToSvg } from "./jsxToSvg";
|
||||
import { RenderConfiguration } from "./renderConfiguration";
|
||||
import { svgToPngThreaded } from "./svgToPngThreaded";
|
||||
|
||||
export async function jsxToPngThreaded(
|
||||
jsx: JSX.Element,
|
||||
renderConfiguration: RenderConfiguration,
|
||||
jsx: React.JSX.Element,
|
||||
renderConfiguration: RenderConfiguration
|
||||
) {
|
||||
const svg = await jsxToSvg(jsx, renderConfiguration);
|
||||
const png = await svgToPngThreaded(svg);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { readFile } from 'fs/promises';
|
||||
import satori from 'satori';
|
||||
import { RenderConfiguration } from '../renderConfiguration';
|
||||
import robotoBlack from './Roboto-Black.ttf';
|
||||
import robotoBold from './Roboto-Bold.ttf';
|
||||
import roboto from './Roboto.ttf';
|
||||
import { readFile } from "fs/promises";
|
||||
import satori from "satori";
|
||||
import { RenderConfiguration } from "../renderConfiguration";
|
||||
import robotoBlack from "./Roboto-Black.ttf";
|
||||
import robotoBold from "./Roboto-Bold.ttf";
|
||||
import roboto from "./Roboto.ttf";
|
||||
|
||||
const fontsPromise = [
|
||||
{ file: roboto, weight: 400 as const },
|
||||
@@ -11,12 +11,12 @@ const fontsPromise = [
|
||||
{ file: robotoBlack, weight: 900 as const },
|
||||
].map(async ({ file, weight }) => {
|
||||
const data = await readFile(`${__dirname}/${file}`);
|
||||
return { name: 'Roboto', weight, data };
|
||||
return { name: "Roboto", weight, data };
|
||||
});
|
||||
|
||||
export async function jsxToSvg(
|
||||
jsx: JSX.Element,
|
||||
renderConfiguration = new RenderConfiguration(),
|
||||
jsx: React.JSX.Element,
|
||||
renderConfiguration = new RenderConfiguration()
|
||||
) {
|
||||
const fonts = await Promise.all(fontsPromise);
|
||||
return await satori(jsx, { width: renderConfiguration.width, fonts });
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { numberToRomanNumeral } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { numberToRomanNumeral } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandStringOption,
|
||||
} from 'discord.js';
|
||||
import { range } from 'lodash-es';
|
||||
import { translator } from '../localization/translator';
|
||||
import { localizationObject } from './localizationObject';
|
||||
} from "discord.js";
|
||||
import { range } from "lodash-es";
|
||||
import { translator } from "../localization/translator";
|
||||
import { localizationObject } from "./localizationObject";
|
||||
|
||||
export function addTierChoices(option: SlashCommandStringOption) {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -18,30 +18,30 @@ export function addTierChoices(option: SlashCommandStringOption) {
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(strings.bot.common.options.tier.description)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.description,
|
||||
),
|
||||
(strings) => strings.bot.common.options.tier.description
|
||||
)
|
||||
)
|
||||
.setChoices(
|
||||
...range(10, 0).map(
|
||||
(tier) =>
|
||||
({
|
||||
name: literals(strings.bot.common.options.tier.choices.tier, [
|
||||
`${tier}`,
|
||||
numberToRomanNumeral(tier),
|
||||
]),
|
||||
name: literals(strings.bot.common.options.tier.choices.tier, {
|
||||
tier_1: tier,
|
||||
tier_2: numberToRomanNumeral(tier),
|
||||
}),
|
||||
value: `${tier}`,
|
||||
name_localizations: localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.choices.tier,
|
||||
[`${tier}`, numberToRomanNumeral(tier)],
|
||||
{ tier_1: tier, tier_2: numberToRomanNumeral(tier) }
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice,
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice
|
||||
)
|
||||
)
|
||||
.setRequired(true);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../localization/translator';
|
||||
import { PeriodSize } from './addPeriodSubCommands';
|
||||
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 { 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]);
|
||||
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, { days: period });
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
|
||||
import { translator } from '../localization/translator';
|
||||
import { BlitzKitStrings, literals as literalsFunc } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { SUPPORTED_LOCALES_DISCORD } from "../localization/strings/constants";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
const validNameRegex =
|
||||
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
|
||||
|
||||
export function localizationObject(
|
||||
path: (strings: BlitzKitStrings) => string,
|
||||
literals: string[] = [],
|
||||
strict = false,
|
||||
literals?: Record<string, string | number>,
|
||||
strict = false
|
||||
) {
|
||||
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
|
||||
(localizations, locale) => {
|
||||
@@ -20,7 +20,7 @@ export function localizationObject(
|
||||
|
||||
if (!isValid) {
|
||||
console.warn(
|
||||
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
|
||||
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`
|
||||
);
|
||||
return localizations;
|
||||
}
|
||||
@@ -30,6 +30,6 @@ export function localizationObject(
|
||||
[locale]: translation,
|
||||
};
|
||||
},
|
||||
{},
|
||||
{}
|
||||
);
|
||||
}
|
||||
@@ -6,14 +6,14 @@ import {
|
||||
InteractionEditReplyOptions,
|
||||
InteractionReplyOptions,
|
||||
MessageEditOptions,
|
||||
} from 'discord.js';
|
||||
import { InteractionReturnable } from '../../events/interactionCreate';
|
||||
import { Writeable } from '../../types/writable';
|
||||
import { jsxToPngThreaded } from '../blitzkit/jsxToPngThreaded';
|
||||
import { RenderConfiguration } from '../blitzkit/renderConfiguration';
|
||||
} from "discord.js";
|
||||
import { InteractionReturnable } from "../../events/interactionCreate";
|
||||
import { Writeable } from "../../types/writable";
|
||||
import { jsxToPngThreaded } from "../blitzkit/jsxToPngThreaded";
|
||||
import { RenderConfiguration } from "../blitzkit/renderConfiguration";
|
||||
|
||||
export async function normalizeInteractionReturnable(
|
||||
returnable: InteractionReturnable,
|
||||
returnable: InteractionReturnable
|
||||
) {
|
||||
const images: [number, Buffer][] = [];
|
||||
const reply: InteractionEditReplyOptions &
|
||||
@@ -27,7 +27,7 @@ export async function normalizeInteractionReturnable(
|
||||
|
||||
await Promise.all(
|
||||
normalizedReturnable.map(async (item, index) => {
|
||||
if (typeof item === 'string') {
|
||||
if (typeof item === "string") {
|
||||
reply.content = item;
|
||||
} else if (item instanceof EmbedBuilder) {
|
||||
if (!reply.embeds) reply.embeds = [];
|
||||
@@ -36,7 +36,7 @@ export async function normalizeInteractionReturnable(
|
||||
if (!reply.components)
|
||||
reply.components = [new ActionRowBuilder<ButtonBuilder>()];
|
||||
(reply.components[0] as ActionRowBuilder<ButtonBuilder>).addComponents(
|
||||
item,
|
||||
item
|
||||
);
|
||||
} else if (item instanceof AttachmentBuilder) {
|
||||
if (!reply.files) reply.files = [];
|
||||
@@ -49,14 +49,14 @@ export async function normalizeInteractionReturnable(
|
||||
const image = await jsxToPngThreaded(item, renderConfiguration);
|
||||
images.push([index, image]);
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
if (images.length > 0) {
|
||||
images.sort(([a], [b]) => b - a);
|
||||
if (!reply.files) reply.files = [];
|
||||
images.forEach(([, image]) =>
|
||||
(reply.files as Writeable<typeof reply.files>)?.push(image),
|
||||
(reply.files as Writeable<typeof reply.files>)?.push(image)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
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';
|
||||
import { UserError } from '../blitzkit/userError';
|
||||
import { translator } from '../localization/translator';
|
||||
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";
|
||||
import { UserError } from "../blitzkit/userError";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
|
||||
export async function resolveClanFromCommand(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
interaction: ChatInputCommandInteraction
|
||||
) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const clan = interaction.options.getString('clan', true);
|
||||
const clan = interaction.options.getString("clan", true);
|
||||
|
||||
if (serverAndIdPattern.test(clan)) {
|
||||
const [server, accountId] = clan.split('/');
|
||||
const [server, accountId] = clan.split("/");
|
||||
return { region: server as Region, id: Number(accountId) };
|
||||
} else {
|
||||
const accounts = await searchClansAcrossRegionsBotWrapper(
|
||||
clan,
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
);
|
||||
|
||||
if (accounts[0]) {
|
||||
return { region: accounts[0].region, id: accounts[0].clan_id };
|
||||
} else {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.clan_not_found, [
|
||||
markdownEscape(clan),
|
||||
]),
|
||||
literals(strings.bot.common.errors.clan_not_found, {
|
||||
search: markdownEscape(clan),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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';
|
||||
import { translator } from '../localization/translator';
|
||||
import { PeriodType } from './addPeriodSubCommands';
|
||||
import { getPeriodOptionName } from './getPeriodOptionName';
|
||||
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";
|
||||
import { translator } from "../localization/translator";
|
||||
import { PeriodType } from "./addPeriodSubCommands";
|
||||
import { getPeriodOptionName } from "./getPeriodOptionName";
|
||||
|
||||
export interface ResolvedPeriod {
|
||||
name: string;
|
||||
@@ -16,7 +16,7 @@ export interface ResolvedPeriod {
|
||||
export function resolvePeriodFromCommand(
|
||||
region: Region,
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
forcedPeriod?: PeriodType,
|
||||
forcedPeriod?: PeriodType
|
||||
) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
let name: string;
|
||||
@@ -26,16 +26,16 @@ export function resolvePeriodFromCommand(
|
||||
? forcedPeriod
|
||||
: (interaction.options.getSubcommand() as PeriodType);
|
||||
|
||||
if (periodSubcommand === 'custom') {
|
||||
const startOption = interaction.options.getInteger('start', true);
|
||||
const endOption = interaction.options.getInteger('end', true);
|
||||
if (periodSubcommand === "custom") {
|
||||
const startOption = interaction.options.getInteger("start", true);
|
||||
const endOption = interaction.options.getInteger("end", true);
|
||||
const startDaysAgoMin = Math.min(startOption, endOption);
|
||||
const endDaysAgoMax = Math.max(startOption, endOption);
|
||||
|
||||
name = literals(strings.bot.common.periods.custom, [
|
||||
`${startDaysAgoMin}`,
|
||||
`${endDaysAgoMax}`,
|
||||
]);
|
||||
name = literals(strings.bot.common.periods.custom, {
|
||||
day_1: startDaysAgoMin,
|
||||
day_2: endDaysAgoMax,
|
||||
});
|
||||
start = getTimeDaysAgo(region, endDaysAgoMax);
|
||||
end = getTimeDaysAgo(region, startDaysAgoMin);
|
||||
} else {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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';
|
||||
import { getBlitzFromDiscord } from '../../blitzkit/getBlitzFromDiscord';
|
||||
import { UserError } from '../../blitzkit/userError';
|
||||
import { translator } from '../../localization/translator';
|
||||
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";
|
||||
import { getBlitzFromDiscord } from "../../blitzkit/getBlitzFromDiscord";
|
||||
import { UserError } from "../../blitzkit/userError";
|
||||
import { translator } from "../../localization/translator";
|
||||
|
||||
export interface ResolvedPlayer {
|
||||
region: Region;
|
||||
@@ -13,10 +13,10 @@ export interface ResolvedPlayer {
|
||||
}
|
||||
|
||||
export async function resolvePlayerFromCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const commandUsername = interaction.options.getString('username');
|
||||
const commandUsername = interaction.options.getString("username");
|
||||
|
||||
if (commandUsername !== null) {
|
||||
const id = parseInt(commandUsername);
|
||||
@@ -27,7 +27,7 @@ export async function resolvePlayerFromCommand(
|
||||
} else {
|
||||
const accounts = await searchPlayersAcrossRegionsBotWrapper(
|
||||
commandUsername,
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
);
|
||||
|
||||
if (accounts[0]) {
|
||||
@@ -37,9 +37,9 @@ export async function resolvePlayerFromCommand(
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.player_not_found, [
|
||||
markdownEscape(commandUsername),
|
||||
]),
|
||||
literals(strings.bot.common.errors.player_not_found, {
|
||||
search: markdownEscape(commandUsername),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
import { Locale } from "discord.js";
|
||||
|
||||
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
||||
const SUPPORTED_LOCALES_DISCORD_MAP: Record<string, Locale> = {
|
||||
en: Locale.EnglishUS,
|
||||
es: Locale.SpanishES,
|
||||
pt: Locale.PortugueseBR,
|
||||
@@ -14,12 +14,12 @@ const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
||||
};
|
||||
|
||||
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
|
||||
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k]),
|
||||
) as Record<Locale, SupportedLocale>;
|
||||
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k])
|
||||
) as Record<Locale, string>;
|
||||
|
||||
export const SUPPORTED_LOCALES_DISCORD = Object.values(
|
||||
SUPPORTED_LOCALES_DISCORD_MAP,
|
||||
SUPPORTED_LOCALES_DISCORD_MAP
|
||||
);
|
||||
|
||||
export const DEFAULT_LOCALE_DISCORD =
|
||||
SUPPORTED_LOCALES_DISCORD_MAP[DEFAULT_LOCALE];
|
||||
SUPPORTED_LOCALES_DISCORD_MAP[locales.default];
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ReadStream } from '@blitzkit/core';
|
||||
import AdmZip from 'adm-zip';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import { Parser } from 'pickleparser';
|
||||
import protobuf from 'protobufjs';
|
||||
import { ReadStream } from "@blitzkit/core";
|
||||
import AdmZip from "adm-zip";
|
||||
import { readFile } from "fs/promises";
|
||||
import { Parser } from "pickleparser";
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
interface WotbReplayMetaJson {
|
||||
version: string;
|
||||
@@ -24,26 +24,24 @@ export class WotbReplayReadStream extends ReadStream {
|
||||
async wotbReplay() {
|
||||
const pickleParser = new Parser();
|
||||
const zip = new AdmZip(Buffer.from(this.buffer));
|
||||
const metaJsonBuffer = zip.getEntry('meta.json')?.getData();
|
||||
const battleResultsDat = zip.getEntry('battle_results.dat')?.getData();
|
||||
const metaJsonBuffer = zip.getEntry("meta.json")?.getData();
|
||||
const battleResultsDat = zip.getEntry("battle_results.dat")?.getData();
|
||||
|
||||
if (!metaJsonBuffer || !battleResultsDat)
|
||||
throw new SyntaxError('meta.json not found');
|
||||
throw new SyntaxError("meta.json not found");
|
||||
|
||||
const metaJson = JSON.parse(
|
||||
metaJsonBuffer.toString(),
|
||||
metaJsonBuffer.toString()
|
||||
) as WotbReplayMetaJson;
|
||||
const unpickledBattleResults = pickleParser.parse(battleResultsDat);
|
||||
const [, battleResultsString] = unpickledBattleResults as [number, string];
|
||||
const root = await protobuf.load(
|
||||
`${__dirname}/../../protos/BattleResultsClient.proto`,
|
||||
`${__dirname}/../../protos/BattleResultsClient.proto`
|
||||
);
|
||||
const BattleResults = root.lookupType(
|
||||
'BattleResultsGenerated.BattleResultsClient',
|
||||
"BattleResultsGenerated.BattleResultsClient"
|
||||
);
|
||||
|
||||
writeFile('test.dat', Buffer.from(battleResultsString));
|
||||
|
||||
const reader = protobuf.Reader.create(Buffer.from(battleResultsString));
|
||||
|
||||
// const decodedBattleResults = BattleResults.decodeDelimited(
|
||||
@@ -53,7 +51,7 @@ export class WotbReplayReadStream extends ReadStream {
|
||||
}
|
||||
}
|
||||
|
||||
const replay = await readFile('temp/test3.wotbreplay');
|
||||
const replay = await readFile("temp/test3.wotbreplay");
|
||||
const stream = new WotbReplayReadStream(replay.buffer as ArrayBuffer);
|
||||
|
||||
stream.wotbReplay();
|
||||
@@ -1,4 +1,4 @@
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import { assertSecret } from "@blitzkit/core";
|
||||
import {
|
||||
AttachmentBuilder,
|
||||
AutocompleteInteraction,
|
||||
@@ -13,30 +13,30 @@ import {
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
SlashCommandSubcommandsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
import { aboutCommand } from '../../commands/about';
|
||||
import { breakdownCommand } from '../../commands/breakdown';
|
||||
import { debugCommand } from '../../commands/debug';
|
||||
import { evolutionCommand } from '../../commands/evolution';
|
||||
import { fullStatsCommand } from '../../commands/fullStats';
|
||||
import { inactiveCommand } from '../../commands/inactive';
|
||||
import { verifyCommand } from '../../commands/link';
|
||||
import { ownedTanksCommand } from '../../commands/ownedTanks';
|
||||
import { permissionsCommand } from '../../commands/permissions';
|
||||
import { pingCommand } from '../../commands/ping';
|
||||
import { playerInfoCommand } from '../../commands/playerInfo';
|
||||
import { ratingCommand } from '../../commands/rating';
|
||||
import { ratingLeaderboardCommand } from '../../commands/ratingLeaderboard';
|
||||
import { replayCommand } from '../../commands/replay';
|
||||
import { researchCommand } from '../../commands/research';
|
||||
import { searchClansCommand } from '../../commands/searchClans';
|
||||
import { searchPlayersCommand } from '../../commands/searchPlayers';
|
||||
import { statsCommand } from '../../commands/stats';
|
||||
import { todayCommand } from '../../commands/today';
|
||||
import { RenderConfiguration } from '../../core/blitzkit/renderConfiguration';
|
||||
import { handleAutocomplete } from './handlers/autocomplete';
|
||||
import { handleButton } from './handlers/button';
|
||||
import { handleChatInputCommand } from './handlers/chatInputCommand';
|
||||
} from "discord.js";
|
||||
import { aboutCommand } from "../../commands/about";
|
||||
import { breakdownCommand } from "../../commands/breakdown";
|
||||
import { debugCommand } from "../../commands/debug";
|
||||
import { evolutionCommand } from "../../commands/evolution";
|
||||
import { fullStatsCommand } from "../../commands/fullStats";
|
||||
import { inactiveCommand } from "../../commands/inactive";
|
||||
import { verifyCommand } from "../../commands/link";
|
||||
import { ownedTanksCommand } from "../../commands/ownedTanks";
|
||||
import { permissionsCommand } from "../../commands/permissions";
|
||||
import { pingCommand } from "../../commands/ping";
|
||||
import { playerInfoCommand } from "../../commands/playerInfo";
|
||||
import { ratingCommand } from "../../commands/rating";
|
||||
import { ratingLeaderboardCommand } from "../../commands/ratingLeaderboard";
|
||||
import { replayCommand } from "../../commands/replay";
|
||||
import { researchCommand } from "../../commands/research";
|
||||
import { searchClansCommand } from "../../commands/searchClans";
|
||||
import { searchPlayersCommand } from "../../commands/searchPlayers";
|
||||
import { statsCommand } from "../../commands/stats";
|
||||
import { todayCommand } from "../../commands/today";
|
||||
import { RenderConfiguration } from "../../core/blitzkit/renderConfiguration";
|
||||
import { handleAutocomplete } from "./handlers/autocomplete";
|
||||
import { handleButton } from "./handlers/button";
|
||||
import { handleChatInputCommand } from "./handlers/chatInputCommand";
|
||||
|
||||
export type InteractionRawReturnable =
|
||||
| string
|
||||
@@ -44,7 +44,7 @@ export type InteractionRawReturnable =
|
||||
| ButtonBuilder
|
||||
| AttachmentBuilder
|
||||
| RenderConfiguration
|
||||
| JSX.Element
|
||||
| React.JSX.Element
|
||||
| null;
|
||||
export type InteractionIterableReturnable =
|
||||
| InteractionRawReturnable
|
||||
@@ -58,7 +58,7 @@ interface CommandRegistryBase {
|
||||
| SlashCommandBuilder
|
||||
| SlashCommandSubcommandsOnlyBuilder
|
||||
| SlashCommandOptionsOnlyBuilder
|
||||
| Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
|
||||
| Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
|
||||
|
||||
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
|
||||
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
|
||||
@@ -72,7 +72,7 @@ interface CommandRegistryImplicit extends CommandRegistryBase {
|
||||
interface CommandRegistryExplicit extends CommandRegistryBase {
|
||||
handlesInteraction?: false;
|
||||
handler: (
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) => InteractionReturnable;
|
||||
}
|
||||
|
||||
@@ -104,16 +104,16 @@ export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
|
||||
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
|
||||
return rawCommands.reduce<Record<string, CommandRegistry>>(
|
||||
(commands, registry, index) => {
|
||||
if (registry.status === 'rejected') {
|
||||
if (registry.status === "rejected") {
|
||||
console.warn(
|
||||
`Command ${index} failed to load; skipping...`,
|
||||
registry.reason,
|
||||
registry.reason
|
||||
);
|
||||
return commands;
|
||||
}
|
||||
return { ...commands, [registry.value.command.name]: registry.value };
|
||||
},
|
||||
{},
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -121,12 +121,12 @@ const rest = new REST().setToken(assertSecret(import.meta.env.DISCORD_TOKEN));
|
||||
|
||||
commands.then((awaitedCommands) => {
|
||||
const body = Object.values(awaitedCommands).map((registry) =>
|
||||
registry.command.toJSON(),
|
||||
registry.command.toJSON()
|
||||
);
|
||||
|
||||
rest.put(
|
||||
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
|
||||
{ body },
|
||||
{ body }
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"dependencies": {
|
||||
"@octokit/plugin-throttling": "^8.2.0",
|
||||
"@octokit/rest": "^20.1.1",
|
||||
"@prisma/client": "^6.6.0",
|
||||
"@octokit/plugin-throttling": "^11.0.1",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@prisma/client": "^6.16.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"crc": "^4.3.2",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv": "^17.2.2",
|
||||
"lz4js": "^0.2.0",
|
||||
"pickleparser": "^0.2.1",
|
||||
"protobufjs": "^7.4.0",
|
||||
"ts-proto": "^2.2.2",
|
||||
"yaml": "^2.5.0"
|
||||
"protobufjs": "^7.5.4",
|
||||
"ts-proto": "^2.7.7",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/lz4js": "^0.2.1"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export function assertSecret(secret?: string) {
|
||||
if (secret === undefined) throw new Error('Missing secret');
|
||||
export function assertSecret<Type>(secret?: Type) {
|
||||
if (secret === undefined) throw new Error("Missing secret");
|
||||
return secret;
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
import { assertSecret } from './assertSecret';
|
||||
|
||||
const repo = assertSecret(import.meta.env.PUBLIC_ASSET_REPO);
|
||||
const branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
|
||||
const fastUser = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_USER);
|
||||
const fastRepo = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_REPO);
|
||||
|
||||
export function asset(path: string) {
|
||||
return branch === 'main'
|
||||
? `https://${fastUser}.github.io/${fastRepo}/${path}`
|
||||
: `https://raw.githubusercontent.com/${repo}/${branch}/${path}`;
|
||||
return `${assertSecret(import.meta.env.PUBLIC_ASSET_BASE)}/${path}`;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
GunDefinition,
|
||||
GunDefinitionAutoReloaderProperties,
|
||||
GunDefinitionAutoReloader,
|
||||
ShellDefinition,
|
||||
} from '../protos';
|
||||
|
||||
@@ -15,34 +15,46 @@ export function resolveDpm(
|
||||
let dps: number;
|
||||
|
||||
if (gun.gun_type!.$case === 'regular') {
|
||||
dps = alpha / (reloadCoefficient * gun.gun_type!.value.extension.reload);
|
||||
dps = alpha / (reloadCoefficient * gun.gun_type!.value.reload);
|
||||
} else if (gun.gun_type!.$case === 'auto_loader') {
|
||||
dps =
|
||||
(alpha * gun.gun_type!.value.extension.shell_count) /
|
||||
(gun.gun_type!.value.extension.clip_reload * reloadCoefficient +
|
||||
(gun.gun_type!.value.extension.shell_count - 1) *
|
||||
gun.gun_type!.value.extension.intra_clip *
|
||||
intraClipCoefficient);
|
||||
} else {
|
||||
const mostOptimalShell =
|
||||
gun.gun_type!.value.extension.shell_reloads.reduce<null | {
|
||||
index: number;
|
||||
reload: number;
|
||||
}>((current, reloadRaw, index) => {
|
||||
const reload =
|
||||
reloadRaw * reloadCoefficient +
|
||||
(index > 0
|
||||
? (
|
||||
gun.gun_type!.value
|
||||
.extension as GunDefinitionAutoReloaderProperties
|
||||
).intra_clip * intraClipCoefficient
|
||||
: 0);
|
||||
const damage = alpha * gun.gun_type!.value.shell_count;
|
||||
let time = gun.gun_type!.value.clip_reload * reloadCoefficient;
|
||||
|
||||
if (current === null || reload < current.reload) {
|
||||
return { index, reload };
|
||||
}
|
||||
return current;
|
||||
}, null)!;
|
||||
if (gun.burst) {
|
||||
time +=
|
||||
(gun.gun_type!.value.shell_count / gun.burst.count - 1) *
|
||||
gun.gun_type!.value.intra_clip *
|
||||
intraClipCoefficient;
|
||||
|
||||
time +=
|
||||
(gun.gun_type!.value.shell_count / gun.burst.count) *
|
||||
(gun.burst.count - 1) *
|
||||
gun.burst.interval;
|
||||
} else {
|
||||
time +=
|
||||
(gun.gun_type!.value.shell_count - 1) *
|
||||
gun.gun_type!.value.intra_clip *
|
||||
intraClipCoefficient;
|
||||
}
|
||||
|
||||
dps = damage / time;
|
||||
} else {
|
||||
const mostOptimalShell = gun.gun_type!.value.shell_reloads.reduce<null | {
|
||||
index: number;
|
||||
reload: number;
|
||||
}>((current, reloadRaw, index) => {
|
||||
const reload =
|
||||
reloadRaw * reloadCoefficient +
|
||||
(index > 0
|
||||
? (gun.gun_type!.value as GunDefinitionAutoReloader).intra_clip *
|
||||
intraClipCoefficient
|
||||
: 0);
|
||||
|
||||
if (current === null || reload < current.reload) {
|
||||
return { index, reload };
|
||||
}
|
||||
return current;
|
||||
}, null)!;
|
||||
|
||||
dps = alpha / mostOptimalShell?.reload;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { asset } from "@blitzkit/core";
|
||||
|
||||
export const BLITZKIT_TANK_ICON_SIZE = { width: 40 * 16, height: 30 * 16 };
|
||||
|
||||
export function tankIcon(
|
||||
id: number,
|
||||
size: 'big' | 'small' | 'blitzkit' = 'blitzkit',
|
||||
) {
|
||||
export function tankIcon(id: number, size: "big" | "small" = "big") {
|
||||
return asset(`icons/tanks/${size}/${id}.webp`);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { deburr } from 'lodash-es';
|
||||
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
|
||||
import { I18nString } from '../protos';
|
||||
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
|
||||
import { fetchTankDefinitions } from './tankDefinitions';
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
import { deburr } from "lodash-es";
|
||||
import { I18nString } from "../protos";
|
||||
import { fetchCamouflageDefinitions } from "./camouflageDefinitions";
|
||||
import { fetchTankDefinitions } from "./tankDefinitions";
|
||||
|
||||
export async function fetchTankNames() {
|
||||
const [tankDefinitions, camouflageDefinitions] = await Promise.all([
|
||||
@@ -26,25 +26,27 @@ export async function fetchTankNames() {
|
||||
searchableNameDeburr,
|
||||
camouflages: tank.camouflages
|
||||
?.map((id) =>
|
||||
SUPPORTED_LOCALES.map(
|
||||
(locale) =>
|
||||
camouflageDefinitions.camouflages[id]?.name.locales[locale],
|
||||
),
|
||||
locales.supported.map(
|
||||
({ locale }) =>
|
||||
camouflageDefinitions.camouflages[id]?.name.locales[locale]
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.filter(Boolean)
|
||||
.map(deburr)
|
||||
.join(' '),
|
||||
.join(" "),
|
||||
treeType: tank.type,
|
||||
};
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export const SEARCH_KEYS = [
|
||||
...SUPPORTED_LOCALES.map((locale) => [
|
||||
`searchableName.locales.${locale}`,
|
||||
`searchableNameDeburr.locales.${locale}`,
|
||||
]).flat(),
|
||||
'camouflages',
|
||||
...locales.supported
|
||||
.map(({ locale }) => [
|
||||
`searchableName.locales.${locale}`,
|
||||
`searchableNameDeburr.locales.${locale}`,
|
||||
])
|
||||
.flat(),
|
||||
"camouflages",
|
||||
];
|
||||
@@ -7,8 +7,8 @@ export function uniqueGuns(turrets: TurretDefinition[]) {
|
||||
|
||||
forEachRight(turrets, (turret) => {
|
||||
forEachRight(turret.guns, (gun) => {
|
||||
if (!gunIds.has(gun.gun_type!.value.base.id)) {
|
||||
gunIds.add(gun.gun_type!.value.base.id);
|
||||
if (!gunIds.has(gun.id)) {
|
||||
gunIds.add(gun.id);
|
||||
guns.push({ turret, gun });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,4 +4,6 @@ export const youtubers = [
|
||||
{ id: 'UCxRpZqMElssTdPYDK3x7wyg', name: 'N90' },
|
||||
{ id: 'UC28Hc6p2MGoB5s78eHCYkQg', name: 'GuidesBlitz' },
|
||||
{ id: 'UCLMEfqtlwLDN-UXX21RiEww', name: 'WoT Blitz Best Replays' },
|
||||
{ id: 'UC3fZbetRUFoJL0IEN7IhG0Q', name: 'EconomicsMate' },
|
||||
{ id: 'UCtsUVwBlhEnUunAKLn2dtsg', name: 'Megalion69' },
|
||||
];
|
||||
@@ -1,5 +0,0 @@
|
||||
import { MessageFns } from '../protos';
|
||||
|
||||
export function encodePBBuffer<Type>(message: MessageFns<Type>, data: Type) {
|
||||
return Buffer.from(message.encode(data).finish());
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './encodePBBuffer';
|
||||
export * from './fetchPB';
|
||||
export * from './pbMap';
|
||||
@@ -5,10 +5,10 @@
|
||||
// source: packages/core/src/protos/camouflage_definitions.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
import { createBaseI18nString, I18nString } from "./i18n";
|
||||
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire';
|
||||
import { createBaseI18nString, I18nString } from './i18n';
|
||||
|
||||
export const protobufPackage = "blitzkit";
|
||||
export const protobufPackage = 'blitzkit';
|
||||
|
||||
export interface CamouflageDefinitions {
|
||||
camouflages: { [key: number]: Camouflage };
|
||||
@@ -22,8 +22,8 @@ export interface CamouflageDefinitions_CamouflagesEntry {
|
||||
export interface Camouflage {
|
||||
id: number;
|
||||
name: I18nString;
|
||||
tank_name?: I18nString | undefined;
|
||||
tank_name_full?: I18nString | undefined;
|
||||
tank_name?: I18nString;
|
||||
tank_name_full?: I18nString;
|
||||
}
|
||||
|
||||
export function createBaseCamouflageDefinitions(): CamouflageDefinitions {
|
||||
@@ -31,15 +31,25 @@ export function createBaseCamouflageDefinitions(): CamouflageDefinitions {
|
||||
}
|
||||
|
||||
export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
|
||||
encode(message: CamouflageDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
encode(
|
||||
message: CamouflageDefinitions,
|
||||
writer: BinaryWriter = new BinaryWriter(),
|
||||
): BinaryWriter {
|
||||
Object.entries(message.camouflages).forEach(([key, value]) => {
|
||||
CamouflageDefinitions_CamouflagesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
|
||||
CamouflageDefinitions_CamouflagesEntry.encode(
|
||||
{ key: key as any, value },
|
||||
writer.uint32(10).fork(),
|
||||
).join();
|
||||
});
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): CamouflageDefinitions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
decode(
|
||||
input: BinaryReader | Uint8Array,
|
||||
length?: number,
|
||||
): CamouflageDefinitions {
|
||||
const reader =
|
||||
input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseCamouflageDefinitions();
|
||||
while (reader.pos < end) {
|
||||
@@ -50,7 +60,10 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
|
||||
break;
|
||||
}
|
||||
|
||||
const entry1 = CamouflageDefinitions_CamouflagesEntry.decode(reader, reader.uint32());
|
||||
const entry1 = CamouflageDefinitions_CamouflagesEntry.decode(
|
||||
reader,
|
||||
reader.uint32(),
|
||||
);
|
||||
if (entry1.value !== undefined) {
|
||||
message.camouflages[entry1.key] = entry1.value;
|
||||
}
|
||||
@@ -68,10 +81,12 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
|
||||
fromJSON(object: any): CamouflageDefinitions {
|
||||
return {
|
||||
camouflages: isObject(object.camouflages)
|
||||
? Object.entries(object.camouflages).reduce<{ [key: number]: Camouflage }>((acc, [key, value]) => {
|
||||
acc[globalThis.Number(key)] = Camouflage.fromJSON(value);
|
||||
return acc;
|
||||
}, {})
|
||||
? Object.entries(object.camouflages).reduce<{
|
||||
[key: number]: Camouflage;
|
||||
}>((acc, [key, value]) => {
|
||||
acc[globalThis.Number(key)] = Camouflage.fromJSON(value);
|
||||
return acc;
|
||||
}, {})
|
||||
: {},
|
||||
};
|
||||
},
|
||||
@@ -90,20 +105,23 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(base?: I): CamouflageDefinitions {
|
||||
create<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(
|
||||
base?: I,
|
||||
): CamouflageDefinitions {
|
||||
return CamouflageDefinitions.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(object: I): CamouflageDefinitions {
|
||||
fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(
|
||||
object: I,
|
||||
): CamouflageDefinitions {
|
||||
const message = createBaseCamouflageDefinitions();
|
||||
message.camouflages = Object.entries(object.camouflages ?? {}).reduce<{ [key: number]: Camouflage }>(
|
||||
(acc, [key, value]) => {
|
||||
if (value !== undefined) {
|
||||
acc[globalThis.Number(key)] = Camouflage.fromPartial(value);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
message.camouflages = Object.entries(object.camouflages ?? {}).reduce<{
|
||||
[key: number]: Camouflage;
|
||||
}>((acc, [key, value]) => {
|
||||
if (value !== undefined) {
|
||||
acc[globalThis.Number(key)] = Camouflage.fromPartial(value);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
return message;
|
||||
},
|
||||
};
|
||||
@@ -112,90 +130,116 @@ export function createBaseCamouflageDefinitions_CamouflagesEntry(): CamouflageDe
|
||||
return { key: 0, value: createBaseCamouflage() };
|
||||
}
|
||||
|
||||
export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefinitions_CamouflagesEntry> = {
|
||||
encode(message: CamouflageDefinitions_CamouflagesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.key !== undefined) {
|
||||
writer.uint32(8).uint32(message.key);
|
||||
}
|
||||
if (message.value !== undefined) {
|
||||
Camouflage.encode(message.value, writer.uint32(18).fork()).join();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): CamouflageDefinitions_CamouflagesEntry {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.key = reader.uint32();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.value = Camouflage.decode(reader, reader.uint32());
|
||||
continue;
|
||||
}
|
||||
export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefinitions_CamouflagesEntry> =
|
||||
{
|
||||
encode(
|
||||
message: CamouflageDefinitions_CamouflagesEntry,
|
||||
writer: BinaryWriter = new BinaryWriter(),
|
||||
): BinaryWriter {
|
||||
if (message.key !== undefined) {
|
||||
writer.uint32(8).uint32(message.key);
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
if (message.value !== undefined) {
|
||||
Camouflage.encode(message.value, writer.uint32(18).fork()).join();
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
return writer;
|
||||
},
|
||||
|
||||
fromJSON(object: any): CamouflageDefinitions_CamouflagesEntry {
|
||||
return {
|
||||
key: globalThis.Number(assertSet("CamouflageDefinitions_CamouflagesEntry.key", object.key)),
|
||||
value: Camouflage.fromJSON(assertSet("CamouflageDefinitions_CamouflagesEntry.value", object.value)),
|
||||
};
|
||||
},
|
||||
decode(
|
||||
input: BinaryReader | Uint8Array,
|
||||
length?: number,
|
||||
): CamouflageDefinitions_CamouflagesEntry {
|
||||
const reader =
|
||||
input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
toJSON(message: CamouflageDefinitions_CamouflagesEntry): unknown {
|
||||
const obj: any = {};
|
||||
if (message.key !== undefined) {
|
||||
obj.key = Math.round(message.key);
|
||||
}
|
||||
if (message.value !== undefined) {
|
||||
obj.value = Camouflage.toJSON(message.value);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
message.key = reader.uint32();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break;
|
||||
}
|
||||
|
||||
create<I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>>(
|
||||
base?: I,
|
||||
): CamouflageDefinitions_CamouflagesEntry {
|
||||
return CamouflageDefinitions_CamouflagesEntry.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>>(
|
||||
object: I,
|
||||
): CamouflageDefinitions_CamouflagesEntry {
|
||||
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
|
||||
message.key = object.key ?? 0;
|
||||
message.value = (object.value !== undefined && object.value !== null)
|
||||
? Camouflage.fromPartial(object.value)
|
||||
: createBaseCamouflage();
|
||||
return message;
|
||||
},
|
||||
};
|
||||
message.value = Camouflage.decode(reader, reader.uint32());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): CamouflageDefinitions_CamouflagesEntry {
|
||||
return {
|
||||
key: globalThis.Number(
|
||||
assertSet('CamouflageDefinitions_CamouflagesEntry.key', object.key),
|
||||
),
|
||||
value: Camouflage.fromJSON(
|
||||
assertSet(
|
||||
'CamouflageDefinitions_CamouflagesEntry.value',
|
||||
object.value,
|
||||
),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: CamouflageDefinitions_CamouflagesEntry): unknown {
|
||||
const obj: any = {};
|
||||
if (message.key !== undefined) {
|
||||
obj.key = Math.round(message.key);
|
||||
}
|
||||
if (message.value !== undefined) {
|
||||
obj.value = Camouflage.toJSON(message.value);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<
|
||||
I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>,
|
||||
>(base?: I): CamouflageDefinitions_CamouflagesEntry {
|
||||
return CamouflageDefinitions_CamouflagesEntry.fromPartial(
|
||||
base ?? ({} as any),
|
||||
);
|
||||
},
|
||||
fromPartial<
|
||||
I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>,
|
||||
>(object: I): CamouflageDefinitions_CamouflagesEntry {
|
||||
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
|
||||
message.key = object.key ?? 0;
|
||||
message.value =
|
||||
object.value !== undefined && object.value !== null
|
||||
? Camouflage.fromPartial(object.value)
|
||||
: createBaseCamouflage();
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
export function createBaseCamouflage(): Camouflage {
|
||||
return { id: 0, name: createBaseI18nString(), tank_name: undefined, tank_name_full: undefined };
|
||||
return {
|
||||
id: 0,
|
||||
name: createBaseI18nString(),
|
||||
tank_name: undefined,
|
||||
tank_name_full: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export const Camouflage: MessageFns<Camouflage> = {
|
||||
encode(message: Camouflage, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
encode(
|
||||
message: Camouflage,
|
||||
writer: BinaryWriter = new BinaryWriter(),
|
||||
): BinaryWriter {
|
||||
if (message.id !== 0) {
|
||||
writer.uint32(8).uint32(message.id);
|
||||
}
|
||||
@@ -206,13 +250,17 @@ export const Camouflage: MessageFns<Camouflage> = {
|
||||
I18nString.encode(message.tank_name, writer.uint32(26).fork()).join();
|
||||
}
|
||||
if (message.tank_name_full !== undefined) {
|
||||
I18nString.encode(message.tank_name_full, writer.uint32(34).fork()).join();
|
||||
I18nString.encode(
|
||||
message.tank_name_full,
|
||||
writer.uint32(34).fork(),
|
||||
).join();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Camouflage {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const reader =
|
||||
input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseCamouflage();
|
||||
while (reader.pos < end) {
|
||||
@@ -261,10 +309,14 @@ export const Camouflage: MessageFns<Camouflage> = {
|
||||
|
||||
fromJSON(object: any): Camouflage {
|
||||
return {
|
||||
id: globalThis.Number(assertSet("Camouflage.id", object.id)),
|
||||
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,
|
||||
id: globalThis.Number(assertSet('Camouflage.id', object.id)),
|
||||
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,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -288,37 +340,57 @@ export const Camouflage: MessageFns<Camouflage> = {
|
||||
create<I extends Exact<DeepPartial<Camouflage>, I>>(base?: I): Camouflage {
|
||||
return Camouflage.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>(object: I): Camouflage {
|
||||
fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>(
|
||||
object: I,
|
||||
): Camouflage {
|
||||
const message = createBaseCamouflage();
|
||||
message.id = object.id ?? 0;
|
||||
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;
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
||||
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>;
|
||||
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 };
|
||||
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;
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
|
||||
@@ -108,55 +108,55 @@ message GunDefinition {
|
||||
GunDefinitionAutoLoader auto_loader = 2;
|
||||
GunDefinitionAutoReloader auto_reloader = 3;
|
||||
}
|
||||
|
||||
required uint32 id = 4;
|
||||
required float rotation_speed = 5;
|
||||
optional ResearchCost research_cost = 6;
|
||||
required uint32 weight = 7;
|
||||
required I18nString name = 8;
|
||||
required uint32 tier = 9;
|
||||
repeated ShellDefinition shells = 10;
|
||||
required float camouflage_loss = 11;
|
||||
required float aim_time = 12;
|
||||
required float dispersion_base = 13;
|
||||
required float dispersion_traverse = 14;
|
||||
required float dispersion_shot = 15;
|
||||
required float dispersion_damaged = 16;
|
||||
repeated Unlock unlocks = 17;
|
||||
required uint32 shell_capacity = 18;
|
||||
optional AssaultRanges assault_ranges = 19;
|
||||
optional Burst burst = 20;
|
||||
}
|
||||
|
||||
message GunDefinitionRegular {
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionRegularProperties extension = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionRegularProperties {
|
||||
required float reload = 1;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoLoader {
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionAutoLoaderProperties extension = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoLoaderProperties {
|
||||
required float clip_reload = 1;
|
||||
required float intra_clip = 2;
|
||||
required float shell_count = 3;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoReloader {
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionAutoReloaderProperties extension = 2;
|
||||
message Burst {
|
||||
required uint32 count = 1;
|
||||
required float interval = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoReloaderProperties {
|
||||
message GunDefinitionAutoReloader {
|
||||
repeated float shell_reloads = 1;
|
||||
required float intra_clip = 2;
|
||||
required float shell_count = 3;
|
||||
}
|
||||
|
||||
message GunDefinitionBase {
|
||||
required uint32 id = 1;
|
||||
required float rotation_speed = 2;
|
||||
optional ResearchCost research_cost = 3;
|
||||
required uint32 weight = 4;
|
||||
required I18nString name = 5;
|
||||
required uint32 tier = 6;
|
||||
repeated ShellDefinition shells = 7;
|
||||
required float camouflage_loss = 8;
|
||||
required float aim_time = 9;
|
||||
required float dispersion_base = 10;
|
||||
required float dispersion_traverse = 11;
|
||||
required float dispersion_shot = 12;
|
||||
required float dispersion_damaged = 13;
|
||||
repeated Unlock unlocks = 14;
|
||||
required uint32 shell_capacity = 15;
|
||||
message AssaultRanges {
|
||||
repeated AssaultRange ranges = 1;
|
||||
repeated ShellType types = 2;
|
||||
}
|
||||
|
||||
message AssaultRange {
|
||||
required float distance = 1;
|
||||
required float factor = 2;
|
||||
}
|
||||
|
||||
message ShellDefinition {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -4,7 +4,7 @@
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"dependencies": {
|
||||
"ts-deepmerge": "^7.0.2"
|
||||
"ts-deepmerge": "^7.0.3"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './literals';
|
||||
export * from './strings';
|
||||
export * from './unwrapper';
|
||||
export * from "./literals";
|
||||
export * from "./strings";
|
||||
export * from "./unwrapper";
|
||||
export * from "./wrapper";
|
||||
@@ -1,5 +1,20 @@
|
||||
export function literals(string: string, literals: string[]) {
|
||||
return literals.reduce((accumulator, literal, index) => {
|
||||
return accumulator.replace(`%s${index + 1}`, literal);
|
||||
}, string);
|
||||
import { assertSecret } from "@blitzkit/core";
|
||||
|
||||
const dev = assertSecret(import.meta.env.DEV);
|
||||
|
||||
export function literals(
|
||||
string: string,
|
||||
literals?: Record<string, string | number | undefined>
|
||||
) {
|
||||
for (const key in literals) {
|
||||
const mutated = string.replaceAll(`{${key}}`, `${literals[key]}`);
|
||||
|
||||
if (dev && mutated === string) {
|
||||
console.warn(`Could not find literal {${key}} in string "${string}"`);
|
||||
}
|
||||
|
||||
string = mutated;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
@@ -1,98 +1,66 @@
|
||||
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';
|
||||
import { merge } from "ts-deepmerge";
|
||||
import locales from "../locales.json";
|
||||
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 };
|
||||
const stringsRaw: Record<string, TranslationTree> = {
|
||||
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>> = {};
|
||||
export const STRINGS: Record<string, BlitzKitStrings> = {};
|
||||
|
||||
for (const locale in stringsRaw) {
|
||||
if (locale === DEFAULT_LOCALE) {
|
||||
stringsPartial[DEFAULT_LOCALE] = stringsRaw[DEFAULT_LOCALE];
|
||||
if (locale === locales.default) {
|
||||
STRINGS[locales.default] = stringsRaw[locales.default] as BlitzKitStrings;
|
||||
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]!);
|
||||
}
|
||||
const localeStrings = stringsRaw[locale];
|
||||
STRINGS[locale] = merge(
|
||||
stringsRaw[locales.default],
|
||||
localeStrings
|
||||
) as BlitzKitStrings;
|
||||
}
|
||||
|
||||
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_BLITZ_MAP: Record<string, 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 const SUPPORTED_LOCALE_FLAGS: Record<string, string> = {
|
||||
en: "🇺🇸",
|
||||
es: "🇪🇸",
|
||||
fr: "🇫🇷",
|
||||
ja: "🇯🇵",
|
||||
pt: "🇵🇹",
|
||||
ru: "🇷🇺",
|
||||
uk: "🇺🇦",
|
||||
zh: "🇨🇳",
|
||||
pl: "🇵🇱",
|
||||
};
|
||||
|
||||
export type TranslationTree = { [key: string]: TranslationNode };
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { I18nString } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from './strings';
|
||||
import { I18nString } from "@blitzkit/core";
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
|
||||
export function unwrapper(locale: string) {
|
||||
export function unwrapper(locale = locales.default) {
|
||||
return function (i18nString: I18nString) {
|
||||
if (locale in i18nString.locales) {
|
||||
return i18nString.locales[locale];
|
||||
}
|
||||
|
||||
return i18nString.locales[DEFAULT_LOCALE];
|
||||
return i18nString.locales[locales.default];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import locales from "../locales.json";
|
||||
import { BlitzKitStrings, STRINGS } from "./strings";
|
||||
|
||||
export function wrapper(slicer: (strings: BlitzKitStrings) => string) {
|
||||
const defaultString = slicer(STRINGS[locales.default] as BlitzKitStrings);
|
||||
|
||||
const object = { [locales.default]: defaultString };
|
||||
|
||||
for (const { locale } of locales.supported) {
|
||||
const string = slicer(STRINGS[locale] as BlitzKitStrings);
|
||||
if (string !== defaultString) object[locale] = string;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
+126
-114
@@ -1,19 +1,21 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "test",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "The quick brown fox jumps over the lazy dog."
|
||||
},
|
||||
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1hp",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
|
||||
"gun_types": {
|
||||
@@ -204,11 +206,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"%s1\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"%s1\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s1\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s1\". Try typing the name of the tank instead of a number.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". 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.",
|
||||
@@ -313,7 +315,7 @@
|
||||
"description": "Tier",
|
||||
|
||||
"choices": {
|
||||
"tier": "Tier %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -321,13 +323,13 @@
|
||||
"periods": {
|
||||
"career": "Career",
|
||||
"today": "Today",
|
||||
"days": "%s1 days",
|
||||
"custom": "%s1 - %s2 days"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
|
||||
"filters": {
|
||||
"none": "No filters",
|
||||
"tier": "Tier %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
|
||||
"no_data": {
|
||||
@@ -354,24 +356,6 @@
|
||||
},
|
||||
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "auction",
|
||||
"description": "Tanks available right now in the auction",
|
||||
|
||||
"body": {
|
||||
"title": "Blitz Auction",
|
||||
"subtitle": "%s1 available • next prices %s2\nBid now: %s3",
|
||||
"next": "Next price: %s1",
|
||||
"available": "Available: %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Bid now"
|
||||
}
|
||||
},
|
||||
|
||||
"replay": {
|
||||
"name": "replay",
|
||||
"description": "Display replay results"
|
||||
@@ -395,20 +379,20 @@
|
||||
"body": {
|
||||
"subtitle": "Research",
|
||||
"total": "Total",
|
||||
"research": "Research: %s1",
|
||||
"upgrades": "Upgrades: %s1",
|
||||
"purchase": "Purchase: %s1",
|
||||
"equipment": "Equipment: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
|
||||
},
|
||||
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# %s1 is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# %s1 is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
|
||||
"start_end_equal": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -454,7 +438,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Winrate",
|
||||
"damage": "Damage",
|
||||
"unknown_tank": "Unknown tank %s1"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -497,13 +481,13 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "threshold",
|
||||
"description": "The number of days inactive (default: %s1 days)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
|
||||
"body": {
|
||||
"subtitle": "Inactive for %s1+ days",
|
||||
"listing": "%s1 days"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -550,7 +534,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Accounts linked",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 failed to verify",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "I don't have the permission to manage roles."
|
||||
}
|
||||
},
|
||||
@@ -568,12 +552,12 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "The max number of clan results (default: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
|
||||
"body": {
|
||||
"title": "Clan search for %s1 in region %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "No clans found"
|
||||
}
|
||||
},
|
||||
@@ -585,12 +569,12 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "The max number of player results (default: %s1)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
|
||||
"body": {
|
||||
"title": "Player search for %s1 in region %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "No players found"
|
||||
}
|
||||
},
|
||||
@@ -614,7 +598,7 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "How many neighbors to display (default: %s1)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
|
||||
"season": {
|
||||
@@ -622,7 +606,7 @@
|
||||
"description": "Season number (default: current)",
|
||||
|
||||
"choices": {
|
||||
"current": "%s1 (current)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -633,12 +617,12 @@
|
||||
},
|
||||
|
||||
"body": {
|
||||
"top_players": "Top %s1 players",
|
||||
"no_participation": "# %s1 didn't play rating in season %s2\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||
"deleted_player": "Deleted %s1",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Rating neighbors",
|
||||
"more": "+ %s1 more"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -657,7 +641,7 @@
|
||||
"description": "Shows all own tanks in a tier",
|
||||
|
||||
"body": {
|
||||
"title": "# %s1's owned tier %s2 tanks\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -666,7 +650,7 @@
|
||||
"description": "Checks wether the bot has the required permissions to function",
|
||||
|
||||
"body": {
|
||||
"title": "# Permissions\n\n%s1",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"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",
|
||||
@@ -680,7 +664,7 @@
|
||||
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
|
||||
"subcommands": {
|
||||
@@ -700,7 +684,7 @@
|
||||
"description": "Basic information about a player",
|
||||
|
||||
"body": {
|
||||
"title": "%s1's information",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Nickname",
|
||||
"battles": "Battles",
|
||||
"winrate": "Winrate",
|
||||
@@ -733,7 +717,7 @@
|
||||
},
|
||||
|
||||
"errors": {
|
||||
"no_participation": "# %s1 hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -763,13 +747,16 @@
|
||||
},
|
||||
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Search tanks...",
|
||||
"recent": "Recently viewed",
|
||||
"select_all": "Select all",
|
||||
"count_singular": "1 tank",
|
||||
"count_plural": "%s1 tanks",
|
||||
"sorting_by": "Sorting by %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "No tanks found.",
|
||||
"search_again": "Try searching again.",
|
||||
@@ -790,6 +777,7 @@
|
||||
"fire.caliber": "caliber",
|
||||
"fire.standardPenetration": "standard penetration",
|
||||
"fire.premiumPenetration": "premium penetration",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "damage",
|
||||
"fire.moduleDamage": "module damage",
|
||||
"fire.shellVelocity": "shell velocity",
|
||||
@@ -832,6 +820,15 @@
|
||||
},
|
||||
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
@@ -839,16 +836,16 @@
|
||||
},
|
||||
|
||||
"hero": {
|
||||
"credit": "By %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Popular tanks",
|
||||
"subtitle": "Last %s1 days"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Other cool projects"
|
||||
},
|
||||
"help_translate": "Please help translate %s1 to %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||
},
|
||||
|
||||
@@ -860,6 +857,15 @@
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -872,7 +878,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "No results"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
|
||||
"tankopedia": {
|
||||
@@ -882,10 +888,18 @@
|
||||
"button": "Find tanks",
|
||||
"description": "Blitz tank encyclopedia",
|
||||
"seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -905,9 +919,15 @@
|
||||
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -923,9 +943,7 @@
|
||||
"green_penetration": "Green penetration",
|
||||
"hide_model_under_armor": "Hide model under armor",
|
||||
"opaque": "Opaque",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Environment",
|
||||
"show_grid": "Show grid"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
|
||||
"screenshot": {
|
||||
@@ -951,7 +969,6 @@
|
||||
},
|
||||
|
||||
"advanced": "Advanced highlighting",
|
||||
"shooter": "Shooter:",
|
||||
|
||||
"shot_card": {
|
||||
"element": {
|
||||
@@ -963,15 +980,15 @@
|
||||
"track": "Tracks"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1hp penetration",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Blocked",
|
||||
"ricochet": "Ricochet",
|
||||
"splash": "%s1hp splash"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetration"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1011,8 +1028,8 @@
|
||||
"tech_tree": {
|
||||
"title": "Tech tree",
|
||||
"battle": "1 battle",
|
||||
"battles": "%s1 battles",
|
||||
"route": "Route %s1 of %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
|
||||
"game_modes": {
|
||||
@@ -1021,8 +1038,7 @@
|
||||
|
||||
"review": {
|
||||
"title": "Review videos",
|
||||
"disclaimer": "BlitzKit does not endorse any review videos.",
|
||||
"join": "Apply to %s1 YouTuber program"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
|
||||
"configuration": {
|
||||
@@ -1061,15 +1077,15 @@
|
||||
"title": "Characteristics",
|
||||
"compare": {
|
||||
"label": "Compare",
|
||||
"class": "Tier %s1 %s2",
|
||||
"tier": "All of tier %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "All tanks"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Shell %s1",
|
||||
"at_distance": "At %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Distance",
|
||||
"dispersion_at_distance": "Dispersion at %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Gun flexibility",
|
||||
"speed": "Speed",
|
||||
"terrain_coefficients": "Terrain coefficients",
|
||||
@@ -1078,7 +1094,6 @@
|
||||
"hull_traverse_speed": "Hull traverse speed",
|
||||
"camouflage": "Camouflage",
|
||||
"reload": "Reload",
|
||||
|
||||
"crewCount": "Crew count",
|
||||
"shellNormalization": "Normalization",
|
||||
"shellRicochet": "Ricochet",
|
||||
@@ -1086,12 +1101,13 @@
|
||||
"dpmEffective": "Effective at 60s",
|
||||
"shells": "Shells",
|
||||
"mostOptimalShellIndex": "Most optimal shell",
|
||||
"shellReloads": "Shell reloads",
|
||||
"shellReload": "Shell reload",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Reload",
|
||||
"intraClip": "Intra-clip",
|
||||
"caliber": "Caliber",
|
||||
"penetration": "Penetration",
|
||||
"damage": "Damage",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Clipping potential",
|
||||
"moduleDamage": "Module damage",
|
||||
"explosionRadius": "Explosion radius",
|
||||
@@ -1144,7 +1160,9 @@
|
||||
"shellRange": "Shell range",
|
||||
"shellCapacity": "Shell capacity",
|
||||
"gunType": "Gun type",
|
||||
"penetrationAt250m": "Penetration at 250m"
|
||||
"penetrationAt250m": "Penetration at 250m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1223,9 +1241,9 @@
|
||||
"description": "Tank performance statistics",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estimated stats based on %s1 players. Updated %s2 ago.",
|
||||
"hours": "%s1 hours",
|
||||
"minutes": "%s1 minutes"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Player count"
|
||||
@@ -1257,10 +1275,10 @@
|
||||
"button": "Browse all",
|
||||
"description": "Blitz player avatars",
|
||||
"seo_description": "All World of Tanks Blitz player avatars",
|
||||
"last_updated": "Last updated on %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Search avatars...",
|
||||
"results": "%s1 avatars"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1275,8 +1293,8 @@
|
||||
"no_results": "No players found"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Tracking %s1",
|
||||
"subtitle": "Since %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Reset",
|
||||
"change": "Change"
|
||||
},
|
||||
@@ -1339,7 +1357,7 @@
|
||||
"cancel": "Cancel",
|
||||
"success": {
|
||||
"title": "Copy URL",
|
||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Back",
|
||||
"copy": "Copy URL"
|
||||
}
|
||||
@@ -1424,17 +1442,11 @@
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+122
-111
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "test",
|
||||
"long": "Esta es una cadena de prueba larga. NO DEBES verla NUNCA en la interfaz de usuario. Mi fruta favorita es %s1 porque tiene el color %s2. ¡Me gusta tanto que diré su nombre 3 veces! ¡ %s1! ¡ %s1! ¡ %s1! Una vez más, es %s2, no %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "El rápido zorro marrón salta sobre el perro perezoso."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1pv",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Auto cargador",
|
||||
@@ -185,11 +186,11 @@
|
||||
"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`.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# Usa `/vincular` para empezar\n¡No sé quién eres, pero puedes decírmelo hoy!",
|
||||
"tank_name_not_found": "# No se pudo encontrar el tanque\nNo se pudo encontrar el tanque con el nombre \"%s\".",
|
||||
"tank_id_not_found": "# Tanque no encontrado\nNo se pudo encontrar al tanque del ID \"%s\". Prueba escribiendo el nombre del tanque en vez de un número.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"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.",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "tier",
|
||||
"description": "Nivel",
|
||||
"choices": {
|
||||
"tier": "Nivel %s - %s"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Carrera",
|
||||
"today": "Hoy",
|
||||
"days": "%s días",
|
||||
"custom": "%s - %s días"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Sin filtros",
|
||||
"tier": "Nivel %s"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "No hay batallas jugadas en este período",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "subasta",
|
||||
"description": "Tanques disponibles ahora mismo en la subasta",
|
||||
"body": {
|
||||
"title": "Subasta Blitz",
|
||||
"subtitle": "%s disponible • siguientes precios %s\nPujar ahora: %s",
|
||||
"next": "Siguiente precio: %s",
|
||||
"available": "Disponible: %s / %s"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# No hay ninguna subasta\nActualmente no hay tanques disponibles para comprar en la subasta. Si crees que esto es un error, por favor, reporta esta discrepancia en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Pujar ahora"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "repetición",
|
||||
"description": "Mostrar resultados de la repetición"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Investigar",
|
||||
"total": "Total",
|
||||
"research": "Investigar: %s",
|
||||
"upgrades": "Mejoras: %s",
|
||||
"purchase": "Comprar: %s",
|
||||
"equipment": "Equipamiento: %s",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"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."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -409,7 +394,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Tasa de victorias",
|
||||
"damage": "Daño",
|
||||
"unknown_tank": "Tanque desconocido %s"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "umbral",
|
||||
"description": "El número de días inactivo (predeterminado: %s días)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Inactivo por %s+ días",
|
||||
"listing": "%s días"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,7 +478,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Cuentas vinculadas",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 no se pudo verificar",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "No tengo permiso para administrar roles."
|
||||
}
|
||||
},
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "límite",
|
||||
"description": "El número máximo de resultados de clanes (predeterminado: %s)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Búsqueda del clan %s en la región %s",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "No se encontraron clanes"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "límite",
|
||||
"description": "El número máximo para resultados de jugador (predeterminado: %s)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Búsqueda del jugador %s en la región %s",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "No se encontraron jugadores"
|
||||
}
|
||||
},
|
||||
@@ -546,13 +531,13 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "Cuántos vecinos a mostrar (predeterminado: %s)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "temporada",
|
||||
"description": "Número de temporada (predeterminado: actual)",
|
||||
"choices": {
|
||||
"current": "%s (actual)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Mejores %s jugadores",
|
||||
"no_participation": "# %s no jugó clasificatoria en la temporada %s\n\nEste jugador no participó en esta temporada o no pasó de la calibración.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No hay temporada en curso\n\nActualmente no hay ninguna temporada de clasificatoria.",
|
||||
"deleted_player": "%s eliminado",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Vecinos de clasificatoria",
|
||||
"more": "+ %s más"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -581,14 +566,14 @@
|
||||
"name": "tanques en propiedad",
|
||||
"description": "Muestra todos los tanques propios en un nivel",
|
||||
"body": {
|
||||
"title": "# Tanques de %s de nivel %s\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permisos",
|
||||
"description": "Comprueba si el bot tiene los permisos requeridos para funcionar",
|
||||
"body": {
|
||||
"title": "# Permisos\n\n%s",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"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",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Comprueba si el bot está vivo.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -617,7 +602,7 @@
|
||||
"name": "info del jugador",
|
||||
"description": "Información básica sobre un jugador",
|
||||
"body": {
|
||||
"title": "Información de %s",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Apodo",
|
||||
"battles": "Batallas",
|
||||
"winrate": "Tasa de victorias",
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s no ha jugado clasificatoria\n\nEste jugador no ha participado en el modo clasificatoria esta temporada.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Modo de desarrollador",
|
||||
"language": "Idioma"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Copiado!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Buscar tanques...",
|
||||
"recent": "Visto recientemente",
|
||||
"select_all": "Seleccionar todo",
|
||||
"count_singular": "1 tanque",
|
||||
"count_plural": "%s1 tanques",
|
||||
"sorting_by": "Ordenar por %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "No se encontraron tanques.",
|
||||
"search_again": "Intenta buscar de nuevo.",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "calibre",
|
||||
"fire.standardPenetration": "penetración estándar",
|
||||
"fire.premiumPenetration": "penetración prémium",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "daño",
|
||||
"fire.moduleDamage": "daño al módulo",
|
||||
"fire.shellVelocity": "velocidad del proyectil",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "Por %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Tanques populares",
|
||||
"subtitle": "Últimos %s1 días"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Otros proyectos interesantes"
|
||||
},
|
||||
"help_translate": "¡Por favor ayuda a traducir %s1 a %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Herramientas para todo lo que es World of Tanks Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "Sin resultados"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "Encuentra tanques",
|
||||
"description": "Enciclopedia de tanques de WOTB",
|
||||
"seo_description": "Estadísticas, armaduras y más para todos los tanques en World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Nivel %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 blindaje, estadisticas, equipamiento, proviciones y mas.\n%s2 DPM, %3hp alfa, %s4mm penetracion, %s5kph velocidad maxima, %s6 hp salud",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Penetración verde",
|
||||
"hide_model_under_armor": "Ocultar modelo bajo la armadura",
|
||||
"opaque": "Opaco",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Entorno",
|
||||
"show_grid": "Mostrar cuadrícula"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Descargar",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Restablecer"
|
||||
},
|
||||
"advanced": "Resaltado avanzado",
|
||||
"shooter": "Tirador:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Primario",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Orugas"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1pv penetracion",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Bloqueado",
|
||||
"ricochet": "Rebote",
|
||||
"splash": "%s1pv splash"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetracion"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Árbol tecnológico",
|
||||
"battle": "1 batalla",
|
||||
"battles": "%s1 batallas",
|
||||
"route": "Ruta %s1 de %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Habilidades del modo de juego"
|
||||
},
|
||||
"review": {
|
||||
"title": "Vídeos de reseña",
|
||||
"disclaimer": "BlitzKit no respalda ningún vídeo de reseñas.",
|
||||
"join": "Aplica para el programa de YouTube de %s1"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Características",
|
||||
"compare": {
|
||||
"label": "Comparar con",
|
||||
"class": "Nivel %s1 - %s2",
|
||||
"tier": "Todos de nivel %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "Todos los tanques"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Proyectil %s1",
|
||||
"at_distance": "A %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Distancia",
|
||||
"dispersion_at_distance": "Dispersión a %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Flexibilidad del arma",
|
||||
"speed": "Velocidad",
|
||||
"terrain_coefficients": "Coeficientes de terreno",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Effective at 60s",
|
||||
"shells": "Proyectiles",
|
||||
"mostOptimalShellIndex": "Proyectil más óptimo",
|
||||
"shellReloads": "Tiempos de recarga",
|
||||
"shellReload": "Tiempo de recarga",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Recarga",
|
||||
"intraClip": "Intra-clip",
|
||||
"caliber": "Calibre",
|
||||
"penetration": "Penetración",
|
||||
"damage": "Daño",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Clipping potential",
|
||||
"moduleDamage": "Daño a módulos",
|
||||
"explosionRadius": "Radio de Explosión",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Rango del proyectil",
|
||||
"shellCapacity": "Capacidad de proyectiles",
|
||||
"gunType": "Tipo de cañón",
|
||||
"penetrationAt250m": "Penetración a 250 m"
|
||||
"penetrationAt250m": "Penetración a 250 m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Estadísticas de rendimiento del tanque",
|
||||
"seo_description": "Los mejores tanques y su rendimiento en World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estadísticas estimadas basadas en %s1 jugadores. Actualizado hace %s2.",
|
||||
"hours": "%s1 horas",
|
||||
"minutes": "%s1 minutos"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Contador de jugadores"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Explorar todo",
|
||||
"description": "Avatares para los jugadores de WOTB",
|
||||
"seo_description": "Todos los avatares del jugador en World of Tanks Blitz",
|
||||
"last_updated": "Última actualización el %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Buscar avatares...",
|
||||
"results": "%s1 avatares"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "No se encontraron jugadores"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Siguiendo a %s1",
|
||||
"subtitle": "Desde %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Restablecer",
|
||||
"change": "Change"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancelar",
|
||||
"success": {
|
||||
"title": "Copiar URL",
|
||||
"info": "Se generó la URL para %s1. El embebido comenzará a rastrear tan pronto como se pegue. Restablece en cualquier momento haciendo clic derecho en PC o manteniendo presionado en móvil y luego seleccionando \"Restablecer\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Volver",
|
||||
"copy": "Copiar URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analizar ahora",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+150
-139
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "teste",
|
||||
"long": "Ceci est une longue chaîne de test. Vous ne DEVEZ JAMAIS le voir dans l'interface utilisateur. Mon fruit préféré est %s1, car il a la couleur %s2. Je l'aime tellement que je vais dire son nom 3 fois ! %s1! %s1! %s1 ! Encore une fois, c'est %s2, pas %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "Le renard brun et rapide saute par-dessus le chien paresseux."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1ps",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Chargement auto",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Impossible de trouver le clan\nImpossible de trouver «%s1 ». Essayez de sélectionner un clan dans le résultat de la recherche.",
|
||||
"player_not_found": "# Impossible de trouver l'utilisateur\nJe n'ai pas pu trouver l'utilisateur \"%s1\". Essayez de choisir un utilisateur dans le résultat de la recherche, en tapant un nom d'utilisateur valide, ou en utilisant la commande `/link`.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# Utilisez `/link` pour commencer\nJe ne sais pas qui vous êtes, mais vous pouvez me le dire aujourd'hui !",
|
||||
"tank_name_not_found": "Impossible de trouver le char sous le nom \"%s1\".",
|
||||
"tank_id_not_found": "# Réservoir introuvable\nImpossible de trouver le char avec l'ID \"%s1\". Essayez de taper le nom du char au lieu d'un nombre.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"no_tank_stats": "# Aucune statistique de tank disponible\nCe joueur n'a pas de statistiques disponibles. Ce n'est peut-être pas le joueur que vous cherchez.",
|
||||
"get_help": "Aidez le serveur Discord",
|
||||
"uncaught_error": "# Blitzkit a rencontré une erreur !\nNous sommes désolés de cela. N'hésitez pas à [rejoindre le serveur officiel Discord](https://discord.gg/nDt7AjGJQH) pour obtenir de l'aide.",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "rang",
|
||||
"description": "Rang",
|
||||
"choices": {
|
||||
"tier": "Rang %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Carrière",
|
||||
"today": "Aujourd'hui",
|
||||
"days": "%s1 jour",
|
||||
"custom": "%s1 - %s2 jours"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Aucun filtre",
|
||||
"tier": "Rang %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "Char cible",
|
||||
"description": "Des chars sont disponibles dès maintenant dans la vente aux enchères",
|
||||
"body": {
|
||||
"title": "Enchères Blitz",
|
||||
"subtitle": "%s1 disponible • prochains prix %s2\nOffre : %s3",
|
||||
"next": "Prochain prix : %s1",
|
||||
"available": "Disponible : %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# Pas de vente aux enchères\n\nActuellement, il n'y a pas de chars disponibles à l'achat dans la vente aux enchères. Si vous pensez qu'il s'agit d'une erreur, veuillez signaler cette anomalie dans [le serveur officiel Discord](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Enchérir maintenant"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "rejouer",
|
||||
"description": "Afficher les résultats de la relecture"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Recherche",
|
||||
"total": "Total",
|
||||
"research": "Recherche: %s1",
|
||||
"upgrades": "Améliorations : %s1",
|
||||
"purchase": "Achat : %s1",
|
||||
"equipment": "Equipment: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# Ceci est une estimation !\n\nCette liste peut inclure des chars déjà recherchés en raison de l'information inexacte fournie par Wargaming. Essayez de fournir un `char de départ` depuis la ligne lorsque vous utilisez la commande si vous le pouvez."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 n'est pas dans l'arbre technologique\n\nVous ne pouvez pas rechercher un char prenium/de collection.",
|
||||
"tier_1": "# %s1 est un char de tier I\n\nLe char de tier 1 sont déverrouillé gratuitement pour tous les joueurs.",
|
||||
"already_researched": "# %s1 est déjà recherché\n\nEssayez de fournir un `char de départ` en utilisant la commande pour voir la progression d'une lignée.",
|
||||
"start_end_equal": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -380,7 +365,7 @@
|
||||
},
|
||||
"embeds": {
|
||||
"name": "intègre",
|
||||
"description": "How embeds work"
|
||||
"description": "Comment fonctionnent les intégrations"
|
||||
},
|
||||
"introduction": {
|
||||
"name": "introduction",
|
||||
@@ -402,14 +387,14 @@
|
||||
},
|
||||
"breakdown": {
|
||||
"name": "décomposition",
|
||||
"description": "A period's breakdown by tanks played",
|
||||
"description": "Répartition d'une période par chars joués",
|
||||
"body": {
|
||||
"total": "Total",
|
||||
"battles": "Batailles",
|
||||
"wn8": "WN8",
|
||||
"winrate": "Taux de victoires",
|
||||
"damage": "Dégâts",
|
||||
"unknown_tank": "Char %s1 inconnu"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "seuil",
|
||||
"description": "Le nombre de jours inactifs (par défaut: %s1 jours)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Inactif depuis %s1+ de jours",
|
||||
"listing": "%s1 jour"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -471,7 +456,7 @@
|
||||
"accuracy": "Précision"
|
||||
},
|
||||
"lethality": {
|
||||
"title": "Lethality",
|
||||
"title": "Létalité",
|
||||
"average_damage": "Dégâts moyens",
|
||||
"damage_per_tier": "Dégâts par tier",
|
||||
"damage_ratio": "Rapport de dégâts",
|
||||
@@ -493,26 +478,26 @@
|
||||
"body": {
|
||||
"accounts_linked": "Comptes liés",
|
||||
"no_role_permissions": {
|
||||
"title": "La vérification de %s1 a échoué",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "Je n'ai pas l'autorisation de gérer les rôles."
|
||||
}
|
||||
},
|
||||
"embed": {
|
||||
"title": "Compte invalide ?",
|
||||
"description": "Use the command again and select a username from the search results."
|
||||
"description": "Utilisez à nouveau la commande et sélectionnez un nom d'utilisateur à partir des résultats de recherche."
|
||||
}
|
||||
},
|
||||
"search_clans": {
|
||||
"name": "search-clans",
|
||||
"description": "Search clans",
|
||||
"name": "clans de recherche",
|
||||
"description": "Rechercher des clans",
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "The max number of clan results (default: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Clan search for %s1 in region %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "Pas de clan trouvé"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "Le nombre maximum de résultats de joueur (par défaut: %s1)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Recherche de %s1 dans la région %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "Pas de joueur de trouvé"
|
||||
}
|
||||
},
|
||||
@@ -535,77 +520,77 @@
|
||||
"description": "Statistiques des batailles de cotés",
|
||||
"subcommands": {
|
||||
"neighbors": {
|
||||
"name": "neighbors",
|
||||
"description": "Neighboring players on the leaderboard"
|
||||
"name": "voisins",
|
||||
"description": "Joueurs voisins dans le classement"
|
||||
},
|
||||
"league": {
|
||||
"name": "ligue",
|
||||
"description": "Top players of a league"
|
||||
"description": "Meilleurs joueurs d'une ligue"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "How many neighbors to display (default: %s1)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"description": "Season number (default: current)",
|
||||
"name": "saison",
|
||||
"description": "Numéro de saison (défaut : courant)",
|
||||
"choices": {
|
||||
"current": "%s1 (current)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
"name": "ligue",
|
||||
"description": "The league to display"
|
||||
"description": "La ligue à afficher"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Top %s1 players",
|
||||
"no_participation": "# %s1 didn't play rating in season %s2\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||
"deleted_player": "Deleted %s1",
|
||||
"neighbors": "Rating neighbors",
|
||||
"more": "+ %s1 more"
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "Pas de saison en cours\n\nIl n'y a pas de saison de notation en cours actuellement.",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "évaluer les voisins",
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
"name": "aujourd'hui",
|
||||
"description": "Today's played tanks and statistics"
|
||||
"description": "Les chars et les statistiques joués aujourd'hui"
|
||||
},
|
||||
"stats": {
|
||||
"name": "statistiques",
|
||||
"description": "Quick statistics for a period"
|
||||
"description": "Statistiques rapides pour une période"
|
||||
},
|
||||
"owned_tanks": {
|
||||
"name": "owned-tanks",
|
||||
"description": "Shows all own tanks in a tier",
|
||||
"name": "chars possédés",
|
||||
"description": "Affiche tous les chars d'un niveau",
|
||||
"body": {
|
||||
"title": "# %s1's owned tier %s2 tanks\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permissions",
|
||||
"description": "Checks wether the bot has the required permissions to function",
|
||||
"description": "Vérifie si le bot a les permissions nécessaires pour fonctionner",
|
||||
"body": {
|
||||
"title": "# Permissions\n\n%s1",
|
||||
"view_channels": "View channels: needed to find the message the refresh button was used on",
|
||||
"read_message_history": "Read message history: needed to find the message to refresh",
|
||||
"attach_files": "Attach files: needed to add images to image-based commands",
|
||||
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"view_channels": "Afficher les canaux : nécessaire pour trouver le message sur lequel le bouton d'actualisation a été utilisé",
|
||||
"read_message_history": "Lire l'historique des messages : nécessaire pour trouver le message à rafraîchir",
|
||||
"attach_files": "Joindre des fichiers : nécessaire pour ajouter des images à des commandes basées sur l'image",
|
||||
"external_emojis": "Émoticônes externes: nécessaire pour ajouter des drapeaux nationaux et des émojis de classe de char"
|
||||
}
|
||||
},
|
||||
"ping": {
|
||||
"name": "ping",
|
||||
"description": "Check wether the bot is alive.",
|
||||
"description": "Vérifiez si le bot est en vie.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
"name": "blitzkit",
|
||||
"description": "Ping BlitzKit"
|
||||
"description": "Ping blitzkit"
|
||||
},
|
||||
"wotb": {
|
||||
"name": "wotb",
|
||||
@@ -614,10 +599,10 @@
|
||||
}
|
||||
},
|
||||
"player_info": {
|
||||
"name": "player-info",
|
||||
"description": "Basic information about a player",
|
||||
"name": "info-joueur",
|
||||
"description": "Informations de base sur un joueur",
|
||||
"body": {
|
||||
"title": "%s1's information",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Nickname",
|
||||
"battles": "Batailles",
|
||||
"winrate": "Taux de victoires",
|
||||
@@ -635,7 +620,7 @@
|
||||
"description": "Today's rating statistics"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"name": "saison",
|
||||
"description": "This season's rating statistics"
|
||||
}
|
||||
},
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s1 hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Development mode",
|
||||
"language": "Language"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Copied!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Search tanks...",
|
||||
"recent": "Recently viewed",
|
||||
"select_all": "Select all",
|
||||
"count_singular": "1 tank",
|
||||
"count_plural": "%s1 tanks",
|
||||
"sorting_by": "Sorting by %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "No tanks found.",
|
||||
"search_again": "Try searching again.",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "caliber",
|
||||
"fire.standardPenetration": "standard penetration",
|
||||
"fire.premiumPenetration": "premium penetration",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "damage",
|
||||
"fire.moduleDamage": "module damage",
|
||||
"fire.shellVelocity": "shell velocity",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "By %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Popular tanks",
|
||||
"subtitle": "Last %s1 days"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Other cool projects"
|
||||
},
|
||||
"help_translate": "Please help translate %s1 to %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "No results"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "Find tanks",
|
||||
"description": "Blitz tank encyclopedia",
|
||||
"seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Green penetration",
|
||||
"hide_model_under_armor": "Hide model under armor",
|
||||
"opaque": "Opaque",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Environment",
|
||||
"show_grid": "Show grid"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Download",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Reset"
|
||||
},
|
||||
"advanced": "Advanced highlighting",
|
||||
"shooter": "Shooter:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Primary",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Tracks"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1hp penetration",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Blocked",
|
||||
"ricochet": "Ricochet",
|
||||
"splash": "%s1hp splash"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetration"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Arbre technologique",
|
||||
"battle": "1 battle",
|
||||
"battles": "%s1 battles",
|
||||
"route": "Route %s1 of %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Game mode abilities"
|
||||
},
|
||||
"review": {
|
||||
"title": "Review videos",
|
||||
"disclaimer": "BlitzKit does not endorse any review videos.",
|
||||
"join": "Apply to %s1 YouTuber program"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Configuration",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Characteristics",
|
||||
"compare": {
|
||||
"label": "Compare against",
|
||||
"class": "Tier %s1 %s2",
|
||||
"tier": "All of tier %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "All tanks"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Shell %s1",
|
||||
"at_distance": "At %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Distance",
|
||||
"dispersion_at_distance": "Dispersion at %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Gun flexibility",
|
||||
"speed": "Speed",
|
||||
"terrain_coefficients": "Terrain coefficients",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Effective at 60s",
|
||||
"shells": "Shells",
|
||||
"mostOptimalShellIndex": "Most optimal shell",
|
||||
"shellReloads": "Shell reloads",
|
||||
"shellReload": "Shell reload",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Reload",
|
||||
"intraClip": "Intra-clip",
|
||||
"caliber": "Caliber",
|
||||
"penetration": "Penetration",
|
||||
"damage": "Dégâts",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Clipping potential",
|
||||
"moduleDamage": "Module damage",
|
||||
"explosionRadius": "Explosion radius",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Shell range",
|
||||
"shellCapacity": "Shell capacity",
|
||||
"gunType": "Gun type",
|
||||
"penetrationAt250m": "Penetration at 250m"
|
||||
"penetrationAt250m": "Penetration at 250m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Tank performance statistics",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estimated stats based on %s1 players. Updated %s2 ago.",
|
||||
"hours": "%s1 hours",
|
||||
"minutes": "%s1 minutes"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Player count"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Browse all",
|
||||
"description": "Blitz player avatars",
|
||||
"seo_description": "All World of Tanks Blitz player avatars",
|
||||
"last_updated": "Last updated on %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Search avatars...",
|
||||
"results": "%s1 avatars"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "Pas de joueur de trouvé"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Tracking %s1",
|
||||
"subtitle": "Since %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Reset",
|
||||
"change": "Change"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancel",
|
||||
"success": {
|
||||
"title": "Copy URL",
|
||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Back",
|
||||
"copy": "Copy URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+121
-110
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "test",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "すばしっこい茶色のキツネが怠け者の犬を飛び越した。"
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1hp",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "自動装填砲",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# クランが見つかりませんでした\n\"%s1\"は見つかりませんでした。検索結果からクランを選択してください。",
|
||||
"player_not_found": "# ユーザーが見つかりませんでした\nユーザー「\"%s1\"」は見つかりませんでした。検索結果からユーザーを選択するか、有効な名前を入力してください。`/link`コマンドを使用することも可能です。",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# `/link`を使用して始めましょう\nはじめまして!あなたが誰か、私に教えてください。",
|
||||
"tank_name_not_found": "# 車輌が見つかりませんでした\n\"%s1\"という名前の車輌は見つかりませんでした。",
|
||||
"tank_id_not_found": "# 車輌が見つかりませんでした\n\"%s1\"というIDの車輌は見つかりませんでした。番号ではなく名前で検索してください。",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"no_tank_stats": "# 利用可能な車輌統計がありません\nこのプレイヤーには利用可能な統計がありません。あなたが探しているプレイヤーではない可能性があります。",
|
||||
"get_help": "Discordサーバーに参加する",
|
||||
"uncaught_error": "# BlitzKitがエラーに遭遇しました!\n申し訳ありません。お気軽に[公式Discordサーバー](https://discord.gg/nDt7AjGJQH)にお問い合わせください。",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "tier",
|
||||
"description": "Tier",
|
||||
"choices": {
|
||||
"tier": "Tier %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Career",
|
||||
"today": "Today",
|
||||
"days": "%s1 days",
|
||||
"custom": "%s1 - %s2 days"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "フィルターなし",
|
||||
"tier": "Tier %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "No battles played in this period",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "オークション",
|
||||
"description": "現在オークションで購入可能な車輌",
|
||||
"body": {
|
||||
"title": "Blitzオークション",
|
||||
"subtitle": "%s1 残り• 次の価格 %s2\n今すぐ入札: %s3",
|
||||
"next": "次の価格: %s1",
|
||||
"available": "残り: %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# 進行中のオークションはありません\n現在、オークションで購入可能な車輌はありません。もしこれが間違いの場合、[公式Discardサーバー](https://discord.gg/nDt7AjGJQH)に報告してください。"
|
||||
},
|
||||
"actions": {
|
||||
"bid": "今すぐ入札"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "replay",
|
||||
"description": "Display replay results"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Research",
|
||||
"total": "トータル",
|
||||
"research": "Research: %s1",
|
||||
"upgrades": "Upgrades: %s1",
|
||||
"purchase": "Purchase: %s1",
|
||||
"equipment": "Equipment: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# %s1 is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# %s1 is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
|
||||
"start_end_equal": "# %s1 was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -409,7 +394,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "勝率",
|
||||
"damage": "ダメージ",
|
||||
"unknown_tank": "Unknown tank %s1"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "threshold",
|
||||
"description": "The number of days inactive (default: %s1 days)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Inactive for %s1+ days",
|
||||
"listing": "%s1 days"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,7 +478,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Accounts linked",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 failed to verify",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "I don't have the permission to manage roles."
|
||||
}
|
||||
},
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "The max number of clan results (default: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Clan search for %s1 in region %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "No clans found"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "The max number of player results (default: %s1)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Player search for %s1 in region %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "No players found"
|
||||
}
|
||||
},
|
||||
@@ -546,13 +531,13 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "How many neighbors to display (default: %s1)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"description": "Season number (default: current)",
|
||||
"choices": {
|
||||
"current": "%s1 (current)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Top %s1 players",
|
||||
"no_participation": "# %s1 didn't play rating in season %s2\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||
"deleted_player": "Deleted %s1",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Rating neighbors",
|
||||
"more": "+ %s1 more"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -581,14 +566,14 @@
|
||||
"name": "owned-tanks",
|
||||
"description": "Shows all own tanks in a tier",
|
||||
"body": {
|
||||
"title": "# %s1's owned tier %s2 tanks\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permissions",
|
||||
"description": "Checks wether the bot has the required permissions to function",
|
||||
"body": {
|
||||
"title": "# Permissions\n\n%s1",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"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",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Check wether the bot is alive.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -617,7 +602,7 @@
|
||||
"name": "player-info",
|
||||
"description": "Basic information about a player",
|
||||
"body": {
|
||||
"title": "%s1's information",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Nickname",
|
||||
"battles": "戦闘数",
|
||||
"winrate": "勝率",
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s1 hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "開発者モード",
|
||||
"language": "言語 / Language"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "コピーしました!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "車輌名を入力…",
|
||||
"recent": "最近の閲覧",
|
||||
"select_all": "全て選択",
|
||||
"count_singular": "1 輌",
|
||||
"count_plural": "%s1輌",
|
||||
"sorting_by": ":%s1で並び替え",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "該当する車輌が存在しません。",
|
||||
"search_again": "もう一度検索してください。",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "砲口径",
|
||||
"fire.standardPenetration": "通常弾貫通力",
|
||||
"fire.premiumPenetration": "課金弾貫通力",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "ダメージ",
|
||||
"fire.moduleDamage": "モジュールダメージ",
|
||||
"fire.shellVelocity": "弾速",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "By %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "人気の車輌",
|
||||
"subtitle": "過去 %s1日間"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "他のプロジェクト"
|
||||
},
|
||||
"help_translate": "%s1の %s2への翻訳にご協力ください!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "World of Tanks Blitz全般のツール"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "該当なし"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "戦車を探す",
|
||||
"description": "Blitz戦車百科",
|
||||
"seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "貫通可能域を緑色表示",
|
||||
"hide_model_under_armor": "車輌モデルを非表示",
|
||||
"opaque": "貫通アシストを不透明にする",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "環境",
|
||||
"show_grid": "グリッドを表示"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "保存する",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "リセット"
|
||||
},
|
||||
"advanced": "高度なハイライト",
|
||||
"shooter": "自車両",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "主装甲",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "履帯"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1hp 貫通",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "非貫通",
|
||||
"ricochet": "跳弾",
|
||||
"splash": "%s1hp 爆風ダメージ"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm 装甲",
|
||||
"ricochet_loss": "%s1% penetration"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "技術ツリー",
|
||||
"battle": "1 戦",
|
||||
"battles": "%s1 戦",
|
||||
"route": "ルート %s1 / %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "ゲームモードアビリティ"
|
||||
},
|
||||
"review": {
|
||||
"title": "Review videos",
|
||||
"disclaimer": "BlitzKitはいかなるレビュー動画も後援していません。",
|
||||
"join": "%s1 YouTuberプログラムに応募する"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "構成",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "性能値",
|
||||
"compare": {
|
||||
"label": "比較対象",
|
||||
"class": "Tier %s1 %s2",
|
||||
"tier": "Tier %s1 全車輌",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "全車輌"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Shell %s1",
|
||||
"at_distance": "At %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "距離",
|
||||
"dispersion_at_distance": "%s1m での散布界",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "主砲旋回上限",
|
||||
"speed": "最高速度",
|
||||
"terrain_coefficients": "地形走破能力",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "60秒での有効値",
|
||||
"shells": "砲弾",
|
||||
"mostOptimalShellIndex": "最適な弾数",
|
||||
"shellReloads": "自動装填時間",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "装填時間",
|
||||
"intraClip": "砲弾再装填時間",
|
||||
"caliber": "口径",
|
||||
"penetration": "貫通力",
|
||||
"damage": "ダメージ",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "弾倉火力",
|
||||
"moduleDamage": "モジュールダメージ",
|
||||
"explosionRadius": "爆風半径",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "射程",
|
||||
"shellCapacity": "総弾数",
|
||||
"gunType": "主砲タイプ",
|
||||
"penetrationAt250m": "250m での貫通力"
|
||||
"penetrationAt250m": "250m での貫通力",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "車輌の戦闘統計",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "%s1 人のプレイヤーの推定統計。 %s2前に更新されました。",
|
||||
"hours": "%s1 時間",
|
||||
"minutes": "%s1 分"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "統計期間:"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "全て見る",
|
||||
"description": "Blitzアバター図鑑",
|
||||
"seo_description": "World of Tanks Blitz の 全ての プレイヤーアバター",
|
||||
"last_updated": "最終更新: %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "アバターを検索...",
|
||||
"results": "検索結果: %s1 個"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "プレイヤーが見つかりませんでした"
|
||||
},
|
||||
"controls": {
|
||||
"title": "%s1 を追跡中",
|
||||
"subtitle": "開始日時: %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "リセット",
|
||||
"change": "変更"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancel",
|
||||
"success": {
|
||||
"title": "Copy URL",
|
||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "戻る",
|
||||
"copy": "Copy URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "さあ、BlitzKit を始めよう!",
|
||||
"seo_description": "さあ、BlitzKit を始めよう!"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "ゲーム内のセッション統計",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+123
-112
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "",
|
||||
"long": "To jest długi ciąg testowy. NIGDY NIE ZOBACZYSZ tego w interfejsie użytkownika. Mój ulubiony owoc to %s1, ponieważ ma kolor %s2. Tak bardzo je lubię, że powiem ich nazwę 3 razy! %s1! %s1! %s1! Po raz kolejny to %s2, a nie %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "Szybki brązowy lis przeskoczył nad leniwym psem."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1hp",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Auto loader",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Nie można znaleźć klanu\nNie można znaleźć \"%s1\". Spróbuj wybrać klan z wyników wyszukiwania.",
|
||||
"player_not_found": "# Nie można znaleźć użytkownika\nNie mogłem znaleźć użytkownika \"%s1\". Spróbuj wybrać użytkownika z wyniku wyszukiwania, wpisując poprawną nazwę użytkownika lub używając polecenia `/link`.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# Użyj `/link` aby zacząć\nNie wiem, kim jesteś, ale możesz mi dziś powiedzieć!",
|
||||
"tank_name_not_found": "# Czołg nie został znaleziony\nNie można znaleźć czołgu o nazwie \"%s1\".",
|
||||
"tank_id_not_found": "# Czołg nie został znaleziony\nNie można znaleźć czołgu o ID \"%s1\". Spróbuj wpisać nazwę czołgu zamiast liczby.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"no_tank_stats": "# Brak dostępnych statystyk czołgów\nTen gracz nie ma żadnych dostępnych statystyk. Może to nie być gracz, którego szukasz.",
|
||||
"get_help": "Pomóż serwerowi Discord",
|
||||
"uncaught_error": "# BlitzKit wpadł w błąd!\nPrzepraszamy za to. Możesz [dołączyć do oficjalnego serwera Discord](https://discord.gg/nDt7AjGJQH), aby uzyskać pomoc.",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "poziom",
|
||||
"description": "Poziom",
|
||||
"choices": {
|
||||
"tier": "Poziom %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Kariera",
|
||||
"today": "Dzisiaj",
|
||||
"days": "%s1 dzień",
|
||||
"custom": "%s1 - %s2 dni"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Brak filtrów",
|
||||
"tier": "Poziom %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "Brak bitew w tym okresie",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "aukcja",
|
||||
"description": "Czołgi dostępne na aukcji",
|
||||
"body": {
|
||||
"title": "Aukcja Blitz",
|
||||
"subtitle": "%s1 dostępne • następne ceny %s2\nOferta teraz: %s3",
|
||||
"next": "Następna cena: %s1",
|
||||
"available": "Dostępne: %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# Brak bieżącej aukcji\n\nObecnie nie ma dostępnych czołgów do zakupu na aukcji. Jeśli uważasz, że to błąd, zgłoś go na [oficjalnym serwerze Discord](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Oferta teraz"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "powtórz",
|
||||
"description": "Wyświetl wyniki powtórki"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Rozpocznij badania",
|
||||
"total": "Całkowity",
|
||||
"research": "Badanie: %s1",
|
||||
"upgrades": "Ulepszenie: %s1",
|
||||
"purchase": "Zakup: %s1",
|
||||
"equipment": "Wyposażenie: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# To jest szacunek!\n\nTa lista może zawierać już zbadane czołgi z powodu niedokładnych informacji dostarczanych przez Wargaming. Spróbuj podać `starting-tank` z wiersza podczas używania polecenia, jeśli możesz."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 nie jest na drzewie technologii\n\nNie możesz zbadać czołg premium/kolekcjonerski.",
|
||||
"tier_1": "# %s1 jest czołgami poziomu I\n\nPoziom I są zawsze odblokowane za darmo dla wszystkich graczy.",
|
||||
"already_researched": "# %s1 jest już zbadany\n\nSpróbuj podać `starting-tank` gdy użyjesz komendy aby zobaczyć sumy dla linii, którą już zbadałeś.",
|
||||
"start_end_equal": "# %s1 został dostarczony dwa razy\n\nUpewnij się, że `starting-tank` i `target-tank` są inne.",
|
||||
"tanks_not_on_line": "# %s i %s nie znajdują się w tej samej linii\n\nUpewnij się, że `starting-tank` i `target-tank` znajdują się na tej samej linii drzewa technicznego.",
|
||||
"unordered": "# %s1 wyższy stopień %s2\n\nUpewnij się, że zamiast tego %s3 jest `starting-tank`, ponieważ jest tierowo niższym czołgiem."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -380,7 +365,7 @@
|
||||
},
|
||||
"embeds": {
|
||||
"name": "osadzenie",
|
||||
"description": "Jak działa osadzanie"
|
||||
"description": "Jak działają osadzone"
|
||||
},
|
||||
"introduction": {
|
||||
"name": "wprowadzenie",
|
||||
@@ -409,7 +394,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Śr. wygranych",
|
||||
"damage": "Uszkodzenia",
|
||||
"unknown_tank": "Nieznany czołg %s1"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "threshold",
|
||||
"description": "Liczba dni nieaktywnych (domyślnie: %s dni)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Inactive for %s1+ days",
|
||||
"listing": "%s1 dzień"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,7 +478,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Accounts linked",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 failed to verify",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "I don't have the permission to manage roles."
|
||||
}
|
||||
},
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "The max number of clan results (default: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Clan search for %s1 in region %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "No clans found"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "Maksymalna liczba wyników gracza (domyślnie: %s)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Player search for %s1 in region %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "No players found"
|
||||
}
|
||||
},
|
||||
@@ -546,13 +531,13 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limit",
|
||||
"description": "How many neighbors to display (default: %s1)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"description": "Season number (default: current)",
|
||||
"choices": {
|
||||
"current": "%s1 (current)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Top %s1 players",
|
||||
"no_participation": "# %s1 didn't play rating in season %s2\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||
"deleted_player": "Usunięto %s1",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Rating neighbors",
|
||||
"more": "+ %s1 więcej"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -581,14 +566,14 @@
|
||||
"name": "posiadane czołgi",
|
||||
"description": "Pokazuje wszystkie własne czołgi na poziomie",
|
||||
"body": {
|
||||
"title": "# %s1 posiadany poziom %s2 czołgi\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "uprawnienia",
|
||||
"description": "Sprawdza, czy bot ma wymagane uprawnienia do funkcji",
|
||||
"body": {
|
||||
"title": "# Uprawnienia\n\n%s1",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"view_channels": "Wyświetl kanały: potrzebne do znalezienia wiadomości użyty przycisk odświeżania",
|
||||
"read_message_history": "Przeczytaj historię wiadomości: potrzebne do znalezienia wiadomości do odświeżenia",
|
||||
"attach_files": "Dołącz pliki: potrzebne do dodawania obrazów do komend opartych na obrazach",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Sprawdź, czy bot żyje.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -617,7 +602,7 @@
|
||||
"name": "informacje o graczu",
|
||||
"description": "Podstawowe informacje o graczu",
|
||||
"body": {
|
||||
"title": "Informacje o %s1",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Nick",
|
||||
"battles": "Bitwy",
|
||||
"winrate": "Śr. wygranych",
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s1 nie grał w \n\nTen gracz nie uczestniczył w grze oceniającej w tym sezonie.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Development mode",
|
||||
"language": "Language"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Copied!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Search tanks...",
|
||||
"recent": "Recently viewed",
|
||||
"select_all": "Select all",
|
||||
"count_singular": "1 tank",
|
||||
"count_plural": "%s1 tanks",
|
||||
"sorting_by": "Sorting by %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "No tanks found.",
|
||||
"search_again": "Try searching again.",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "caliber",
|
||||
"fire.standardPenetration": "standard penetration",
|
||||
"fire.premiumPenetration": "premium penetration",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "damage",
|
||||
"fire.moduleDamage": "module damage",
|
||||
"fire.shellVelocity": "shell velocity",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "By %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Popular tanks",
|
||||
"subtitle": "Last %s1 days"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Other cool projects"
|
||||
},
|
||||
"help_translate": "Please help translate %s1 to %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "Brak wyników"
|
||||
},
|
||||
"stats": ""
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "Find tanks",
|
||||
"description": "Blitz tank encyclopedia",
|
||||
"seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Green penetration",
|
||||
"hide_model_under_armor": "Hide model under armor",
|
||||
"opaque": "Opaque",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Environment",
|
||||
"show_grid": "Show grid"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Download",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Reset"
|
||||
},
|
||||
"advanced": "Advanced highlighting",
|
||||
"shooter": "Shooter:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Primary",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Tracks"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1hp penetration",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Blocked",
|
||||
"ricochet": "Ricochet",
|
||||
"splash": "%s1hp splash"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetration"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Tech tree",
|
||||
"battle": "1 battle",
|
||||
"battles": "%s1 battles",
|
||||
"route": "Route %s1 of %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Game mode abilities"
|
||||
},
|
||||
"review": {
|
||||
"title": "Review videos",
|
||||
"disclaimer": "BlitzKit does not endorse any review videos.",
|
||||
"join": "Apply to %s1 YouTuber program"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Configuration",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Characteristics",
|
||||
"compare": {
|
||||
"label": "Compare against",
|
||||
"class": "Tier %s1 %s2",
|
||||
"tier": "All of tier %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "All tanks"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Shell %s1",
|
||||
"at_distance": "At %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Distance",
|
||||
"dispersion_at_distance": "Dispersion at %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Gun flexibility",
|
||||
"speed": "Speed",
|
||||
"terrain_coefficients": "Terrain coefficients",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Effective at 60s",
|
||||
"shells": "Shells",
|
||||
"mostOptimalShellIndex": "Most optimal shell",
|
||||
"shellReloads": "Shell reloads",
|
||||
"shellReload": "Shell reload",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Reload",
|
||||
"intraClip": "Intra-clip",
|
||||
"caliber": "Caliber",
|
||||
"penetration": "Penetration",
|
||||
"damage": "Uszkodzenia",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Clipping potential",
|
||||
"moduleDamage": "Module damage",
|
||||
"explosionRadius": "Explosion radius",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Shell range",
|
||||
"shellCapacity": "Shell capacity",
|
||||
"gunType": "Gun type",
|
||||
"penetrationAt250m": "Penetration at 250m"
|
||||
"penetrationAt250m": "Penetration at 250m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Tank performance statistics",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estimated stats based on %s1 players. Updated %s2 ago.",
|
||||
"hours": "%s1 hours",
|
||||
"minutes": "%s1 minutes"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Player count"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Browse all",
|
||||
"description": "Blitz player avatars",
|
||||
"seo_description": "All World of Tanks Blitz player avatars",
|
||||
"last_updated": "Last updated on %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Search avatars...",
|
||||
"results": "%s1 avatars"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "No players found"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Tracking %s1",
|
||||
"subtitle": "Since %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Reset",
|
||||
"change": "Change"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancel",
|
||||
"success": {
|
||||
"title": "Copy URL",
|
||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Back",
|
||||
"copy": "Copy URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+122
-111
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "teste",
|
||||
"long": "Esta é uma sequência de testes longa. Você NUNCA DEVE ver isto na UI. Minha fruta favorita é %s1 porque tem a cor de %s2. Eu gosto tanto dela que vou dizer o nome dela 3 vezes! %s1! %s1! %s1! Mais uma vez, é %s2, não %s3.",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "A ágil raposa castanha salta por cima do cão preguiçoso."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1m",
|
||||
"mm": "%s1mm",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1hp",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Autocarregador",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Não foi possível encontrar o clã\nNão foi possível encontrar \"%s1\". Tente selecionar um clã do resultado de pesquisa.",
|
||||
"player_not_found": "# Não foi possível encontrar o usuário\nEu não consegui encontrar o usuário \"%s1\". Tente escolher um usuário do resultado de pesquisa, digitando um nome de usuário válido, ou usando o comando `/link`.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "Digite \"/link\" para começar.\nEu posso não saber quem é você, mas pode-me dizer hoje sobre isso!",
|
||||
"tank_name_not_found": "# Tanque não encontrado\nNão foi possível encontrar um tanque pelo nome \"%s1\".",
|
||||
"tank_id_not_found": "# Tanque não encontrado\nNão foi possível encontrar o tanque pelo ID \"%s1\". Tente digitar o nome do tanque ao invés de um número.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"no_tank_stats": "# Nenhuma estatística de tanques disponível\nEsse jogador não tem as suas estatísticas disponíveis. Talvez esse pode não ser o jogador que procura.",
|
||||
"get_help": "Servidor de Ajuda no Discord",
|
||||
"uncaught_error": "# O BlitzKit apresentou um erro!\nSentimos muito por isso. Sinta-se a vontade para [entrar no servidor oficial no Discord]\n(https://discord.gg/nDt7AjGJQH) para conseguir ajuda.",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "nível",
|
||||
"description": "Nível",
|
||||
"choices": {
|
||||
"tier": "Nível %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Carreira",
|
||||
"today": "Hoje",
|
||||
"days": "%s1 dias",
|
||||
"custom": "%s1 - %s2 dias"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Sem filtros",
|
||||
"tier": "Nível %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "Não há batalhas jogadas neste período",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "leilão",
|
||||
"description": "Tanques disponíveis agora mesmo no leilão",
|
||||
"body": {
|
||||
"title": "Leilão Blitz",
|
||||
"subtitle": "%s1 disponível • próximos preços %s2 oferta agora: %s3",
|
||||
"next": "Próximo preço: %s1",
|
||||
"available": "Disponível: %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# Nenhum leilão em curso\n\nNo momento, não há tanques disponíveis para compra no leilão. Se você acha que isso é um erro, relate esta discrepância no [servidor oficial do Discord](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Ofertar agora"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "replay",
|
||||
"description": "Exibir resultados de replays"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Pesquisa",
|
||||
"total": "Total",
|
||||
"research": "Pesquisa: %s1",
|
||||
"upgrades": "Melhorias: %s1",
|
||||
"purchase": "Compra: %s1",
|
||||
"equipment": "Equipamento: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# Esta é uma estimativa!\n\nEsta lista pode incluir tanques já pesquisados devido à informação imprecisa que a Wargaming fornece. Tente fornecer um `tanque-inicial` da linha ao usar o comando, se puder."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 não está na árvore tecnológica\n\nVocê não pode pesquisar um tanque premium/colecionador.",
|
||||
"tier_1": "# %s1 é um nível I\n\nOs tanques de nível I sempre são desbloqueados gratuitamente para todos os jogadores.",
|
||||
"already_researched": "# %s1 já foi pesquisado\n\nTente fornecer um `tanque-inicial` ao usar o comando para ver os totais de uma linha que você já pesquisou.",
|
||||
"start_end_equal": "# %s1 foi fornecido duas vezes\n\nCertifique-se de que `tanque-inicial` e `tanque-alvo` são diferentes.",
|
||||
"tanks_not_on_line": "# %s1 e %s2 não estão na mesma linha\n\nCertifique-se de que `tanque-inicial` e `tanque-alvo` estão na mesma linha tecnológica.",
|
||||
"unordered": "# %s1 de nível mais alto para %s2\n\nCertifique-se de fornecer %s3 como o `tanque-inicial` em vez disso, pois é o tanque de nível inferior."
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -409,7 +394,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Taxa de vitória",
|
||||
"damage": "Dano",
|
||||
"unknown_tank": "Tanque desconhecido %s1"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "limite",
|
||||
"description": "O número de dias inativos (padrão: %s1 dias)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Inativo por %s1+ dias",
|
||||
"listing": "%s1 dias"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,7 +478,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Contas vinculadas",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 falhou ao verificar",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "Eu não tenho permissão para gerenciar funções."
|
||||
}
|
||||
},
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "O número máximo de resultados do clã (padrão: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Pesquisa do clã para %s1 na região %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "Nenhum clã encontrado"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "O número máximo de resultados do jogador (padrão: %s1)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Busca de jogador para %s1 na região %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "Nenhum jogador encontrado"
|
||||
}
|
||||
},
|
||||
@@ -546,13 +531,13 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "limite",
|
||||
"description": "Quantos vizinhos exibir (padrão: %s1)"
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "temporada",
|
||||
"description": "Número da temporada (padrão: atual)",
|
||||
"choices": {
|
||||
"current": "%s1 (atual)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Melhores %s1 jogadores",
|
||||
"no_participation": "# %s1 não jogou classificação na temporada %s2\n\nEste jogador não participou nesta temporada ou não passou da calibração.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# Nenhuma temporada em andamento\n\nNão há uma temporada de classificação em execução no momento.",
|
||||
"deleted_player": "Excluído %s1",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Classificação de vizinhos",
|
||||
"more": "+ %s1 mais"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -581,14 +566,14 @@
|
||||
"name": "tanques-possuídos",
|
||||
"description": "Mostra todos os tanques possuídos em um nível",
|
||||
"body": {
|
||||
"title": "# %s1's possuía nível %s2 tanques\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "Permissões",
|
||||
"description": "Verifica se o bot tem as permissões necessárias para funcionar",
|
||||
"body": {
|
||||
"title": "# Permissões\n\n%s1",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"view_channels": "Ver canais: necessário para encontrar a mensagem em que o botão atualizar foi usado",
|
||||
"read_message_history": "Histórico de mensagens lidas: necessário para encontrar a mensagem para atualizar",
|
||||
"attach_files": "Anexar arquivos: necessário para adicionar imagens a comandos baseados em imagens",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Verifique se o bot está vivo.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -617,7 +602,7 @@
|
||||
"name": "informação do jogador",
|
||||
"description": "Informações básicas sobre um jogador",
|
||||
"body": {
|
||||
"title": "%s1's informações",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Apelido",
|
||||
"battles": "Batalhas",
|
||||
"winrate": "Taxa de vitória",
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s1 não jogou classificação\n\nEste jogador não participou do modo de classificação nesta temporada.",
|
||||
"no_participation": "# {name} hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
|
||||
"unrecorded_stats": "# Estatísticas atualmente indisponível\n\nWargaming pode estar no momento, enfrentando tempo de inatividade do servidor. Se houver uma temporada em curso, as estatísticas deverão ser disponibilizadas dentro de 24 horas."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Modo de desenvolvimento",
|
||||
"language": "Idioma"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Copiado!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Procurar tanques...",
|
||||
"recent": "Visto recentemente",
|
||||
"select_all": "Selecionar todos",
|
||||
"count_singular": "1 tanque",
|
||||
"count_plural": "%s1 tanques",
|
||||
"sorting_by": "Ordenando por %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "Nenhum tanque encontrado.",
|
||||
"search_again": "Tente pesquisar novamente.",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "calibre",
|
||||
"fire.standardPenetration": "penetração padrão",
|
||||
"fire.premiumPenetration": "penetração premium",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "dano",
|
||||
"fire.moduleDamage": "dano a módulos",
|
||||
"fire.shellVelocity": "velocidade do projétil",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "Por %s1 ❤️"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Tanques populares",
|
||||
"subtitle": "Últimos %s1 dias"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Outros projetos legais"
|
||||
},
|
||||
"help_translate": "Por favor, ajude a traduzir %s1 para %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Ferramentas para tudo World of Tanks Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "Sem resultados"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "Encontrar tanques",
|
||||
"description": "Blitz enciclopédia de tanques",
|
||||
"seo_description": "Estatísticas, blindagem, e mais para todos os tanques de Wolrd of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - Nível %s2 %s3 %s4 | %s5 Tanquepedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 blindagem, estatísticas, equipamentos, provisões e mais. %s2 DPM, %s3hp alfa, %s4mm de penetração, %s5km/h velocidade máxima, %s6hp vida.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Penetração verde",
|
||||
"hide_model_under_armor": "Ocultar modelo sob blindagem",
|
||||
"opaque": "Opaco",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Ambiente",
|
||||
"show_grid": "Exibir grade"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Download",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Restaurar"
|
||||
},
|
||||
"advanced": "Realce avançado",
|
||||
"shooter": "Atirador:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Primário",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Esteiras"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1HP penetração",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Bloqueado",
|
||||
"ricochet": "Ricochete",
|
||||
"splash": "%s1hp respingo"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetração"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Árvore tecnológica",
|
||||
"battle": "1 batalha",
|
||||
"battles": "%s1 batalhas",
|
||||
"route": "Rota %s1 de %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Habilidades do modo de jogo"
|
||||
},
|
||||
"review": {
|
||||
"title": "Vídeos de análise",
|
||||
"disclaimer": "BlitzKit não aprova nenhum vídeo de análise.",
|
||||
"join": "Candidate-se para %s1 um programa YouTuber"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Configuração",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Características",
|
||||
"compare": {
|
||||
"label": "Comparar com",
|
||||
"class": "Nível %s1 %s2",
|
||||
"tier": "Tudo de nível %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "Todos os tanques"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Projétil %s1",
|
||||
"at_distance": "À %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Distância",
|
||||
"dispersion_at_distance": "Dispersão à %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Flexibilidade da arma",
|
||||
"speed": "Velocidade",
|
||||
"terrain_coefficients": "Coeficientes do terreno",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Efetivo aos 60s",
|
||||
"shells": "Projéteis",
|
||||
"mostOptimalShellIndex": "Projétil mais ideal",
|
||||
"shellReloads": "Recarga de projétil",
|
||||
"shellReload": "Recarregamento de projétil",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Recarga",
|
||||
"intraClip": "Intra-clipe",
|
||||
"caliber": "Calibre",
|
||||
"penetration": "Penetração",
|
||||
"damage": "Dano",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Potencial de clipe",
|
||||
"moduleDamage": "Dano a módulos",
|
||||
"explosionRadius": "Raio de Explosão",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Alcance de projétil",
|
||||
"shellCapacity": "Capacidade de projéteis",
|
||||
"gunType": "Tipo de arma",
|
||||
"penetrationAt250m": "Penetração à 250m"
|
||||
"penetrationAt250m": "Penetração à 250m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Estatísticas de desempenho do tanque",
|
||||
"seo_description": "Melhores tanques e suas performances no World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estatísticas estimadas baseadas em %s1 jogadores. Atualizado %s2 atrás.",
|
||||
"hours": "%s1 horas",
|
||||
"minutes": "%s1 minutos"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Número de jogadores"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Procurar todos",
|
||||
"description": "Avatares do jogador Blitz",
|
||||
"seo_description": "Todos os avatares do jogador World of Tanks Blitz",
|
||||
"last_updated": "Última atualização em %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Procurar avatares...",
|
||||
"results": "%s1 avatares"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "Nenhum jogador encontrado"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Rastreando %s1",
|
||||
"subtitle": "Desde %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Restaurar",
|
||||
"change": "Mudança"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancelar",
|
||||
"success": {
|
||||
"title": "Copiar URL",
|
||||
"info": "Gerou a URL para %s1. A incorporação começará a rastrear assim que a URL for colada. Redefina a qualquer momento clicando com o botão direito no PC ou pressionando longamente no celular e, em seguida, selecionando \"Redefinir\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Voltar",
|
||||
"copy": "Copiar URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Comece com BlitzKit",
|
||||
"seo_description": "Comece com BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "AnaliseBlitz[]",
|
||||
"button": "Analise agora",
|
||||
"description": "Estatísticas médias históricas ricas",
|
||||
"seo_description": "Estatísticas médias históricas ricas no World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Assistente Blitz",
|
||||
"button": "Descubra hoje",
|
||||
"description": "Estatísticas da temporada dentro do jogo",
|
||||
"seo_description": "Estatísticas da sessão no jogo World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+132
-121
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "тест",
|
||||
"long": "Это длинная тестовая строка. Вы НИКОГДА не должны видеть её в пользовательском интерфейсе. Мой любимый фрукт - %s1, потому что его цвет %s2. Он мне так нравится, что я произнесу его имя 3 раза! %s1! %s1! %s1! И ещё раз, это %s2, а не %s3.",
|
||||
"fox": "Это длинная тестовая строка. Вы НИКОГДА не должны видеть её в пользовательском интерфейсе. Мой любимый фрукт - %s1, потому что его цвет %s2. Он мне так нравится, что я произнесу его имя 3 раза! %s1! %s1! %s1! И ещё раз, это %s2, а не %s3."
|
||||
"long": "Это длинная тестовая строка. Вы НИКОГДА не должны видеть её в пользовательском интерфейсе. Мой любимый фрукт — {fruit}, потому что это цвет {color}. Он мне так нравится, так что я скажу его 3 раза {fruit}! {fruit}! {fruit}! И ещё раз, это {color}, а не {not_color}.",
|
||||
"fox": "Съешь же ещё этих мягких французских булок, да выпей чаю."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1м",
|
||||
"mm": "%s1мм",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1hp",
|
||||
"x": "x%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/с"
|
||||
"m": "{value}м",
|
||||
"mm": "{value}мм",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}оп",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/с"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Барабан",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Не удалось найти клан\nНе найден «%s1». Попробуйте выбрать клан из результатов поиска.",
|
||||
"player_not_found": "# Не удалось найти пользователя\nНе удалось найти пользователя «%s1». Попробуйте выбрать пользователя из результатов поиска, введя правильное имя пользователя или используя команду «/link».",
|
||||
"clan_not_found": "# Не удалось найти клан\nНе найден «{search}». Попробуйте выбрать клан из результатов поиска.",
|
||||
"player_not_found": "# Не удалось найти пользователя\nНе удалось найти пользователя «{search}». Попробуйте выбрать пользователя из результатов поиска, введя правильное имя пользователя или используя команду «/link».",
|
||||
"player_not_linked": "# Используйте «/link», чтобы начать\nЯ не знаю, кто ты, но давай познакомимся сегодня!",
|
||||
"tank_name_not_found": "# Танк не найден\nНе удалось найти танк по названию «%s1».",
|
||||
"tank_id_not_found": "# Танк не найден\nНе удалось найти танк по ID «%s1». Попробуйте ввести название танка вместо числа.",
|
||||
"tank_name_not_found": "# Танк не найден\nНе удалось найти танк с названием «{search}».",
|
||||
"tank_id_not_found": "# Танк не найден\nНе удалось найти танк с ID «{search}». Попробуйте ввести название танка вместо числа.",
|
||||
"no_tank_stats": "# Статистика танка недоступна\nУ этого игрока нет доступной статистики. Возможно, это не тот игрок, которого вы ищете.",
|
||||
"get_help": "Получить помощь",
|
||||
"uncaught_error": "# BlitzKit столкнулся с ошибкой!\nНам очень жаль. Не стесняйтесь и [присоединяйтесь к официальному серверу Discord](https://discord.gg/nDt7AjGJQH), чтобы получить помощь.",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "уровень",
|
||||
"description": "Уровень",
|
||||
"choices": {
|
||||
"tier": "Уровень %s - %s"
|
||||
"tier": "Уровень {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Карьера",
|
||||
"today": "Cегодня",
|
||||
"days": "%s дней",
|
||||
"custom": "%s - %s дней"
|
||||
"days": "{days} дней",
|
||||
"custom": "{day_1} - {day_2} дней"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Без фильтров",
|
||||
"tier": "Уровень %s1"
|
||||
"tier": " {tier} уровня"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "За этот период боёв не было",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "аукцион",
|
||||
"description": "Танки доступные на аукционе сейчас",
|
||||
"body": {
|
||||
"title": "Blitz аукцион",
|
||||
"subtitle": "%s доступные • следующие цены %s\nПокупка сейчас: %s",
|
||||
"next": "Следующая цена: %s",
|
||||
"available": "Доступно: %s / %s"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# Аукцион сейчас не проходит\n\nНа данный момент нет танков для покупки на аукционе. Если считаете, что это ошибка, пожалуйста о несоответствии на [официальном сервере Discord](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Купить сейчас"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "повтор",
|
||||
"description": "Показать результаты повтора"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Исследование",
|
||||
"total": "Всего",
|
||||
"research": "Исследование: %s",
|
||||
"upgrades": "Улучшения: %s",
|
||||
"purchase": "Покупка: %s",
|
||||
"equipment": "Снаряжение: %s",
|
||||
"research": "Исследование: {cost}",
|
||||
"upgrades": "Улучшения: {cost}",
|
||||
"purchase": "Покупка: {cost}",
|
||||
"equipment": "Снаряжение: {cost}",
|
||||
"estimation": "# Это приблизительная оценка!\n\nВ этот список могут входить уже исследованные танки из-за неточной информации, предоставляемой Wargaming. Попробуйте указать «стартовый-танк» в строке при использовании команды, если сможете."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s отсутствует в дереве танков\n\nВы не можете исследовать премиум / коллекционный танк.",
|
||||
"tier_1": "# %s - это уровень I\n\nУровень I всегда открыт бесплатно для всех игроков.",
|
||||
"already_researched": "# %s уже исследован\n\nПопробуйте предоставить «starting-tank» при использовании команды для просмотра итогов уже исследованной ветки.",
|
||||
"start_end_equal": "# %s был предоставлен дважды\n\nУбедитесь, что «starting-tank» и «target-tank» различаются.",
|
||||
"tanks_not_on_line": "# %s и %s находятся не в одной ветке\n\nУбедитесь, что «starting-tank» и «target-tank» находятся на той же ветке дерева танков.",
|
||||
"unordered": "# У %s более высокий уровень чем у %s\n\nОбязательно укажите %s в качестве «starting-tank», так как это танк более низкого уровня."
|
||||
"non_tech_tree": "# {tank} отсутствует в дереве танков\n\nВы не можете исследовать премиумный / коллекционный танк.",
|
||||
"tier_1": "# {tank} — танк I уровня\n\nУровень I всегда открыт бесплатно для всех игроков.",
|
||||
"already_researched": "# {tank} уже исследован\n\nПопробуйте предоставить «starting-tank» при использовании команды для просмотра итогов уже исследованной ветки.",
|
||||
"start_end_equal": "# {tank} был предоставлен дважды\n\nУбедитесь, что «starting-tank» и «target-tank» различаются.",
|
||||
"tanks_not_on_line": "# {tank_1} и {tank_2} находятся не в одной ветке\n\nУбедитесь, что «starting-tank» и «target-tank» находятся на той же ветке дерева танков.",
|
||||
"unordered": "# У {tank_1} более высокий уровень чем у {tank_2}\n\nОбязательно укажите {tank_2} в качестве «starting-tank», так как это танк более низкого уровня."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -409,7 +394,7 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Коэффициент побед",
|
||||
"damage": "Урон",
|
||||
"unknown_tank": "Неизвестный танк %s"
|
||||
"unknown_tank": "Неизвестный танк {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "порог",
|
||||
"description": "Количество дней не активности (по умолчанию: %s дней)"
|
||||
"description": "Количество дней не активности (по умолчанию: {days} дней)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Неактивен %s+ дней",
|
||||
"listing": "%s дней"
|
||||
"subtitle": "Неактивен {days}+ дней",
|
||||
"listing": "{days} дней"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,7 +478,7 @@
|
||||
"body": {
|
||||
"accounts_linked": "Аккаунты привязаны",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 не удалось проверить",
|
||||
"title": "{name} не удалось проверить",
|
||||
"description": "У меня нет разрешения на управление ролями."
|
||||
}
|
||||
},
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "лимит",
|
||||
"description": "Максимальное количество результатов клана (по умолчанию: %s)"
|
||||
"description": "Максимальное количество результатов клана (по умолчанию: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Поиск клана %s в регионе %s",
|
||||
"title": "Поиск клана {search} в регионе {region}",
|
||||
"no_results": "Кланов не найдено"
|
||||
}
|
||||
},
|
||||
@@ -522,11 +507,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "лимит",
|
||||
"description": "Максимальное число результатов игрока (по умолчанию: %s)"
|
||||
"description": "Максимальное число результатов игрока (по умолчанию: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Поиск игрока %s в регионе %s",
|
||||
"title": "Поиск игрока {search} в регионе {region}",
|
||||
"no_results": "Игроков не найдено"
|
||||
}
|
||||
},
|
||||
@@ -546,13 +531,13 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "лимит",
|
||||
"description": "Количество отображаемых ближайших игроков (по умолчанию: %s)"
|
||||
"description": "Количество отображаемых ближайших игроков (по умолчанию: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "сезон",
|
||||
"description": "Номер сезона (по умолчанию: текущий)",
|
||||
"choices": {
|
||||
"current": "%s (текущая)"
|
||||
"current": "{season} (текущий)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Топ %s игроков",
|
||||
"no_participation": "# %s не играл в рейтинг в сезоне %s\n\nЭтот игрок не участвовал в этом сезоне или не получил прошлую калибровку.",
|
||||
"top_players": "Топ {count} игроков",
|
||||
"no_participation": "# {player} не играл в рейтинг в сезоне {season}\n\nЭтот игрок не участвовал в этом сезоне или не получил прошлую калибровку.",
|
||||
"no_ongoing_season": "# Нет текущего сезона\n\nВ данный момент нет рейтингового сезона.",
|
||||
"deleted_player": "Удалено %s",
|
||||
"deleted_player": "Удаленный {id}",
|
||||
"neighbors": "Рейтинг ближайших игроков",
|
||||
"more": "+ ещё %s"
|
||||
"more": "+ ещё {count}"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -581,14 +566,14 @@
|
||||
"name": "cобственные-танки",
|
||||
"description": "Показывает все принадлежащие танки на уровне",
|
||||
"body": {
|
||||
"title": "# %s принадлежал уровень %s танков\n\n"
|
||||
"title": "# {name} принадлежат танки {tier} уровня\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "разрешения",
|
||||
"description": "Проверяет, есть ли у бота необходимые разрешения для работы",
|
||||
"body": {
|
||||
"title": "# Разрешения\n\n%s",
|
||||
"title": "# Разрешения\n\n{body}",
|
||||
"view_channels": "Просмотр каналов: необходимо найти сообщение, на котором была использована кнопка обновления",
|
||||
"read_message_history": "Просмотр истории сообщений: необходимо найти сообщение для обновления",
|
||||
"attach_files": "Прикрепить файлы: необходимо для добавления изображений в команды на основе изображения",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Проверьте, что бот жив.",
|
||||
"body": {
|
||||
"pong": "Понг 🏓",
|
||||
"pong_with_time": "Понг 🏓 - %sмс"
|
||||
"pong_with_time": "Понг 🏓 — {time}мс"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -617,7 +602,7 @@
|
||||
"name": "информация-об-игроке",
|
||||
"description": "Основная информация о игроке",
|
||||
"body": {
|
||||
"title": "Информация о %s",
|
||||
"title": "Информация о {name}",
|
||||
"nickname": "Никнейм",
|
||||
"battles": "Боёв",
|
||||
"winrate": "Коэффициент побед",
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s не получил рейтинга\n\nЭтот игрок в этом сезоне не участвовал в рейтинговом режиме.",
|
||||
"no_participation": "# {name} не получил рейтинга\n\nЭтот игрок в этом сезоне не участвовал в рейтинговом режиме.",
|
||||
"unrecorded_stats": "# Статистика недоступна в настоящее время\n\nВ настоящее время Wargaming, возможно, сталкивается с простоями сервера. Если сезон продолжается, статистика должна быть доступна в течение 24 часов."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "Эта страница перемещена. Перенаправление..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Режим разработчика",
|
||||
"language": "Язык"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Скопировано!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Поиск танка",
|
||||
"swap_dialog_title": "Заменить танк",
|
||||
"clear_filters": "Сбросить фильтры",
|
||||
"login": "Войти, чтобы воспользоваться",
|
||||
"search_bar_hint": "Поиск танков...",
|
||||
"recent": "Недавние",
|
||||
"select_all": "Выбрать всё",
|
||||
"count_singular": "1 танк",
|
||||
"count_plural": "%s1 танк",
|
||||
"sorting_by": "Сортировать по %s1",
|
||||
"count_plural": "Танков: {count}",
|
||||
"sorting_by": "Сортировать по {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "Танков не найдено.",
|
||||
"search_again": "Попробуйте повторить поиск.",
|
||||
@@ -693,11 +683,12 @@
|
||||
"survivability.volume": "объём",
|
||||
"survivability.length": "длина",
|
||||
"fire.dpm": "базовый ДПМ",
|
||||
"fire.dpmPremium": "улучшенный ДПМ",
|
||||
"fire.dpmPremium": "премиумный ДПМ",
|
||||
"fire.reload": "перезарядка",
|
||||
"fire.caliber": "калибр",
|
||||
"fire.standardPenetration": "базовое пробитие",
|
||||
"fire.premiumPenetration": "улучшенное пробитие ",
|
||||
"fire.premiumPenetration": "премиумное пробитие ",
|
||||
"fire.tertiaryPenetration": "пробитие третьего снаряда",
|
||||
"fire.damage": "урон",
|
||||
"fire.moduleDamage": "урон по модулям",
|
||||
"fire.shellVelocity": "скорость снаряда",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Установите приложение BlitzKit",
|
||||
"new": "НОВИНКА",
|
||||
"checking": "Проверка совместимости...",
|
||||
"instructions": "Инструкция для устройств Apple.",
|
||||
"install": "Установка",
|
||||
"dismiss": "Отклонить"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Присоединяйтесь к крутым ребятам!",
|
||||
"join_long": "Присоединяйся к супер-дупер крутым ребятам!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "От %s1 ❤️"
|
||||
"credit": "От {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Популярные танки",
|
||||
"subtitle": "За последнее %s1 дней"
|
||||
"subtitle": "За последнее {days} дней"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Другие классные проекты"
|
||||
},
|
||||
"help_translate": "Пожалуйста, помогите перевести %s1 на %s2!",
|
||||
"help_translate": "Пожалуйста, помогите перевести {name} на {language}!",
|
||||
"seo_description": "Инструменты для World of Tanks Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,8 +762,16 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Смешать",
|
||||
"button": "Смешать и собрать",
|
||||
"description": "Модули смешанного танка",
|
||||
"seo_description": "Смешивание и сбор корпусов, башен и пушек танков из World of Tanks Blitz",
|
||||
"screenshot": "Скриншот",
|
||||
"randomize": "Случайно"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Угадайте танк",
|
||||
"name": "Угадай танк",
|
||||
"button": "Угадать",
|
||||
"description": "Мини-игра «Силуэт»",
|
||||
"seo_description": "Играйте угадывая силуэт танка из World of Tanks Blitz",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Следующий",
|
||||
"no_results": "Ничего не найденo"
|
||||
},
|
||||
"stats": "%s1 / %s2 угадано верно"
|
||||
"stats": "{correct} / {total} угадано верно"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Поиск характеристик...",
|
||||
@@ -783,15 +790,21 @@
|
||||
"button": "Поиск танков",
|
||||
"description": "Танковая энциклопедия Blitz",
|
||||
"seo_description": "Статистика, броня и многое другое по всем танкам World of Tanks Blitz",
|
||||
"tank_seo_title": "%s1 - %s4 %s3 %s2 уровня | Танкопедия %s5 | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "%s1 броня, статистика, оборудование, снабжение и многое другое. %s2 УВМ, %s3hp альфа, %s4mm пробитие, %s5км/ч лучшая скорость, %s6hp здоровье.",
|
||||
"tank_seo_title": "{name} — {class} {nationality} {tier} уровня | Танкопедия {website} | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "Броня, статистика, оборудование, снабжение {name} и многое другое. DPM {dpm}, альфа {alpha}оп, пробитие{penetration}мм, лучшая скорость{speed}км/ч, здоровье {health}оп.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Пропуск"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Штурмовые снаряды"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Корпус",
|
||||
"turret": "Башня",
|
||||
"hard": "Твёрдые",
|
||||
"medium": "Средний танк",
|
||||
"soft": "На мягком грунте",
|
||||
"medium": "Средние",
|
||||
"soft": "Мягкие",
|
||||
"effective": "Эффективно"
|
||||
},
|
||||
"ricochet": {
|
||||
@@ -802,10 +815,15 @@
|
||||
"no_ricochet": "Разрывные снаряды не рикошетят"
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Камо",
|
||||
"percentage": "%s1%",
|
||||
"camo": "Камуфляж",
|
||||
"ghost_factory": "Промзона",
|
||||
"dead_rail": "Эшелон"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Поворот",
|
||||
"pitch": "Наклон",
|
||||
"min": "Вниз",
|
||||
"max": "Вверх"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Зелёное пробитие",
|
||||
"hide_model_under_armor": "Скрыть модель под броней",
|
||||
"opaque": "Прозрачность",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Окружение",
|
||||
"show_grid": "Показать сетку"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Загрузить",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Сброс"
|
||||
},
|
||||
"advanced": "Продвинутая подсветка",
|
||||
"shooter": "Стреляет:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Броня",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Гусеницы"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "Пробитие на %s1оп",
|
||||
"penetration": "Пробитие на {damage}оп",
|
||||
"blocked": "Не пробил",
|
||||
"ricochet": "Рикошет",
|
||||
"splash": "Урон осколками на %s1оп"
|
||||
"splash": "Урон осколками на {damage}оп"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1мм %s2°",
|
||||
"nominal": "%s1мм номинала",
|
||||
"ricochet_loss": "%s1% пробития"
|
||||
"thickness_and_angle": "{thickness}мм {angle}°",
|
||||
"nominal": "{thickness}1мм номинала",
|
||||
"ricochet_loss": "{loss}% пробития"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -892,7 +907,7 @@
|
||||
"tier": "Уровень",
|
||||
"type": "Тип",
|
||||
"dev_id": "DEV: ID",
|
||||
"purchase": "Стоимость покупки",
|
||||
"purchase": "Цена покупки",
|
||||
"restoration": "Цена восстановления",
|
||||
"sale": "Цена продажи",
|
||||
"research": "Необходимо опыта"
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Дерево танков",
|
||||
"battle": "1 бой",
|
||||
"battles": "%s1 боёв",
|
||||
"route": "Ветка %s1 из %s2"
|
||||
"battles": "{count} боёв",
|
||||
"route": "Ветка {index} из {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Способности в игровых режимах"
|
||||
},
|
||||
"review": {
|
||||
"title": "Видеообзоры",
|
||||
"disclaimer": "BlitzKit не несёт ответственности за видеообзоры.",
|
||||
"join": "Предложить ютубера для программы %s1"
|
||||
"join": "Предложить ютубера для программы {name}"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Конфигурация",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Характеристики",
|
||||
"compare": {
|
||||
"label": "Сравнивать с",
|
||||
"class": "%s2 %s1 уровня",
|
||||
"tier": "Все %s1 уровня",
|
||||
"class": "{class} {tier} уровня",
|
||||
"tier": "Все {tier} уровня",
|
||||
"all": "Все танки"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Снаряд %s1",
|
||||
"at_distance": "На %s1м",
|
||||
"shell_index": "Снаряд {index}",
|
||||
"at_distance": "На {distance}м",
|
||||
"at_distance_slider_label": "Дистанция",
|
||||
"dispersion_at_distance": "Разброс на %s1м",
|
||||
"dispersion_at_distance": "Разброс на {distance}м",
|
||||
"gun_flexibility": "Углы наводки",
|
||||
"speed": "Скорость",
|
||||
"terrain_coefficients": "Сопротивление грунтам",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Эффективность за 60с",
|
||||
"shells": "Снарядов в барабане",
|
||||
"mostOptimalShellIndex": "Самый эффективный снаряд",
|
||||
"shellReloads": "Перезарядка снарядов",
|
||||
"shellReloads": "Перезарядка",
|
||||
"shellReload": "Перезарядка",
|
||||
"intraClip": "Между снарядами",
|
||||
"caliber": "Калибр",
|
||||
"penetration": "Пробитие",
|
||||
"damage": "Урон",
|
||||
"damageWithoutAssault": "Урон без штурмовых снарядов",
|
||||
"clipDamage": "Урон с барабана",
|
||||
"moduleDamage": "Урон по модулям",
|
||||
"explosionRadius": "Разлет осколков",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Дальность снаряда",
|
||||
"shellCapacity": "Боезапас",
|
||||
"gunType": "Тип орудия",
|
||||
"penetrationAt250m": "Пробитие на 250м"
|
||||
"penetrationAt250m": "Пробитие на 250м",
|
||||
"burstShells": "Кол-во разрывных снарядов",
|
||||
"burstInterShell": "Взрыв между снарядами"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Статистика эффективности танков",
|
||||
"seo_description": "Лучшие танки и их эффективность в World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Примерная статистика на основе %s1 игроков. Обновлено %s2 назад.",
|
||||
"hours": "%s1 часов",
|
||||
"minutes": "%s1 минут"
|
||||
"body": "Примерная статистика на основе {count} игроков. Обновлено {age} назад.",
|
||||
"hours": "{count} часов",
|
||||
"minutes": "{count} минут"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Количество игроков"
|
||||
@@ -1124,7 +1141,7 @@
|
||||
"hits": "Уничтожен",
|
||||
"damage_ratio": "Коэф. урона",
|
||||
"damage_taken": "Получено урона",
|
||||
"capture_points": "Ограничительные точки"
|
||||
"capture_points": "Точки ограничения"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Просмотреть",
|
||||
"description": "Аватары игрока Blitz",
|
||||
"seo_description": "Все аватары игрока World of Tanks Blitz",
|
||||
"last_updated": "Последнее обновление %s1",
|
||||
"last_updated": "Последнее обновление {time}",
|
||||
"search": {
|
||||
"hint": "Поиск аватаров...",
|
||||
"results": "%s1 аватар"
|
||||
"results": "Аватаров: {count}"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1145,14 +1162,14 @@
|
||||
"description": "Статистика сеанса игры",
|
||||
"seo_description": "Отслеживайте свою статистику в реальном времени во время игры World of Tanks Blitz",
|
||||
"search": {
|
||||
"lookup": "Искать игрока, чтобы начать",
|
||||
"lookup": "Введите ник игрока",
|
||||
"hint": "Поиск игроков...",
|
||||
"searching": "Поиск...",
|
||||
"no_results": "Игроков не найдено"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Отслеживание %s1",
|
||||
"subtitle": "Начиная с %s1",
|
||||
"title": "Отслеживание {name}",
|
||||
"subtitle": "Начиная с {time}",
|
||||
"reset": "Сброс",
|
||||
"change": "Изменить"
|
||||
},
|
||||
@@ -1186,7 +1203,7 @@
|
||||
"button": "Создать",
|
||||
"description": "Встраиваемые элементы для стримов",
|
||||
"seo_description": "Создать вставки для стримов по World of Tanks Blitz с невероятной кастомизации",
|
||||
"coming_soon": "Скоро появятся новые встраиваемые элементы.",
|
||||
"coming_soon": "В скором времени появятся новые встраиваемые элементы.",
|
||||
"suggest": "Предложить идеи.",
|
||||
"preview": {
|
||||
"title": "Предпросмотр",
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Отмена",
|
||||
"success": {
|
||||
"title": "Скопировать URL",
|
||||
"info": "Сгенерирован URL для %s1. Встройка начнет отслеживание, как только URL будет вставлен. Можно сбросить, щелкнув правой кнопкой мыши на ПК или долгим нажатием на мобильном телефоне и выбором «Сброс».",
|
||||
"info": "Сгенерирован URL для {name}. Встройка начнет отслеживание, как только URL будет вставлен. Можно сбросить, щелкнув правой кнопкой мыши на ПК или долгим нажатием на мобильном телефоне и выбором «Сброс».",
|
||||
"back": "Назад",
|
||||
"copy": "Скопировать URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Начните с BlitzKit",
|
||||
"seo_description": "Начните с BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Анализируй",
|
||||
"description": "Богатая статистика",
|
||||
"seo_description": "Богатые статистика по World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Открой для себя",
|
||||
"description": "Статистика сеанса игры",
|
||||
"seo_description": "Статистика сеанса игры в World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "К документации",
|
||||
"description": "API сервера BlitzKit",
|
||||
"seo_description": "Статический API сервера BlitzKit для World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+138
-127
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"test": {
|
||||
"default": "тест",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.",
|
||||
"fox": "The quick brown fox jumps over the lazy dog."
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
||||
"fox": "Швидка бура лисиця перестрибує через лінивого собаку."
|
||||
},
|
||||
"common": {
|
||||
"name": "BlitzKit",
|
||||
"units": {
|
||||
"m": "%s1м",
|
||||
"mm": "%s1мм",
|
||||
"deg": "%s1°",
|
||||
"hp": "%s1хп",
|
||||
"x": "х%s1",
|
||||
"percentage": "%s1%",
|
||||
"deg_s": "%s1°/s"
|
||||
"m": "{value}m",
|
||||
"mm": "{value}mm",
|
||||
"deg": "{value}°",
|
||||
"hp": "{value}hp",
|
||||
"x": "x{value}",
|
||||
"percentage": "{value}%",
|
||||
"deg_s": "{value}°/s"
|
||||
},
|
||||
"gun_types": {
|
||||
"auto_loader": "Магазинна система заряджання",
|
||||
@@ -185,11 +186,11 @@
|
||||
"bot": {
|
||||
"common": {
|
||||
"errors": {
|
||||
"clan_not_found": "# Не вдалося знайти клан\nНе вдалося знайти клан \"%s1\". Спробуйте вибрати клан з пошукового результату.",
|
||||
"player_not_found": "# Не вдалося знайти користувача\nЯ не зміг знайти користувача \"%s1\". Спробуйте вибрати користувача з результату пошуку, ввести коректний нікнейм, або використати команду '/link'.",
|
||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||
"player_not_linked": "# Використайте команду \"/link\" щоб почати\nЯ не знаю, хто ви, але ви можете сказати мені сьогодні!",
|
||||
"tank_name_not_found": "# Танк не знайдено\nНе вдалось знайти танк за ім'ям \"%s1\".",
|
||||
"tank_id_not_found": "# Танк не знайдено\nНе вдалось знайти танк за ID \"%s1\". Спробуйте ввести назву танка замість числа.",
|
||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
||||
"no_tank_stats": "# Немає статистики для танка\nЦей гравець не має статистики.\nМожливо, це не той гравець, якого ви шукаєте.",
|
||||
"get_help": "Допомогти Discord серверу",
|
||||
"uncaught_error": "# Упс! BlitzKit зіткнувся з помилкою!\nНам дуже шкода. Можете попросити допомоги на [офіційному сервері Discord](https://discord.gg/nDt7AjGJQH).",
|
||||
@@ -283,19 +284,19 @@
|
||||
"name": "рівень",
|
||||
"description": "Рівень",
|
||||
"choices": {
|
||||
"tier": "Рівень %s1 - %s2"
|
||||
"tier": "Tier {tier_1} - {tier_2}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"periods": {
|
||||
"career": "Кар'єра",
|
||||
"today": "Сьогодні",
|
||||
"days": "%s1 днів",
|
||||
"custom": "%s1 - %s2 днів"
|
||||
"days": "{days} days",
|
||||
"custom": "{day_1} - {day_2} days"
|
||||
},
|
||||
"filters": {
|
||||
"none": "Немає фільтрів",
|
||||
"tier": "Рівень %s1"
|
||||
"tier": "Tier {tier}"
|
||||
},
|
||||
"no_data": {
|
||||
"battles_in_period": "За цей період боїв не було",
|
||||
@@ -318,22 +319,6 @@
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"auction": {
|
||||
"name": "аукціон",
|
||||
"description": "Танки доступні зараз на аукціоні",
|
||||
"body": {
|
||||
"title": "Бліц Аукціон",
|
||||
"subtitle": "%s1 доступно • наступна ціна %s2\nПоточна ставка: %s3",
|
||||
"next": "Наступна ціна:%s1",
|
||||
"available": "Доступно: %s1 / %s2"
|
||||
},
|
||||
"errors": {
|
||||
"no_auction": "# Немає поточного аукціону\n\nНаразі немає танків, доступних для купівлі на аукціоні. Якщо ви вважаєте це помилкою, будь ласка, повідомте про цю невідповідність на [офіційний Дискорд сервер](https://discord.gg/nDt7AjGJQH)."
|
||||
},
|
||||
"actions": {
|
||||
"bid": "Поточна ставка"
|
||||
}
|
||||
},
|
||||
"replay": {
|
||||
"name": "реплей",
|
||||
"description": "Показати результати реплею"
|
||||
@@ -354,19 +339,19 @@
|
||||
"body": {
|
||||
"subtitle": "Дослідження",
|
||||
"total": "Загалом",
|
||||
"research": "Дослідження: %s1",
|
||||
"upgrades": "Покращення: %s1",
|
||||
"purchase": "Придбання: %s1",
|
||||
"equipment": "Спорядження: %s1",
|
||||
"research": "Research: {cost}",
|
||||
"upgrades": "Upgrades: {cost}",
|
||||
"purchase": "Purchase: {cost}",
|
||||
"equipment": "Equipment: {cost}",
|
||||
"estimation": "# Це оцінка!\n\nЦей список може включати вже досліджені танки через неточну інформацію для вантажу. Спробуйте використати \"початковий танк\" з рядка за допомогою команди."
|
||||
},
|
||||
"errors": {
|
||||
"non_tech_tree": "# %s1 не в технічному дереві\n\nВи не можете досліджувати преміум/колекційний танк.",
|
||||
"tier_1": "# %s1 це рівень I\n\nТанки рівню I завжди відкриті для всіх гравців безкоштовно.",
|
||||
"already_researched": "# %s1 уже досліджено!\n\nСпробуйте забезпечити `starting-tank` коли використовуєте команду щоб подивитись підсумки гілки, яку ви вже дослідили.",
|
||||
"start_end_equal": "# %s1 було надано двічі\n\nПереконайся, що `starting-tank` та `target-tank` різні.",
|
||||
"tanks_not_on_line": ".",
|
||||
"unordered": ""
|
||||
"non_tech_tree": "# {tank} is not on the tech tree\n\nYou cannot research a premium/collector tank.",
|
||||
"tier_1": "# {tank} is a tier I\n\nTier I tanks are always unlocked for free for all players.",
|
||||
"already_researched": "# {tank} 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": "# {tank} was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
|
||||
"tanks_not_on_line": "# {tank_1} and {tank_2} are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
||||
"unordered": "# {tank_1} higher tiered to {tank_2}\n\nMake sure to provide {tank_2} as the `starting-tank` instead as it is the lower tiered tank."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
@@ -409,14 +394,14 @@
|
||||
"wn8": "WN8",
|
||||
"winrate": "Відсоток перемог",
|
||||
"damage": "Шкода",
|
||||
"unknown_tank": "Невідомий танк %s1"
|
||||
"unknown_tank": "Unknown tank {id}"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"name": "debug",
|
||||
"description": "Debug information about the bot",
|
||||
"name": "налагодження",
|
||||
"description": "Інформація про налагодження бота",
|
||||
"body": {
|
||||
"title": "# Debug\n\n",
|
||||
"title": "# Налагодження\n\n",
|
||||
"version": "Версія",
|
||||
"shard": {
|
||||
"label": "Уламок",
|
||||
@@ -445,12 +430,12 @@
|
||||
"options": {
|
||||
"threshold": {
|
||||
"name": "поріг",
|
||||
"description": "Кількість днів неактивності (за замовчуванням: %s1 днів)"
|
||||
"description": "The number of days inactive (default: {days} days)"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"subtitle": "Неактивний %s1+ днів",
|
||||
"listing": "%s1 днів"
|
||||
"subtitle": "Inactive for {days}+ days",
|
||||
"listing": "{days} days"
|
||||
}
|
||||
},
|
||||
"full_stats": {
|
||||
@@ -493,13 +478,13 @@
|
||||
"body": {
|
||||
"accounts_linked": "Прив'язані акаунти",
|
||||
"no_role_permissions": {
|
||||
"title": "%s1 не вдалося перевірити",
|
||||
"title": "{name} failed to verify",
|
||||
"description": "У мене немає дозволу керувати ролями."
|
||||
}
|
||||
},
|
||||
"embed": {
|
||||
"title": "Невірний обліковий запис?",
|
||||
"description": "Use the command again and select a username from the search results."
|
||||
"description": "Використайте команду ще раз і виберіть ім'я користувача з результатів пошуку."
|
||||
}
|
||||
},
|
||||
"search_clans": {
|
||||
@@ -508,11 +493,11 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "ліміт",
|
||||
"description": "The max number of clan results (default: %s1)"
|
||||
"description": "The max number of clan results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Clan search for %s1 in region %s2",
|
||||
"title": "Clan search for {search} in region {region}",
|
||||
"no_results": "Жодного клану не знайдено"
|
||||
}
|
||||
},
|
||||
@@ -522,37 +507,37 @@
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "ліміт",
|
||||
"description": "The max number of player results (default: %s1)"
|
||||
"description": "The max number of player results (default: {limit})"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"title": "Player search for %s1 in region %s2",
|
||||
"title": "Player search for {search} in region {region}",
|
||||
"no_results": "Гравця не знайдено"
|
||||
}
|
||||
},
|
||||
"rating_leaderboard": {
|
||||
"name": "rating-leaderboard",
|
||||
"description": "Rating battles statistics",
|
||||
"name": "таблиця рейтингу",
|
||||
"description": "Статистика рейтингових боїв",
|
||||
"subcommands": {
|
||||
"neighbors": {
|
||||
"name": "neighbors",
|
||||
"description": "Neighboring players on the leaderboard"
|
||||
},
|
||||
"league": {
|
||||
"name": "league",
|
||||
"description": "Top players of a league"
|
||||
"name": "ліга",
|
||||
"description": "Найкращий гравець ліги"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"limit": {
|
||||
"name": "ліміт",
|
||||
"description": ""
|
||||
"description": "How many neighbors to display (default: {count})"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"description": "Season number (default: current)",
|
||||
"name": "сезон",
|
||||
"description": "Базова інформація про гравця",
|
||||
"choices": {
|
||||
"current": "%s1 (current)"
|
||||
"current": "{season} (current)"
|
||||
}
|
||||
},
|
||||
"league": {
|
||||
@@ -561,12 +546,12 @@
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"top_players": "Top %s1 players",
|
||||
"no_participation": "# %s1 didn't play rating in season %s2\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"top_players": "Top {count} players",
|
||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||
"deleted_player": "Deleted %s1",
|
||||
"deleted_player": "Deleted {id}",
|
||||
"neighbors": "Rating neighbors",
|
||||
"more": "+ %s1 more"
|
||||
"more": "+ {count} more"
|
||||
}
|
||||
},
|
||||
"today": {
|
||||
@@ -578,17 +563,17 @@
|
||||
"description": "Коротка статистика за період"
|
||||
},
|
||||
"owned_tanks": {
|
||||
"name": "owned-tanks",
|
||||
"description": "Shows all own tanks in a tier",
|
||||
"name": "отримані танки",
|
||||
"description": "Показує всі ваші танки даного рівня",
|
||||
"body": {
|
||||
"title": "# %s1's owned tier %s2 tanks\n\n"
|
||||
"title": "# {name}'s owned tier {tier} tanks\n\n"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permissions",
|
||||
"name": "дозволи",
|
||||
"description": "Checks wether the bot has the required permissions to function",
|
||||
"body": {
|
||||
"title": "# Permissions\n\n%s1",
|
||||
"title": "# Permissions\n\n{body}",
|
||||
"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",
|
||||
@@ -600,7 +585,7 @@
|
||||
"description": "Check wether the bot is alive.",
|
||||
"body": {
|
||||
"pong": "Pong 🏓",
|
||||
"pong_with_time": "Pong 🏓 - %s1ms"
|
||||
"pong_with_time": "Pong 🏓 - {time}ms"
|
||||
},
|
||||
"subcommands": {
|
||||
"blitzkit": {
|
||||
@@ -615,9 +600,9 @@
|
||||
},
|
||||
"player_info": {
|
||||
"name": "player-info",
|
||||
"description": "Basic information about a player",
|
||||
"description": "Базова інформація про гравця",
|
||||
"body": {
|
||||
"title": "%s1's information",
|
||||
"title": "{name}'s information",
|
||||
"nickname": "Нік",
|
||||
"battles": "Бої",
|
||||
"winrate": "Відсоток перемог",
|
||||
@@ -635,7 +620,7 @@
|
||||
"description": "Today's rating statistics"
|
||||
},
|
||||
"season": {
|
||||
"name": "season",
|
||||
"name": "сезон",
|
||||
"description": "This season's rating statistics"
|
||||
}
|
||||
},
|
||||
@@ -646,7 +631,7 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"no_participation": "# %s1 hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
|
||||
"no_participation": "# {name} 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."
|
||||
}
|
||||
}
|
||||
@@ -658,6 +643,8 @@
|
||||
"body": "This page has moved. Redirecting..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Change BlitzKit settings",
|
||||
"dev_mode": "Development mode",
|
||||
"language": "Мова"
|
||||
},
|
||||
@@ -671,13 +658,16 @@
|
||||
"copied": "Скопійовано!"
|
||||
},
|
||||
"tank_search": {
|
||||
"select_dialog_title": "Search for tanks",
|
||||
"swap_dialog_title": "Swap tank",
|
||||
"clear_filters": "Clear filters",
|
||||
"login": "Login to use this",
|
||||
"search_bar_hint": "Шукайте техніку...",
|
||||
"recent": "Недавно переглянуті",
|
||||
"select_all": "Вибрати все",
|
||||
"count_singular": "1 танк",
|
||||
"count_plural": "%s1 танків",
|
||||
"sorting_by": "Сортувати за %s1",
|
||||
"count_plural": "{count} tanks",
|
||||
"sorting_by": "Sorting by {name}",
|
||||
"no_tanks_found": {
|
||||
"body": "Нічого не знайдено.",
|
||||
"search_again": "Спробуйте шукати заново.",
|
||||
@@ -698,6 +688,7 @@
|
||||
"fire.caliber": "калібр",
|
||||
"fire.standardPenetration": "базове пробиття",
|
||||
"fire.premiumPenetration": "преміум пробиття",
|
||||
"fire.tertiaryPenetration": "tertiary penetration",
|
||||
"fire.damage": "шкода",
|
||||
"fire.moduleDamage": "шкода по модулях",
|
||||
"fire.shellVelocity": "швидкість польоту снаряда",
|
||||
@@ -738,22 +729,30 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"pwa": {
|
||||
"title": "Install the BlitzKit App",
|
||||
"new": "NEW",
|
||||
"checking": "Checking compatibility...",
|
||||
"instructions": "Instructions for Apple devices.",
|
||||
"install": "Install",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"discord": {
|
||||
"join_short": "Join cool people!",
|
||||
"join_long": "Join super duper cool people!",
|
||||
"action": "Discord"
|
||||
},
|
||||
"hero": {
|
||||
"credit": "Зроблено %s1 ❤"
|
||||
"credit": "By {name} ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Популярні танки",
|
||||
"subtitle": "Останні %s1 дні(в)"
|
||||
"subtitle": "Last {days} days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Інші класні проєкти"
|
||||
},
|
||||
"help_translate": "Будь ласка, допоможіть перекласти %s1 на %s2!",
|
||||
"help_translate": "Please help translate {name} to {language}!",
|
||||
"seo_description": "Інструменти для всього, пов'язаного з WoT Blitz"
|
||||
},
|
||||
"docs": {
|
||||
@@ -763,6 +762,14 @@
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
"description": "Mix tank modules",
|
||||
"seo_description": "Mix and match hulls, turrets, and guns of tanks in World of Tanks Blitz",
|
||||
"screenshot": "Screenshot",
|
||||
"randomize": "Randomize"
|
||||
},
|
||||
"guess": {
|
||||
"name": "Guess the tank",
|
||||
"button": "Guess away",
|
||||
@@ -775,7 +782,7 @@
|
||||
"next": "Next",
|
||||
"no_results": "Немає результатів"
|
||||
},
|
||||
"stats": "%s1 / %s2 guessed correctly"
|
||||
"stats": "{correct} / {total} guessed correctly"
|
||||
},
|
||||
"tankopedia": {
|
||||
"search": "Search a stat...",
|
||||
@@ -783,9 +790,15 @@
|
||||
"button": "Знайти танки",
|
||||
"description": "Танкова енциклопедія",
|
||||
"seo_description": "Характеристики, броня та інше для всіх танків WoT Blitz",
|
||||
"tank_seo_title": "%s1 - Рівень %s2 %s3 %s4 | %s5 Танкопедія | WoT Blitz",
|
||||
"tank_seo_description": "%s1 броня, характеристики, обладнання, амуніція та більше. %s2 шкода на хвилину, %s3 разова шкода, %s4 мм пробиття, %s5 км/год максимальна швидкість, %s6 міцність.",
|
||||
"tank_seo_title": "{name} - Tier {tier} {nationality} {class} | {website} Tankopedia | World of Tanks Blitz (WoTB)",
|
||||
"tank_seo_description": "{name} armor, statistics, equipment, provisions, and more. {dpm} DPM, {alpha}hp alpha, {penetration}mm penetration, {speed}kph top speed, {health}hp health.",
|
||||
"visualizers": {
|
||||
"reload": {
|
||||
"skip": "Skip"
|
||||
},
|
||||
"assault": {
|
||||
"title": "Assault shells"
|
||||
},
|
||||
"rotator": {
|
||||
"hull": "Hull",
|
||||
"turret": "Turret",
|
||||
@@ -803,9 +816,14 @@
|
||||
},
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"ghost_factory": "Ghost Factory",
|
||||
"dead_rail": "Dead Rail"
|
||||
},
|
||||
"flexibility": {
|
||||
"yaw": "Yaw",
|
||||
"pitch": "Pitch",
|
||||
"min": "Min",
|
||||
"max": "Max"
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
@@ -819,9 +837,7 @@
|
||||
"green_penetration": "Зелене пробиття",
|
||||
"hide_model_under_armor": "Hide model under armor",
|
||||
"opaque": "Opaque",
|
||||
"dev_wireframe": "DEV: wireframe",
|
||||
"environment": "Environment",
|
||||
"show_grid": "Show grid"
|
||||
"dev_wireframe": "DEV: wireframe"
|
||||
},
|
||||
"screenshot": {
|
||||
"download": "Завантажити",
|
||||
@@ -843,7 +859,6 @@
|
||||
"reset": "Reset"
|
||||
},
|
||||
"advanced": "Advanced highlighting",
|
||||
"shooter": "Shooter:",
|
||||
"shot_card": {
|
||||
"element": {
|
||||
"primary": "Primary",
|
||||
@@ -854,15 +869,15 @@
|
||||
"track": "Tracks"
|
||||
},
|
||||
"status": {
|
||||
"penetration": "%s1hp penetration",
|
||||
"penetration": "{damage}hp penetration",
|
||||
"blocked": "Blocked",
|
||||
"ricochet": "Рикошет",
|
||||
"splash": "%s1hp splash"
|
||||
"splash": "{damage}hp splash"
|
||||
},
|
||||
"stats": {
|
||||
"thickness_and_angle": "%s1mm %s2°",
|
||||
"nominal": "%s1mm nominal",
|
||||
"ricochet_loss": "%s1% penetration"
|
||||
"thickness_and_angle": "{thickness}mm {angle}°",
|
||||
"nominal": "{thickness}mm nominal",
|
||||
"ricochet_loss": "{loss}% penetration"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -900,16 +915,15 @@
|
||||
"tech_tree": {
|
||||
"title": "Звичайний",
|
||||
"battle": "1 бій",
|
||||
"battles": "%s1 battles",
|
||||
"route": "Route %s1 of %s2"
|
||||
"battles": "{count} battles",
|
||||
"route": "Route {index} of {count}"
|
||||
},
|
||||
"game_modes": {
|
||||
"title": "Game mode abilities"
|
||||
},
|
||||
"review": {
|
||||
"title": "Review videos",
|
||||
"disclaimer": "BlitzKit does not endorse any review videos.",
|
||||
"join": "Apply to %s1 YouTuber program"
|
||||
"join": "Apply to {name} YouTuber program"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Configuration",
|
||||
@@ -945,15 +959,15 @@
|
||||
"title": "Characteristics",
|
||||
"compare": {
|
||||
"label": "Compare against",
|
||||
"class": "Tier %s1 %s2",
|
||||
"tier": "All of tier %s1",
|
||||
"class": "Tier {tier} {class}",
|
||||
"tier": "All of tier {tier}",
|
||||
"all": "Усі танки"
|
||||
},
|
||||
"values": {
|
||||
"shell_index": "Снаряд %s1",
|
||||
"at_distance": "At %s1m",
|
||||
"shell_index": "Shell {index}",
|
||||
"at_distance": "At {distance}m",
|
||||
"at_distance_slider_label": "Відстань",
|
||||
"dispersion_at_distance": "Dispersion at %s1m",
|
||||
"dispersion_at_distance": "Dispersion at {distance}m",
|
||||
"gun_flexibility": "Gun flexibility",
|
||||
"speed": "Швидкість",
|
||||
"terrain_coefficients": "Terrain coefficients",
|
||||
@@ -969,12 +983,13 @@
|
||||
"dpmEffective": "Effective at 60s",
|
||||
"shells": "Снаряди",
|
||||
"mostOptimalShellIndex": "Most optimal shell",
|
||||
"shellReloads": "Shell reloads",
|
||||
"shellReload": "Shell reload",
|
||||
"shellReloads": "Reloads",
|
||||
"shellReload": "Час перезаряджання",
|
||||
"intraClip": "Intra-clip",
|
||||
"caliber": "Калібр",
|
||||
"penetration": "Бронепробиття",
|
||||
"damage": "Шкода",
|
||||
"damageWithoutAssault": "Damage without assault shells",
|
||||
"clipDamage": "Clipping potential",
|
||||
"moduleDamage": "Шкода по модулях",
|
||||
"explosionRadius": "Радіус розльоту уламків",
|
||||
@@ -1027,7 +1042,9 @@
|
||||
"shellRange": "Shell range",
|
||||
"shellCapacity": "Shell capacity",
|
||||
"gunType": "Тип гармати",
|
||||
"penetrationAt250m": "Penetration at 250m"
|
||||
"penetrationAt250m": "Penetration at 250m",
|
||||
"burstShells": "Burst shell count",
|
||||
"burstInterShell": "Burst inter-shell"
|
||||
}
|
||||
},
|
||||
"firepower": {
|
||||
@@ -1099,9 +1116,9 @@
|
||||
"description": "Tank performance statistics",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz",
|
||||
"estimation": {
|
||||
"body": "Estimated stats based on %s1 players. Updated %s2 ago.",
|
||||
"hours": "%s1 hours",
|
||||
"minutes": "%s1 minutes"
|
||||
"body": "Estimated stats based on {count} players. Updated {age} ago.",
|
||||
"hours": "{count} hours",
|
||||
"minutes": "{count} minutes"
|
||||
},
|
||||
"player_count": {
|
||||
"label": "Player count"
|
||||
@@ -1133,10 +1150,10 @@
|
||||
"button": "Browse all",
|
||||
"description": "Blitz player avatars",
|
||||
"seo_description": "All World of Tanks Blitz player avatars",
|
||||
"last_updated": "Last updated on %s1",
|
||||
"last_updated": "Last updated on {time}",
|
||||
"search": {
|
||||
"hint": "Search avatars...",
|
||||
"results": "%s1 avatars"
|
||||
"results": "{count} avatars"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
@@ -1151,8 +1168,8 @@
|
||||
"no_results": "Гравця не знайдено"
|
||||
},
|
||||
"controls": {
|
||||
"title": "Відстежуємо %s1",
|
||||
"subtitle": "Від %s1",
|
||||
"title": "Tracking {name}",
|
||||
"subtitle": "Since {time}",
|
||||
"reset": "Скинути",
|
||||
"change": "Змінити"
|
||||
},
|
||||
@@ -1212,7 +1229,7 @@
|
||||
"cancel": "Cancel",
|
||||
"success": {
|
||||
"title": "Copy URL",
|
||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"info": "Generated the URL for {name}. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||
"back": "Назад",
|
||||
"copy": "Copy URL"
|
||||
}
|
||||
@@ -1295,17 +1312,11 @@
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
"api": {
|
||||
"name": "API",
|
||||
"button": "To the docs",
|
||||
"description": "BlitzKit hosted API",
|
||||
"seo_description": "BlitzKit hosted static API for World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
|
||||
+713
-702
File diff suppressed because it is too large.
Load diff
@@ -11,20 +11,21 @@
|
||||
"build:ids": "bun src/discoverIds.ts",
|
||||
"build:bot": "bun src/buildBot.ts",
|
||||
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts",
|
||||
"build:proxyclient": "bun src/buildProxyClient.ts",
|
||||
"prunestrings": "bun src/pruneStrings.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzkit/core": "workspace:*",
|
||||
"@blitzkit/i18n": "workspace:*",
|
||||
"@gltf-transform/core": "^4.0.10",
|
||||
"@gltf-transform/extensions": "^4.0.10",
|
||||
"@gltf-transform/functions": "^4.0.10",
|
||||
"csv-parse": "^5.5.6",
|
||||
"@gltf-transform/core": "^4.2.1",
|
||||
"@gltf-transform/extensions": "^4.2.1",
|
||||
"@gltf-transform/functions": "^4.2.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"decompress": "^4.2.1",
|
||||
"esbuild": "^0.19.12",
|
||||
"fast-xml-parser": "^4.4.1",
|
||||
"esbuild": "^0.25.10",
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
"progress": "^2.0.3",
|
||||
"puppeteer": "^23.10.4"
|
||||
"puppeteer": "^24.22.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/decompress": "^4.2.7",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mkdir, writeFile } from 'fs/promises';
|
||||
import { parse as parsePath } from 'path';
|
||||
import ProgressBar from 'progress';
|
||||
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
||||
import { dvp } from '../../../submodules/blitzkit-closed/src/dvp';
|
||||
import { dvp } from '../../../submodules/blitzkit-closed/src/dava/dvp';
|
||||
import { readStringDVPL } from '../src/core/blitz/readStringDVPL';
|
||||
import { DATA } from './buildAssets/constants';
|
||||
import { readYAMLDVPL } from './core/blitz/readYAMLDVPL';
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Samples,
|
||||
averageDefinitionsAllStatsKeys,
|
||||
emptySamples,
|
||||
encodePBBuffer,
|
||||
getAccountInfo,
|
||||
getTankStats,
|
||||
idToRegion,
|
||||
@@ -235,11 +234,11 @@ async function postWork() {
|
||||
commitAssets('averages', [
|
||||
{
|
||||
path: `averages/${latest}.pb`,
|
||||
content: encodePBBuffer(AverageDefinitions, averageDefinitions),
|
||||
content: AverageDefinitions.encode(averageDefinitions).finish(),
|
||||
},
|
||||
{
|
||||
path: 'averages/manifest.json',
|
||||
content: Buffer.from(JSON.stringify(manifest), 'utf-8'),
|
||||
content: new TextEncoder().encode(JSON.stringify(manifest)),
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import { BLITZKIT_TANK_ICON_SIZE, fetchTankDefinitions } from '@blitzkit/core';
|
||||
import { launch } from 'puppeteer';
|
||||
import sharp from 'sharp';
|
||||
import { commitAssets } from '../core/github/commitAssets';
|
||||
import { FileChange } from '../core/github/commitMultipleFiles';
|
||||
import { BLITZKIT_TANK_ICON_SIZE, fetchTankDefinitions } from "@blitzkit/core";
|
||||
import { launch } from "puppeteer";
|
||||
import sharp from "sharp";
|
||||
import { commitAssets } from "../core/github/commitAssets";
|
||||
import { FileChange } from "../core/github/commitMultipleFiles";
|
||||
|
||||
export async function blitzkitTankIcons() {
|
||||
console.log('Building blitzkit tank icons...');
|
||||
console.log("Building blitzkit tank icons...");
|
||||
|
||||
console.log('Starting website dev server...');
|
||||
const browser = await launch();
|
||||
const page = await browser.newPage();
|
||||
const tanks = Object.values((await fetchTankDefinitions()).tanks);
|
||||
@@ -19,30 +18,32 @@ export async function blitzkitTankIcons() {
|
||||
width: BLITZKIT_TANK_ICON_SIZE.width - 1,
|
||||
height: BLITZKIT_TANK_ICON_SIZE.height,
|
||||
});
|
||||
await page.goto(`http://localhost:4321/api/tankopedia/tank-icon/${id}/`, {
|
||||
waitUntil: 'networkidle0',
|
||||
});
|
||||
|
||||
if (index === 0) {
|
||||
console.log('Dev server started!');
|
||||
try {
|
||||
await page.goto(`http://localhost:4321/api/tankopedia/tank-icon/${id}/`, {
|
||||
waitUntil: "networkidle0",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`${id} ${name} time out`, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
const screenshot = await page.screenshot({
|
||||
type: 'webp',
|
||||
type: "webp",
|
||||
omitBackground: true,
|
||||
});
|
||||
const content = await sharp(screenshot).trim({ threshold: 100 }).toBuffer();
|
||||
|
||||
index++;
|
||||
console.log(
|
||||
`Rendered ${id} (${name}) ${((index / tanks.length) * 100).toFixed(2)}%`,
|
||||
`Rendered ${id} (${name}) ${((index / tanks.length) * 100).toFixed(2)}%`
|
||||
);
|
||||
|
||||
files.push({ path: `icons/tanks/blitzkit/${id}.webp`, content });
|
||||
files.push({ path: `icons/tanks/big/${id}.webp`, content });
|
||||
}
|
||||
|
||||
await page.close();
|
||||
await browser.close();
|
||||
|
||||
await commitAssets('tank icons', files);
|
||||
await commitAssets("tank icons", files);
|
||||
}
|
||||
Loaded 100 of 394 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user