feat: update bot chunk 1

This commit is contained in:
tresabhi committed 2025-01-15 20:14:05 -06:00
1 parent bb557a7dd7
commit d2b24fc953
42 files changed
+370 -389

No files matched your search

+13 -9
View File
@@ -3,6 +3,7 @@ import {
Region, Region,
regionToRegionSubdomain, regionToRegionSubdomain,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises'; import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
import { chunkLines } from '../core/discord/chunkLines'; import { chunkLines } from '../core/discord/chunkLines';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
@@ -75,7 +76,7 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('auction'), command: createLocalizedCommand('auction'),
async handler(interaction) { async handler(interaction) {
const { t, translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions; const awaitedTankDefinitions = await tankDefinitions;
const region: Region = 'com'; const region: Region = 'com';
const pageSize = 80; const pageSize = 80;
@@ -90,13 +91,13 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
const saleableTanks = data.results.filter((result) => result.available); const saleableTanks = data.results.filter((result) => result.available);
if (saleableTanks.length === 0) { if (saleableTanks.length === 0) {
return t`bot.commands.auction.errors.no_auction`; return strings.bot.commands.auction.errors.no_auction;
} }
const nextPricesTimestamp = saleableTanks[0].next_price_timestamp; const nextPricesTimestamp = saleableTanks[0].next_price_timestamp;
const title = `# ${t`bot.commands.auction.body.title`}`; const title = `# ${strings.bot.commands.auction.body.title}`;
const subtitle = `${translate('bot.commands.auction.body.subtitle', [ const subtitle = `${literals(strings.bot.commands.auction.body.subtitle, [
`${saleableTanks.length} / ${data.results.length}`, `${saleableTanks.length} / ${data.results.length}`,
`<t:${nextPricesTimestamp}:R>`, `<t:${nextPricesTimestamp}:R>`,
'<https://wotblitz.com/auction/>', '<https://wotblitz.com/auction/>',
@@ -112,15 +113,18 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
const next = const next =
data.next_price === null data.next_price === null
? '' ? ''
: `\n-# ${translate('bot.commands.auction.body.next', [ : `\n-# ${literals(strings.bot.commands.auction.body.next, [
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString( `<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
interaction.locale, interaction.locale,
)}`, )}`,
])}`; ])}`;
const available = translate('bot.commands.auction.body.available', [ const available = literals(
data.current_count.toLocaleString(interaction.locale), strings.bot.commands.auction.body.available,
data.initial_count.toLocaleString(interaction.locale), [
]); data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
],
);
const isOut = data.current_count === 0; const isOut = data.current_count === 0;
const outString = isOut ? '~~' : ''; const outString = isOut ? '~~' : '';
const isLow = data.current_count <= WARNING_COUNT; const isLow = data.current_count <= WARNING_COUNT;
+9 -7
View File
@@ -1,4 +1,5 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core'; import { getAccountInfo, getClanInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from '../components/CommandWrapper';
import { GenericStats } from '../components/GenericStats'; import { GenericStats } from '../components/GenericStats';
@@ -19,7 +20,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('inactive') command: createLocalizedCommand('inactive')
.addStringOption(addClanChoices) .addStringOption(addClanChoices)
.addNumberOption((option) => { .addNumberOption((option) => {
const { translate } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName('threshold') .setName('threshold')
@@ -31,7 +32,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
), ),
) )
.setDescription( .setDescription(
translate('bot.commands.inactive.options.threshold.description'), strings.bot.commands.inactive.options.threshold.description,
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
@@ -42,7 +43,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
}), }),
async handler(interaction) { async handler(interaction) {
const { translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction); const { region, id } = await resolveClanFromCommand(interaction);
const threshold = const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD; interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
@@ -63,7 +64,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) => ([name, days]) =>
[ [
name, name,
translate('bot.commands.inactive.body.listing', [ literals(strings.bot.commands.inactive.body.listing, [
days.toFixed(0), days.toFixed(0),
]), ]),
] as [string, string], ] as [string, string],
@@ -75,9 +76,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar <TitleBar
title={clanInfo.name} title={clanInfo.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`} image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${translate('bot.commands.inactive.body.subtitle', [ description={`${literals(
`${threshold}`, strings.bot.commands.inactive.body.subtitle,
])} • ${new Date().toLocaleDateString(interaction.locale)}`} [`${threshold}`],
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
/> />
{!hasInactiveMembers && ( {!hasInactiveMembers && (
@@ -16,39 +16,67 @@ export function addPeriodSubCommands(
option: SlashCommandSubcommandBuilder, option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option, ) => SlashCommandSubcommandBuilder = (option) => option,
) { ) {
const { translate } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
const subcommandNameLocalizations = { const subcommandNameLocalizations = {
today: localizationObject('bot.common.subcommands.today', undefined, true), today: localizationObject(
30: localizationObject('bot.common.subcommands.30', undefined, true), (strings) => strings.bot.common.subcommands.today.$,
60: localizationObject('bot.common.subcommands.60', undefined, true), undefined,
90: localizationObject('bot.common.subcommands.90', undefined, true), true,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].$,
undefined,
true,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].$,
undefined,
true,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].$,
undefined,
true,
),
career: localizationObject( career: localizationObject(
'bot.common.subcommands.career', (strings) => strings.bot.common.subcommands.career.$,
undefined, undefined,
true, true,
), ),
custom: localizationObject( custom: localizationObject(
'bot.common.subcommands.custom', (strings) => strings.bot.common.subcommands.custom.$,
undefined, undefined,
true, true,
), ),
}; };
const subcommandDescriptionLocalizations = { const subcommandDescriptionLocalizations = {
today: localizationObject('bot.common.subcommands.today.description'), today: localizationObject(
30: localizationObject('bot.common.subcommands.30.description'), (strings) => strings.bot.common.subcommands.today.description,
60: localizationObject('bot.common.subcommands.60.description'), ),
90: localizationObject('bot.common.subcommands.90.description'), 30: localizationObject(
career: localizationObject('bot.common.subcommands.career.description'), (strings) => strings.bot.common.subcommands[30].description,
custom: localizationObject('bot.common.subcommands.custom.description'), ),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].description,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].description,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.description,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.description,
),
}; };
option option
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.today')) .setName(strings.bot.common.subcommands.today.$)
.setNameLocalizations(subcommandNameLocalizations.today) .setNameLocalizations(subcommandNameLocalizations.today)
.setDescription(translate('bot.common.subcommands.today.description')) .setDescription(strings.bot.common.subcommands.today.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
subcommandDescriptionLocalizations.today, subcommandDescriptionLocalizations.today,
), ),
@@ -57,38 +85,36 @@ export function addPeriodSubCommands(
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.30')) .setName(strings.bot.common.subcommands[30].$)
.setNameLocalizations(subcommandNameLocalizations[30]) .setNameLocalizations(subcommandNameLocalizations[30])
.setDescription(translate('bot.common.subcommands.30.description')) .setDescription(strings.bot.common.subcommands[30].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[30]), .setDescriptionLocalizations(subcommandDescriptionLocalizations[30]),
), ),
) )
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.60')) .setName(strings.bot.common.subcommands[60].$)
.setNameLocalizations(subcommandNameLocalizations[60]) .setNameLocalizations(subcommandNameLocalizations[60])
.setDescription(translate('bot.common.subcommands.60.description')) .setDescription(strings.bot.common.subcommands[60].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[60]), .setDescriptionLocalizations(subcommandDescriptionLocalizations[60]),
), ),
) )
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.90')) .setName(strings.bot.common.subcommands[90].$)
.setNameLocalizations(subcommandNameLocalizations[90]) .setNameLocalizations(subcommandNameLocalizations[90])
.setDescription(translate('bot.common.subcommands.90.description')) .setDescription(strings.bot.common.subcommands[90].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[90]), .setDescriptionLocalizations(subcommandDescriptionLocalizations[90]),
), ),
) )
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.career')) .setName(strings.bot.common.subcommands.career.$)
.setNameLocalizations(subcommandNameLocalizations.career) .setNameLocalizations(subcommandNameLocalizations.career)
.setDescription( .setDescription(strings.bot.common.subcommands.career.description)
translate('bot.common.subcommands.career.description'),
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
subcommandDescriptionLocalizations.career, subcommandDescriptionLocalizations.career,
), ),
@@ -97,31 +123,25 @@ export function addPeriodSubCommands(
.addSubcommand((option) => .addSubcommand((option) =>
extra( extra(
option option
.setName(translate('bot.common.subcommands.custom')) .setName(strings.bot.common.subcommands.custom.$)
.setNameLocalizations(subcommandNameLocalizations.custom) .setNameLocalizations(subcommandNameLocalizations.custom)
.setDescription( .setDescription(strings.bot.common.subcommands.custom.description)
translate('bot.common.subcommands.custom.description'),
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
subcommandDescriptionLocalizations.custom, subcommandDescriptionLocalizations.custom,
) )
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
.setName(translate('bot.common.subcommands.custom.options.start')) .setName(strings.bot.common.subcommands.custom.options.start.$)
.setDescription( .setDescription(
translate( strings.bot.common.subcommands.custom.options.start.description,
'bot.common.subcommands.custom.options.start.description',
),
) )
.setRequired(true), .setRequired(true),
) )
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
.setName(translate('bot.common.subcommands.custom.options.end')) .setName(strings.bot.common.subcommands.custom.options.end.$)
.setDescription( .setDescription(
translate( strings.bot.common.subcommands.custom.options.end.description,
'bot.common.subcommands.custom.options.end.description',
),
) )
.setRequired(true), .setRequired(true),
), ),
@@ -8,25 +8,31 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject'; import { localizationObject } from './localizationObject';
export function addRegionChoices(option: SlashCommandStringOption) { export function addRegionChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName(t`bot.common.options.region`) .setName(strings.bot.common.options.region.$)
.setNameLocalizations( .setNameLocalizations(
localizationObject('bot.common.options.region', undefined, true), localizationObject(
(strings) => strings.bot.common.options.region.$,
undefined,
true,
),
) )
.setDescription(t`bot.common.options.region.description`) .setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject('bot.common.options.region.description'), localizationObject(
(strings) => strings.bot.common.options.region.description,
),
) )
.addChoices( .addChoices(
...REGIONS.map( ...REGIONS.map(
(region) => (region) =>
({ ({
value: region, value: region,
name: translate(`common.regions.normal.${region}`), name: strings.common.regions.normal[region],
name_localizations: localizationObject( name_localizations: localizationObject(
`common.regions.normal.${region}`, (strings) => strings.common.regions.normal[region],
), ),
}) satisfies APIApplicationCommandOptionChoice, }) satisfies APIApplicationCommandOptionChoice,
), ),
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject'; import { localizationObject } from './localizationObject';
export function addTankChoices(option: SlashCommandStringOption) { export function addTankChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName(translate('bot.common.options.tank')) .setName(strings.bot.common.options.tank.$)
.setNameLocalizations( .setNameLocalizations(
localizationObject('bot.common.options.tank', undefined, true), localizationObject(
(strings) => strings.bot.common.options.tank.$,
undefined,
true,
),
) )
.setDescription(translate('bot.common.options.tank.description')) .setDescription(strings.bot.common.options.tank.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject('bot.common.options.tank.description'), localizationObject(
(strings) => strings.bot.common.options.tank.description,
),
) )
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(true); .setRequired(true);
@@ -1,4 +1,5 @@
import { numberToRomanNumeral } from '@blitzkit/core'; import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { import {
APIApplicationCommandOptionChoice, APIApplicationCommandOptionChoice,
Locale, Locale,
@@ -9,28 +10,34 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject'; import { localizationObject } from './localizationObject';
export function addTierChoices(option: SlashCommandStringOption) { export function addTierChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName(t`bot.common.options.tier`) .setName(strings.bot.common.options.tier.$)
.setNameLocalizations( .setNameLocalizations(
localizationObject('bot.common.options.tier', undefined, true), localizationObject(
(strings) => strings.bot.common.options.tier.$,
undefined,
true,
),
) )
.setDescription(t`bot.common.options.tier.description`) .setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject('bot.common.options.tier.description'), localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
) )
.setChoices( .setChoices(
...range(10, 0).map( ...range(10, 0).map(
(tier) => (tier) =>
({ ({
name: translate('bot.common.options.tier.choices.tier', [ name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`, `${tier}`,
numberToRomanNumeral(tier), numberToRomanNumeral(tier),
]), ]),
value: `${tier}`, value: `${tier}`,
name_localizations: localizationObject( name_localizations: localizationObject(
'bot.common.options.tier.choices.tier', (strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)], [`${tier}`, numberToRomanNumeral(tier)],
), ),
}) satisfies APIApplicationCommandOptionChoice, }) satisfies APIApplicationCommandOptionChoice,
@@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants';
export async function autocompleteClan( export async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>, interaction: AutocompleteInteraction<CacheType>,
) { ) {
const { translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true); const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return; if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]); if (focusedOption.value.length < 2) return interaction.respond([]);
@@ -19,9 +19,7 @@ export async function autocompleteClan(
await interaction.respond( await interaction.respond(
clans clans
? clans.map((clan) => { ? clans.map((clan) => {
let name = `${clan.name} (${translate( let name = `${clan.name} (${strings.common.regions.short[clan.region]})`;
`common.regions.short.${clan.region}`,
)})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) { if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE; const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -29,9 +27,7 @@ export async function autocompleteClan(
name = `${clan.name.slice( name = `${clan.name.slice(
0, 0,
clan.name.length - overSize - OVERFLOW_SUFFIX.length, clan.name.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate( )}${OVERFLOW_SUFFIX} (${strings.common.regions.short[clan.region]})`;
`common.regions.short.${clan.region}`,
)})`;
} }
return { return {
@@ -13,7 +13,7 @@ import {
export async function autocompleteUsername( export async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>, interaction: AutocompleteInteraction<CacheType>,
) { ) {
const { translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true); const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return; if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value); const players = await searchPlayersAcrossRegions(focusedOption.value);
@@ -22,9 +22,9 @@ export async function autocompleteUsername(
await interaction.respond( await interaction.respond(
players players
? players.map((player) => { ? players.map((player) => {
let name = `${player.nickname} (${translate( let name = `${player.nickname} (${
`common.regions.short.${player.region}`, strings.common.regions.short[player.region]
)})`; })`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) { if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE; const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -32,9 +32,9 @@ export async function autocompleteUsername(
name = `${player.nickname.slice( name = `${player.nickname.slice(
0, 0,
player.nickname.length - overSize - OVERFLOW_SUFFIX.length, player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate( )}${OVERFLOW_SUFFIX} (${
`common.regions.short.${player.region}`, strings.common.regions.short[player.region]
)})`; })`;
} }
return { return {
@@ -1,3 +1,4 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import { import {
Locale, Locale,
SlashCommandBuilder, SlashCommandBuilder,
@@ -23,28 +24,47 @@ export function createLocalizedCommand(
| CreateLocalizedCommandSubcommand[] | CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[], | CreateLocalizedCommandSubcommandGroup[],
) { ) {
const commandPathItem = command.replaceAll('-', '_'); const commandPathItem = command.replaceAll(
const { translate } = translator(Locale.EnglishUS); '-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder() const slashCommand = new SlashCommandBuilder()
.setName(command) .setName(command)
.setNameLocalizations( .setNameLocalizations(
localizationObject(`bot.commands.${commandPathItem}`, undefined, true), localizationObject(
(strings) => strings.bot.commands[commandPathItem].$,
undefined,
true,
),
) )
.setDescription(translate(`bot.commands.${commandPathItem}.description`)) .setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject(`bot.commands.${commandPathItem}.description`), localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
); );
if (sub) { if (sub) {
sub.map((subItem) => { sub.map((subItem) => {
if ('subcommand' in subItem) { if ('subcommand' in subItem) {
const subcommandNameLocalizations = localizationObject( const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`, (strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { $: string }>;
}
).subcommands[subItem.subcommand].$,
undefined, undefined,
true, true,
); );
const subcommandDescriptionLocalizations = localizationObject( const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`, (strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
); );
slashCommand.addSubcommand((option) => { slashCommand.addSubcommand((option) => {
@@ -54,20 +74,32 @@ export function createLocalizedCommand(
.setName(subItem.subcommand) .setName(subItem.subcommand)
.setNameLocalizations(subcommandNameLocalizations) .setNameLocalizations(subcommandNameLocalizations)
.setDescription( .setDescription(
translate( (
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`, strings.bot.commands[commandPathItem] as unknown as {
), subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
) )
.setDescriptionLocalizations(subcommandDescriptionLocalizations); .setDescriptionLocalizations(subcommandDescriptionLocalizations);
}); });
} else { } else {
const subcommandNameLocalizations = localizationObject( const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}`, (strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { $: string }>;
}
).groups[subItem.group].$,
undefined, undefined,
true, true,
); );
const subcommandDescriptionLocalizations = localizationObject( const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`, (strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
); );
slashCommand.addSubcommandGroup((option) => { slashCommand.addSubcommandGroup((option) => {
@@ -77,9 +109,11 @@ export function createLocalizedCommand(
.setName(subItem.group) .setName(subItem.group)
.setNameLocalizations(subcommandNameLocalizations) .setNameLocalizations(subcommandNameLocalizations)
.setDescription( .setDescription(
translate( (
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`, strings.bot.commands[commandPathItem] as unknown as {
), groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
) )
.setDescriptionLocalizations(subcommandDescriptionLocalizations); .setDescriptionLocalizations(subcommandDescriptionLocalizations);
}); });
@@ -1,10 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { translator } from '../localization/translator'; import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands'; import { PeriodSize } from './addPeriodSubCommands';
export function getPeriodOptionName(period: PeriodSize, locale: Locale) { export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { translate } = translator(locale); const { strings } = translator(locale);
if (period === 'career') return translate('bot.common.periods.career'); if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return translate('bot.common.periods.today'); if (period === 'today') return strings.bot.common.periods.today;
return translate('bot.common.periods.days', [period]); return literals(strings.bot.common.periods.days, [period]);
} }
@@ -1,3 +1,4 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants'; import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator'; import { translator } from '../localization/translator';
@@ -5,14 +6,14 @@ import { translator } from '../localization/translator';
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u; const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
export function localizationObject( export function localizationObject(
path: string, path: (strings: BlitzKitStrings) => string,
literals?: string[], literals: string[] = [],
strict = false, strict = false,
) { ) {
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>( return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => { (localizations, locale) => {
const { translate } = translator(locale); const { strings } = translator(locale);
const translation = translate(path, literals); const translation = literalsFunc(path(strings), literals);
const isValid = !strict || validNameRegex.test(translation); const isValid = !strict || validNameRegex.test(translation);
if (!isValid) { if (!isValid) {
@@ -1,4 +1,5 @@
import { Region } from '@blitzkit/core'; import { Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { ChatInputCommandInteraction } from 'discord.js'; import { ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper'; import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
@@ -10,7 +11,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export async function resolveClanFromCommand( export async function resolveClanFromCommand(
interaction: ChatInputCommandInteraction, interaction: ChatInputCommandInteraction,
) { ) {
const { translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const clan = interaction.options.getString('clan', true); const clan = interaction.options.getString('clan', true);
if (serverAndIdPattern.test(clan)) { if (serverAndIdPattern.test(clan)) {
@@ -26,7 +27,9 @@ export async function resolveClanFromCommand(
return { region: accounts[0].region, id: accounts[0].clan_id }; return { region: accounts[0].region, id: accounts[0].clan_id };
} else { } else {
throw new UserError( throw new UserError(
translate('bot.common.errors.clan_not_found', [markdownEscape(clan)]), literals(strings.bot.common.errors.clan_not_found, [
markdownEscape(clan),
]),
); );
} }
} }
@@ -1,4 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core'; import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js'; import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { getPeriodNow } from '../blitzkit/getPeriodNow'; import { getPeriodNow } from '../blitzkit/getPeriodNow';
import { getPeriodStart } from '../blitzkit/getPeriodStart'; import { getPeriodStart } from '../blitzkit/getPeriodStart';
@@ -17,7 +18,7 @@ export function resolvePeriodFromCommand(
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
forcedPeriod?: PeriodType, forcedPeriod?: PeriodType,
) { ) {
const { translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
let name: string; let name: string;
let start: number; let start: number;
let end: number; let end: number;
@@ -31,7 +32,7 @@ export function resolvePeriodFromCommand(
const startDaysAgoMin = Math.min(startOption, endOption); const startDaysAgoMin = Math.min(startOption, endOption);
const endDaysAgoMax = Math.max(startOption, endOption); const endDaysAgoMax = Math.max(startOption, endOption);
name = translate('bot.common.periods.custom', [ name = literals(strings.bot.common.periods.custom, [
`${startDaysAgoMin}`, `${startDaysAgoMin}`,
`${endDaysAgoMax}`, `${endDaysAgoMax}`,
]); ]);
@@ -1,4 +1,5 @@
import { idToRegion, Region } from '@blitzkit/core'; import { idToRegion, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js'; import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper'; import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
@@ -14,7 +15,7 @@ export interface ResolvedPlayer {
export async function resolvePlayerFromCommand( export async function resolvePlayerFromCommand(
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
) { ) {
const { t, translate } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const commandUsername = interaction.options.getString('username'); const commandUsername = interaction.options.getString('username');
if (commandUsername !== null) { if (commandUsername !== null) {
@@ -36,7 +37,7 @@ export async function resolvePlayerFromCommand(
} satisfies ResolvedPlayer; } satisfies ResolvedPlayer;
} else { } else {
throw new UserError( throw new UserError(
translate('bot.common.errors.player_not_found', [ literals(strings.bot.common.errors.player_not_found, [
markdownEscape(commandUsername), markdownEscape(commandUsername),
]), ]),
); );
@@ -47,6 +48,6 @@ export async function resolvePlayerFromCommand(
if (account) return account; if (account) return account;
throw new UserError(t`bot.common.errors.player_not_linked`); throw new UserError(strings.bot.common.errors.player_not_linked);
} }
} }
@@ -1,24 +0,0 @@
import { Locale } from 'discord.js';
import {
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './constants';
export type TranslationFragmentTree = {
[key: string]: TranslationFragment;
} & {
$?: string;
};
export type TranslationFragment = string | TranslationFragmentTree;
export const translations = SUPPORTED_LOCALES_DISCORD.reduce<
Partial<Record<Locale, TranslationFragment>>
>(
(table, locale) => ({
...table,
[locale]: require(
`@blitzkit/i18n/strings/${SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]}.json`,
),
}),
{},
);
@@ -1,6 +1,5 @@
import { unwrapper } from '@blitzkit/i18n'; import { STRINGS, unwrapper } from '@blitzkit/i18n';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { translations } from './strings';
import { import {
DEFAULT_LOCALE_DISCORD, DEFAULT_LOCALE_DISCORD,
SUPPORTED_LOCALES_DISCORD, SUPPORTED_LOCALES_DISCORD,
@@ -13,70 +12,10 @@ export function translator(localeRaw: Locale) {
SUPPORTED_LOCALES_DISCORD.includes(localeRaw) SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw ? localeRaw
: DEFAULT_LOCALE_DISCORD; : DEFAULT_LOCALE_DISCORD;
const strings = translations[locale]!; STRINGS;
const strings = STRINGS[SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]];
function translate(path: string, literals?: string[]): string {
const pathArray = path.split('.');
let fragment = strings;
let resolvedString: string;
for (const pathItem of pathArray) {
if (typeof fragment === 'string') {
throw new SyntaxError(
`Attempted to access string with key "${pathItem}" in "${path}" from within a string instead of an object in locale "${locale}"`,
);
} else {
fragment = fragment[pathItem];
if (typeof fragment === 'undefined') {
const message = `Undefined translation at "${pathItem}" in "${path}" for locale "${locale}"`;
if (locale === DEFAULT_LOCALE_DISCORD) {
throw new Error(message);
} else {
console.warn(`${message} falling back to en-US`);
return translator(DEFAULT_LOCALE_DISCORD).translate(path, literals);
}
}
}
}
if (typeof fragment === 'string') {
resolvedString = fragment;
} else if (fragment.$) {
resolvedString = fragment.$!;
} else {
throw new Error(
`Unresolved tree ending for "${path}" in locale "${locale}"`,
);
}
if (literals) {
const chunks = resolvedString.split('%s');
let embeddedString = '';
chunks.forEach((chunk, index) => {
if (index !== 0) embeddedString += literals[index - 1];
embeddedString += chunk;
});
return embeddedString;
}
return resolvedString;
}
function t(paths: TemplateStringsArray, ...embeds: string[]) {
return paths
.map((path, index) =>
path.length === 0
? ''
: `${translate(path)}${index === embeds.length ? '' : embeds[index]}`,
)
.join('');
}
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]); const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
return { locale, translate, t, unwrap }; return { locale, strings, unwrap };
} }
@@ -2,7 +2,6 @@ import {
ActionRowBuilder, ActionRowBuilder,
AttachmentBuilder, AttachmentBuilder,
ButtonBuilder, ButtonBuilder,
ButtonStyle,
CacheType, CacheType,
ChatInputCommandInteraction, ChatInputCommandInteraction,
InteractionReplyOptions, InteractionReplyOptions,
@@ -14,7 +13,6 @@ import { normalizeInteractionReturnable } from '../../../core/discord/normalizeI
import { psa } from '../../../core/discord/psa'; import { psa } from '../../../core/discord/psa';
import { translator } from '../../../core/localization/translator'; import { translator } from '../../../core/localization/translator';
import { Writeable } from '../../../types/writable'; import { Writeable } from '../../../types/writable';
import { UserError } from '../../../core/blitzkit/userError';
export async function handleChatInputCommand( export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
@@ -127,27 +125,25 @@ export async function handleChatInputCommand(
} }
} catch (error) { } catch (error) {
const { t } = translator(interaction.locale); const { t } = translator(interaction.locale);
const components = [ // const components = [
new ActionRowBuilder<ButtonBuilder>().addComponents( // new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder() // new ButtonBuilder()
.setLabel(t`bot.common.errors.get_help`) // .setLabel(t`bot.common.errors.get_help`)
.setURL('https://discord.gg/nDt7AjGJQH') // .setURL('https://discord.gg/nDt7AjGJQH')
.setStyle(ButtonStyle.Link), // .setStyle(ButtonStyle.Link),
), // ),
]; // ];
// if (error instanceof UserError) {
if (error instanceof UserError) { // interaction.editReply({
interaction.editReply({ // content: error.message,
content: error.message, // components,
components, // });
}); // } else {
} else { // console.error(interaction.commandName, error);
console.error(interaction.commandName, error); // interaction.editReply({
// content: t`bot.common.errors.uncaught_error`,
interaction.editReply({ // components,
content: t`bot.common.errors.uncaught_error`, // });
components, // }
});
}
} }
} }
@@ -1,4 +1,3 @@
import { assertSecret } from '@blitzkit/core';
import { import {
AttachmentBuilder, AttachmentBuilder,
AutocompleteInteraction, AutocompleteInteraction,
@@ -8,35 +7,12 @@ import {
ChatInputCommandInteraction, ChatInputCommandInteraction,
EmbedBuilder, EmbedBuilder,
Interaction, Interaction,
REST,
Routes,
SlashCommandBuilder, SlashCommandBuilder,
SlashCommandOptionsOnlyBuilder, SlashCommandOptionsOnlyBuilder,
SlashCommandSubcommandsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js'; } from 'discord.js';
import { aboutCommand } from '../../commands/about';
import { auctionCommand } from '../../commands/auction';
import { breakdownCommand } from '../../commands/breakdown';
import { debugCommand } from '../../commands/debug';
import { evolutionCommand } from '../../commands/evolution';
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 { RenderConfiguration } from '../../core/blitzkit/renderConfiguration';
import { handleAutocomplete } from './handlers/autocomplete'; import { handleAutocomplete } from './handlers/autocomplete';
import { handleButton } from './handlers/button';
import { handleChatInputCommand } from './handlers/chatInputCommand'; import { handleChatInputCommand } from './handlers/chatInputCommand';
export type InteractionRawReturnable = export type InteractionRawReturnable =
@@ -64,71 +40,72 @@ interface CommandRegistryBase {
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void; autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable; button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
} }
interface CommandRegistryImplicit extends CommandRegistryBase { interface CommandRegistryImplicit extends CommandRegistryBase {
handlesInteraction: true; handlesInteraction: true;
handler: (interaction: ChatInputCommandInteraction<CacheType>) => void; handler: (interaction: ChatInputCommandInteraction<CacheType>) => void;
} }
interface CommandRegistryExplicit extends CommandRegistryBase { interface CommandRegistryExplicit extends CommandRegistryBase {
handlesInteraction?: false; handlesInteraction?: false;
handler: ( handler: (
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
) => InteractionReturnable; ) => InteractionReturnable;
} }
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit; export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [ export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
auctionCommand, // auctionCommand,
permissionsCommand, // permissionsCommand,
debugCommand, // debugCommand,
aboutCommand, // aboutCommand,
inactiveCommand, // inactiveCommand,
ownedTanksCommand, // ownedTanksCommand,
playerInfoCommand, // playerInfoCommand,
searchClansCommand, // searchClansCommand,
searchPlayersCommand, // searchPlayersCommand,
fullStatsCommand, // fullStatsCommand,
breakdownCommand, // breakdownCommand,
verifyCommand, // verifyCommand,
pingCommand, // pingCommand,
evolutionCommand, // evolutionCommand,
statsCommand, // statsCommand,
ratingLeaderboardCommand, // ratingLeaderboardCommand,
todayCommand, // todayCommand,
researchCommand, // researchCommand,
ratingCommand, // ratingCommand,
replayCommand, // replayCommand,
]; ];
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => { export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
return rawCommands.reduce<Record<string, CommandRegistry>>( // return rawCommands.reduce<Record<string, CommandRegistry>>(
(commands, registry, index) => { // (commands, registry, index) => {
if (registry.status === 'rejected') { // if (registry.status === 'rejected') {
console.warn( // console.warn(
`Command ${index} failed to load; skipping...`, // `Command ${index} failed to load; skipping...`,
registry.reason, // registry.reason,
); // );
// return commands;
return commands; // }
} // return { ...commands, [registry.value.command.name]: registry.value };
// },
return { ...commands, [registry.value.command.name]: registry.value }; // {},
}, // );
{},
);
}); });
const rest = new REST().setToken(assertSecret(import.meta.env.DISCORD_TOKEN)); // const rest = new REST().setToken(assertSecret(import.meta.env.DISCORD_TOKEN));
commands.then((awaitedCommands) => { // commands.then((awaitedCommands) => {
const body = Object.values(awaitedCommands).map((registry) => // const body = Object.values(awaitedCommands).map((registry) =>
registry.command.toJSON(), // registry.command.toJSON(),
); // );
rest.put( // rest.put(
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)), // Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
{ body }, // { body },
); // );
}); // });
export async function interactionCreate(interaction: Interaction<CacheType>) { export async function interactionCreate(interaction: Interaction<CacheType>) {
if (interaction.isAutocomplete()) { if (interaction.isAutocomplete()) {
@@ -136,6 +113,6 @@ export async function interactionCreate(interaction: Interaction<CacheType>) {
} else if (interaction.isChatInputCommand()) { } else if (interaction.isChatInputCommand()) {
handleChatInputCommand(interaction); handleChatInputCommand(interaction);
} else if (interaction.isButton()) { } else if (interaction.isButton()) {
handleButton(interaction); // handleButton(interaction);
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
export * from './literals';
export * from './strings'; export * from './strings';
export * from './supportedLocales';
export * from './unwrapper'; export * from './unwrapper';
File renamed without changes.
+80 -30
View File
@@ -1,38 +1,21 @@
import { merge } from 'lodash-es'; import { merge } from 'lodash-es';
import type en from '../strings/en.json'; import en from '../strings/en.json';
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from './supportedLocales'; 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 TranslationTree = { [key: string]: TranslationNode }; export type BlitzKitStrings = typeof en;
export type TranslationNode = string | TranslationTree; const stringsRaw = { en, es, fr, ja, pl, pt, ru, uk, zh };
const files = import.meta.glob('../strings/*.json', { eager: true }); export const SUPPORTED_LOCALES = Object.keys(stringsRaw) as SupportedLocale[];
export const localizedStrings: Record<string, typeof en> = {};
function nuke(object: TranslationTree) { export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
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);
}
}
SUPPORTED_LOCALES.forEach((locale) => {
const defaultStrings = files[`../strings/${DEFAULT_LOCALE}.json`];
const strings = files[`../strings/${locale}.json`];
const mergedStrings = merge({}, defaultStrings, strings);
/**
* For some reason, this won't load on the server on the first render so I am
* not asserting this one lol
*/
if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') nuke(mergedStrings);
localizedStrings[locale] = mergedStrings as typeof en;
});
function redact(string: string) { function redact(string: string) {
const matches = [...string.matchAll(/%s\d+/g)]; const matches = [...string.matchAll(/%s\d+/g)];
@@ -50,3 +33,70 @@ function redact(string: string) {
return characters.join(''); return characters.join('');
} }
function nuke(object: TranslationTree) {
for (const key in object) {
const typedKey = key as keyof typeof object;
if (typeof object[typedKey] === 'string') {
const value = object[typedKey];
object[typedKey] = redact(value as string);
} else nuke(object[typedKey] as TranslationTree);
}
}
const stringsPartial: Partial<Record<SupportedLocale, TranslationTree>> = {};
for (const locale in stringsRaw) {
if (locale === DEFAULT_LOCALE) {
stringsPartial[DEFAULT_LOCALE] = stringsRaw[DEFAULT_LOCALE];
continue;
}
const localeStrings = stringsRaw[locale as SupportedLocale];
stringsPartial[locale as SupportedLocale] = merge(
{},
stringsRaw[DEFAULT_LOCALE],
localeStrings,
);
if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') {
nuke(stringsPartial[locale as SupportedLocale]!);
}
}
export const STRINGS = stringsPartial as Record<
SupportedLocale,
BlitzKitStrings
>;
export type SupportedLocale = keyof typeof stringsRaw;
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
en: 'en',
es: 'es',
fr: 'fr',
ja: 'ja',
pt: 'pt',
ru: 'ru',
uk: 'uk',
zh: 'zh-Hans',
pl: 'pl',
};
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
en: '🇺🇸',
es: '🇪🇸',
fr: '🇫🇷',
ja: '🇯🇵',
pt: '🇵🇹',
ru: '🇷🇺',
uk: '🇺🇦',
zh: '🇨🇳',
pl: '🇵🇱',
};
export type TranslationTree = { [key: string]: TranslationNode };
export type TranslationNode = string | TranslationTree;
-39
View File
@@ -1,39 +0,0 @@
export const SUPPORTED_LOCALES = [
'en',
'es',
'fr',
'ja',
'pl',
'pt',
'ru',
'uk',
'zh',
] as const;
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
en: 'en',
es: 'es',
fr: 'fr',
ja: 'ja',
pt: 'pt',
ru: 'ru',
uk: 'uk',
zh: 'zh-Hans',
pl: 'pl',
};
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
en: '🇺🇸',
es: '🇪🇸',
fr: '🇫🇷',
ja: '🇯🇵',
pt: '🇵🇹',
ru: '🇷🇺',
uk: '🇺🇦',
zh: '🇨🇳',
pl: '🇵🇱',
};
+1 -1
View File
@@ -1,5 +1,5 @@
import { I18nString } from '@blitzkit/core'; import { I18nString } from '@blitzkit/core';
import { DEFAULT_LOCALE } from './supportedLocales'; import { DEFAULT_LOCALE } from './strings';
export function unwrapper(locale: string) { export function unwrapper(locale: string) {
return function (i18nString: I18nString) { return function (i18nString: I18nString) {
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n/src/literals';
import { import {
Card, Card,
Flex, Flex,
@@ -8,7 +9,6 @@ import {
} from '@radix-ui/themes'; } from '@radix-ui/themes';
import type { ComponentProps } from 'react'; import type { ComponentProps } from 'react';
import { radToDeg } from 'three/src/math/MathUtils.js'; import { radToDeg } from 'three/src/math/MathUtils.js';
import { literals } from '../../../core/i18n/literals';
import { useLocale } from '../../../hooks/useLocale'; import { useLocale } from '../../../hooks/useLocale';
import type { Shot, ShotLayer } from '../../../stores/tankopediaEphemeral'; import type { Shot, ShotLayer } from '../../../stores/tankopediaEphemeral';
import { shotStatusColors } from './ShotDisplay'; import { shotStatusColors } from './ShotDisplay';
@@ -3,6 +3,7 @@ import {
type AccountListItem, type AccountListItem,
type AccountListWithServer, type AccountListWithServer,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons'; import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
import { import {
Badge, Badge,
@@ -18,7 +19,6 @@ import { debounce } from 'lodash-es';
import { useCallback, useRef, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
import { stringify } from 'urlon'; import { stringify } from 'urlon';
import type { embedConfigurations } from '../../constants/embeds'; import type { embedConfigurations } from '../../constants/embeds';
import { literals } from '../../core/i18n/literals';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { EmbedState, type EmbedStateStore } from '../../stores/embedState'; import { EmbedState, type EmbedStateStore } from '../../stores/embedState';
import { CopyButton } from '../CopyButton'; import { CopyButton } from '../CopyButton';
@@ -1,8 +1,8 @@
import { literals } from '@blitzkit/i18n/src/literals';
import { Flex, Text } from '@radix-ui/themes'; import { Flex, Text } from '@radix-ui/themes';
import fuzzysort from 'fuzzysort'; import fuzzysort from 'fuzzysort';
import { times } from 'lodash-es'; import { times } from 'lodash-es';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { literals } from '../../core/i18n/literals';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { GalleryEphemeral } from '../../stores/galleryEphemeral'; import { GalleryEphemeral } from '../../stores/galleryEphemeral';
import { GalleryCard } from './Card'; import { GalleryCard } from './Card';
@@ -2,7 +2,7 @@
import { Code, Flex, Heading, Text } from '@radix-ui/themes'; import { Code, Flex, Heading, Text } from '@radix-ui/themes';
import { resolveBranchName } from '../core/blitzkit/branches'; import { resolveBranchName } from '../core/blitzkit/branches';
import { getStrings } from '../core/i18n/getStrings'; import { getStrings } from '../core/i18n/getStrings';
import { literals } from '../core/i18n/literals'; import { literals } from '@blitzkit/i18n/src/literals';
const { locale } = Astro.params; const { locale } = Astro.params;
const branchName = resolveBranchName(locale); const branchName = resolveBranchName(locale);
@@ -1,9 +1,9 @@
import { assertSecret } from '@blitzkit/core'; import { assertSecret } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { EyeOpenIcon } from '@radix-ui/react-icons'; import { EyeOpenIcon } from '@radix-ui/react-icons';
import { Box, Flex, Heading, Text } from '@radix-ui/themes'; import { Box, Flex, Heading, Text } from '@radix-ui/themes';
import { google } from 'googleapis'; import { google } from 'googleapis';
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions';
import { literals } from '../core/i18n/literals';
import { import {
LocaleProvider, LocaleProvider,
useLocale, useLocale,
@@ -1,9 +1,9 @@
import { formatCompact } from '@blitzkit/core'; import { formatCompact } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { InfoCircledIcon } from '@radix-ui/react-icons'; import { InfoCircledIcon } from '@radix-ui/react-icons';
import { Callout, Flex } from '@radix-ui/themes'; import { Callout, Flex } from '@radix-ui/themes';
import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions';
import { awaitableDiscoveredIdsDefinitions } from '../../core/awaitables/discoveredIdsDefinitions'; import { awaitableDiscoveredIdsDefinitions } from '../../core/awaitables/discoveredIdsDefinitions';
import { literals } from '../../core/i18n/literals';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps'; import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
import { InlineSkeleton } from '../InlineSkeleton'; import { InlineSkeleton } from '../InlineSkeleton';
@@ -5,6 +5,7 @@ import {
unionBoundingBox, unionBoundingBox,
type TankDefinition, type TankDefinition,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { Callout, Flex, Link, Text, type FlexProps } from '@radix-ui/themes'; import { Callout, Flex, Link, Text, type FlexProps } from '@radix-ui/themes';
import fuzzysort from 'fuzzysort'; import fuzzysort from 'fuzzysort';
@@ -16,7 +17,6 @@ import { awaitableTankDefinitions } from '../../core/awaitables/tankDefinitions'
import { awaitableTankNames } from '../../core/awaitables/tankNames'; import { awaitableTankNames } from '../../core/awaitables/tankNames';
import { filterTank } from '../../core/blitzkit/filterTank'; import { filterTank } from '../../core/blitzkit/filterTank';
import { resolveReload } from '../../core/blitzkit/resolveReload'; import { resolveReload } from '../../core/blitzkit/resolveReload';
import { literals } from '../../core/i18n/literals';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { $tankFilters } from '../../stores/tankFilters'; import { $tankFilters } from '../../stores/tankFilters';
import { TankopediaPersistent } from '../../stores/tankopediaPersistent'; import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
@@ -1,6 +1,6 @@
import { TIER_ROMAN_NUMERALS } from '@blitzkit/core'; import { TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { Flex, Heading, SegmentedControl, Text } from '@radix-ui/themes'; import { Flex, Heading, SegmentedControl, Text } from '@radix-ui/themes';
import { literals } from '../../../../../../core/i18n/literals';
import { useLocale } from '../../../../../../hooks/useLocale'; import { useLocale } from '../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../stores/duel'; import { Duel } from '../../../../../../stores/duel';
import { import {
@@ -6,6 +6,7 @@ import {
isExplosive, isExplosive,
resolvePenetrationCoefficient, resolvePenetrationCoefficient,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { AccessibilityIcon, InfoCircledIcon } from '@radix-ui/react-icons'; import { AccessibilityIcon, InfoCircledIcon } from '@radix-ui/react-icons';
import { import {
Flex, Flex,
@@ -25,7 +26,6 @@ import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/eq
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics'; import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
import { literals } from '../../../../../core/i18n/literals';
import { useEquipment } from '../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../hooks/useEquipment';
import { useLocale } from '../../../../../hooks/useLocale'; import { useLocale } from '../../../../../hooks/useLocale';
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition'; import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
@@ -1,4 +1,5 @@
import { imgur, ShellType } from '@blitzkit/core'; import { imgur, ShellType } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { import {
Button, Button,
Flex, Flex,
@@ -11,7 +12,6 @@ import {
} from '@radix-ui/themes'; } from '@radix-ui/themes';
import { throttle } from 'lodash-es'; import { throttle } from 'lodash-es';
import { type ChangeEvent } from 'react'; import { type ChangeEvent } from 'react';
import { literals } from '../../../../../../core/i18n/literals';
import { useLocale } from '../../../../../../hooks/useLocale'; import { useLocale } from '../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../stores/duel'; import { Duel } from '../../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
@@ -1,7 +1,7 @@
import { literals } from '@blitzkit/i18n/src/literals';
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons'; import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
import { Button, Checkbox, Flex, Text } from '@radix-ui/themes'; import { Button, Checkbox, Flex, Text } from '@radix-ui/themes';
import { Suspense } from 'react'; import { Suspense } from 'react';
import { literals } from '../../../../../../core/i18n/literals';
import { useLocale } from '../../../../../../hooks/useLocale'; import { useLocale } from '../../../../../../hooks/useLocale';
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent'; import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
@@ -5,6 +5,7 @@ import {
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
uniqueGuns, uniqueGuns,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { import {
CameraIcon, CameraIcon,
CopyIcon, CopyIcon,
@@ -30,7 +31,6 @@ import {
} from '@radix-ui/themes'; } from '@radix-ui/themes';
import { Suspense, useState, type RefObject } from 'react'; import { Suspense, useState, type RefObject } from 'react';
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose'; import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
import { literals } from '../../../../../core/i18n/literals';
import { useEquipment } from '../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../hooks/useEquipment';
import { useFullScreen } from '../../../../../hooks/useFullScreen'; import { useFullScreen } from '../../../../../hooks/useFullScreen';
import { useFullscreenAvailability } from '../../../../../hooks/useFullscreenAvailability'; import { useFullscreenAvailability } from '../../../../../hooks/useFullscreenAvailability';
@@ -1,8 +1,8 @@
import { asset, formatCompact, TIER_ROMAN_NUMERALS } from '@blitzkit/core'; import { asset, formatCompact, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { Box, Flex, Skeleton, Text } from '@radix-ui/themes'; import { Box, Flex, Skeleton, Text } from '@radix-ui/themes';
import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions';
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
import { literals } from '../../../../core/i18n/literals';
import { useLocale } from '../../../../hooks/useLocale'; import { useLocale } from '../../../../hooks/useLocale';
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps'; import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps';
@@ -1,9 +1,9 @@
import { asset, imgur, TankType } from '@blitzkit/core'; import { asset, imgur, TankType } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons'; import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes'; import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'; import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
import { literals } from '../../../core/i18n/literals';
import { useLocale } from '../../../hooks/useLocale'; import { useLocale } from '../../../hooks/useLocale';
import { Duel } from '../../../stores/duel'; import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
@@ -1,7 +1,7 @@
import { youtubers } from '@blitzkit/core'; import { youtubers } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { Box, Flex, Heading, Link, Skeleton, Text } from '@radix-ui/themes'; import { Box, Flex, Heading, Link, Skeleton, Text } from '@radix-ui/themes';
import { awaitableReviews } from '../../core/awaitables/reviews'; import { awaitableReviews } from '../../core/awaitables/reviews';
import { literals } from '../../core/i18n/literals';
import { Var } from '../../core/radix/var'; import { Var } from '../../core/radix/var';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { Duel } from '../../stores/duel'; import { Duel } from '../../stores/duel';
@@ -21,7 +21,7 @@ import {
tierListTool, tierListTool,
} from '../../constants/tools'; } from '../../constants/tools';
import { getStrings } from '../../core/i18n/getStrings'; import { getStrings } from '../../core/i18n/getStrings';
import { literals } from '../../core/i18n/literals'; import { literals } from '@blitzkit/i18n/src/literals';
import BaseLayout from '../../layouts/BaseLayout.astro'; import BaseLayout from '../../layouts/BaseLayout.astro';
const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH); const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
@@ -1,9 +1,9 @@
import { literals } from '@blitzkit/i18n/src/literals';
import { CalendarIcon } from '@radix-ui/react-icons'; import { CalendarIcon } from '@radix-ui/react-icons';
import { Callout, Flex } from '@radix-ui/themes'; import { Callout, Flex } from '@radix-ui/themes';
import { GalleryList, type Avatar } from '../../../../components/Gallery/List'; import { GalleryList, type Avatar } from '../../../../components/Gallery/List';
import { GallerySearch } from '../../../../components/Gallery/Search'; import { GallerySearch } from '../../../../components/Gallery/Search';
import { PageWrapper } from '../../../../components/PageWrapper'; import { PageWrapper } from '../../../../components/PageWrapper';
import { literals } from '../../../../core/i18n/literals';
import { import {
LocaleProvider, LocaleProvider,
useLocale, useLocale,
@@ -13,6 +13,7 @@ import {
type IndividualAccountInfo, type IndividualAccountInfo,
type IndividualTankStats, type IndividualTankStats,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { import {
ArrowDownIcon, ArrowDownIcon,
MagnifyingGlassIcon, MagnifyingGlassIcon,
@@ -38,7 +39,6 @@ import { StickyTableRoot } from '../../../../components/StickyTableRoot';
import { TankRowHeaderCell } from '../../../../components/TankRowHeaderCell'; import { TankRowHeaderCell } from '../../../../components/TankRowHeaderCell';
import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions';
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
import { literals } from '../../../../core/i18n/literals';
import { import {
LocaleProvider, LocaleProvider,
useLocale, useLocale,
@@ -14,7 +14,7 @@ import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
import type { GetStaticPaths } from 'astro'; import type { GetStaticPaths } from 'astro';
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics'; import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
import { getStrings } from '../../../../../core/i18n/getStrings'; import { getStrings } from '../../../../../core/i18n/getStrings';
import { literals } from '../../../../../core/i18n/literals'; import { literals } from '@blitzkit/i18n/src/literals';
import BaseLayout from '../../../../../layouts/BaseLayout.astro'; import BaseLayout from '../../../../../layouts/BaseLayout.astro';
import { genericDefaultEquipmentMatrix } from '../../../../../stores/duel/constants'; import { genericDefaultEquipmentMatrix } from '../../../../../stores/duel/constants';
import { getStaticPaths as parentGetStaticPaths } from '../index.astro'; import { getStaticPaths as parentGetStaticPaths } from '../index.astro';