diff --git a/packages/bot/src/commands/breakdown/render.tsx b/packages/bot/src/commands/breakdown/render.tsx index 3fe9a189c..19617b8bf 100644 --- a/packages/bot/src/commands/breakdown/render.tsx +++ b/packages/bot/src/commands/breakdown/render.tsx @@ -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={[ { diff --git a/packages/bot/src/commands/research.tsx b/packages/bot/src/commands/research.tsx index 0c539303c..aca660c97 100644 --- a/packages/bot/src/commands/research.tsx +++ b/packages/bot/src/commands/research.tsx @@ -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((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((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((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((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((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((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((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,18 +186,18 @@ export const researchCommand = new Promise((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.research_cost !== undefined && - typeof gun.research_cost.research_cost_type!.value === 'number' + typeof gun.research_cost.research_cost_type!.value === "number" ) { gunXps.set(gun.id, gun.research_cost.research_cost_type!.value); } @@ -208,11 +207,11 @@ export const researchCommand = new Promise((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 ); } }); @@ -220,11 +219,11 @@ export const researchCommand = new Promise((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 ); } }); @@ -249,10 +248,10 @@ export const researchCommand = new Promise((resolve) => { purchase: tank.price.value, equipment: equipmentPriceMatrix[tank.tier].reduce( (a, b) => a + 3 * b, - 0, + 0 ), }; - }), + }) ); const image = ( @@ -267,14 +266,14 @@ export const researchCommand = new Promise((resolve) => {
((resolve) => {
@@ -304,23 +303,23 @@ export const researchCommand = new Promise((resolve) => {
XP icon @@ -330,7 +329,7 @@ export const researchCommand = new Promise((resolve) => { color: theme.colors.textHighContrast, }} > - Experience:{' '} + Experience:{" "} {costs .reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0) .toLocaleString(interaction.locale)} @@ -339,14 +338,14 @@ export const researchCommand = new Promise((resolve) => {
Silver icon @@ -356,7 +355,7 @@ export const researchCommand = new Promise((resolve) => { color: theme.colors.textHighContrast, }} > - Purchase:{' '} + Purchase:{" "} {costs .reduce((a, b) => a + b.purchase, 0) .toLocaleString(interaction.locale)} @@ -365,14 +364,14 @@ export const researchCommand = new Promise((resolve) => {
Silver icon @@ -382,7 +381,7 @@ export const researchCommand = new Promise((resolve) => { color: theme.colors.textHighContrast, }} > - Equipment:{' '} + Equipment:{" "} {costs .reduce((a, b) => a + b.equipment, 0) .toLocaleString(interaction.locale)} @@ -399,7 +398,7 @@ export const researchCommand = new Promise((resolve) => {
((resolve) => {
((resolve) => {
((resolve) => { style={{ height: 100, width: 100, - objectFit: 'contain', - position: 'absolute', - left: '25%', + objectFit: "contain", + position: "absolute", + left: "25%", top: 0, }} />
((resolve) => {
XP icon @@ -504,11 +503,11 @@ export const researchCommand = new Promise((resolve) => { > {literals( strings.bot.commands.research.body.research, - [ - `${costs[index].research?.toLocaleString( - interaction.locale, - )}`, - ], + { + cost: costs[index].research?.toLocaleString( + interaction.locale + ), + } )}
@@ -516,15 +515,15 @@ export const researchCommand = new Promise((resolve) => { {costs[index].upgrades > 0 && (
Free XP icon((resolve) => { > {literals( strings.bot.commands.research.body.upgrades, - [costs[index].upgrades.toLocaleString()], + { cost: costs[index].upgrades.toLocaleString() } )}
@@ -545,15 +544,15 @@ export const researchCommand = new Promise((resolve) => {
Silver icon((resolve) => { > {literals( strings.bot.commands.research.body.purchase, - [ - costs[index].purchase.toLocaleString( - interaction.locale, + { + cost: costs[index].purchase.toLocaleString( + interaction.locale ), - ], + } )}
Silver icon((resolve) => { > {literals( strings.bot.commands.research.body.equipment, - [ - costs[index].equipment.toLocaleString( - interaction.locale, + { + cost: costs[index].equipment.toLocaleString( + interaction.locale ), - ], + } )}
); - }), + }) )}
@@ -621,7 +620,7 @@ export const researchCommand = new Promise((resolve) => { }, autocomplete(interaction) { - autocompleteTanks(interaction, true, ['target-tank', 'starting-tank']); + autocompleteTanks(interaction, true, ["target-tank", "starting-tank"]); }, }); }); diff --git a/packages/bot/src/commands/searchClans.ts b/packages/bot/src/commands/searchClans.ts index 0d481d68f..1c3e29d58 100644 --- a/packages/bot/src/commands/searchClans.ts +++ b/packages/bot/src/commands/searchClans.ts @@ -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((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((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((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(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(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\`\`\`` ); }, }); diff --git a/packages/bot/src/commands/searchPlayers.ts b/packages/bot/src/commands/searchPlayers.ts index 0b766cb76..0097720e4 100644 --- a/packages/bot/src/commands/searchPlayers.ts +++ b/packages/bot/src/commands/searchPlayers.ts @@ -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((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((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(region, 'account/list', { + ? await fetchBlitz(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") + }\`\`\`` ); }, }); diff --git a/packages/bot/src/core/blitz/resolveTankId.ts b/packages/bot/src/core/blitz/resolveTankId.ts index 65eda9de6..4415a8362 100644 --- a/packages/bot/src/core/blitz/resolveTankId.ts +++ b/packages/bot/src/core/blitz/resolveTankId.ts @@ -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, + }) ); } } diff --git a/packages/bot/src/core/blitzkit/filtersToDescription.ts b/packages/bot/src/core/blitzkit/filtersToDescription.ts index a146c58cd..ad6f0131c 100644 --- a/packages/bot/src/core/blitzkit/filtersToDescription.ts +++ b/packages/bot/src/core/blitzkit/filtersToDescription.ts @@ -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)[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(", "); } diff --git a/packages/bot/src/core/discord/addTierChoices.ts b/packages/bot/src/core/discord/addTierChoices.ts index 76ae80cb3..40ac51671 100644 --- a/packages/bot/src/core/discord/addTierChoices.ts +++ b/packages/bot/src/core/discord/addTierChoices.ts @@ -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); } diff --git a/packages/bot/src/core/discord/getPeriodOptionName.ts b/packages/bot/src/core/discord/getPeriodOptionName.ts index d72d8027d..503c8e8da 100644 --- a/packages/bot/src/core/discord/getPeriodOptionName.ts +++ b/packages/bot/src/core/discord/getPeriodOptionName.ts @@ -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 }); } diff --git a/packages/bot/src/core/discord/resolveClanFromCommand.ts b/packages/bot/src/core/discord/resolveClanFromCommand.ts index 6603acacc..30bbb9521 100644 --- a/packages/bot/src/core/discord/resolveClanFromCommand.ts +++ b/packages/bot/src/core/discord/resolveClanFromCommand.ts @@ -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), + }) ); } } diff --git a/packages/bot/src/core/discord/resolvePeriodFromCommand.ts b/packages/bot/src/core/discord/resolvePeriodFromCommand.ts index 4b8555d93..ab6bf37aa 100644 --- a/packages/bot/src/core/discord/resolvePeriodFromCommand.ts +++ b/packages/bot/src/core/discord/resolvePeriodFromCommand.ts @@ -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, - 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 { diff --git a/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts b/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts index 2d6039b40..74115bde3 100644 --- a/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts +++ b/packages/bot/src/core/discord/resolvePlayerFromCommand/index.ts @@ -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, + interaction: ChatInputCommandInteraction ) { 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), + }) ); } } diff --git a/packages/core/src/blitzkit/assertSecret.ts b/packages/core/src/blitzkit/assertSecret.ts index 949e735f9..934379286 100644 --- a/packages/core/src/blitzkit/assertSecret.ts +++ b/packages/core/src/blitzkit/assertSecret.ts @@ -1,4 +1,4 @@ -export function assertSecret(secret?: string) { - if (secret === undefined) throw new Error('Missing secret'); +export function assertSecret(secret?: Type) { + if (secret === undefined) throw new Error("Missing secret"); return secret; } diff --git a/packages/i18n/src/literals.ts b/packages/i18n/src/literals.ts index a467675ce..8f802dd46 100644 --- a/packages/i18n/src/literals.ts +++ b/packages/i18n/src/literals.ts @@ -1,9 +1,19 @@ +import { assertSecret } from "@blitzkit/core"; + +const dev = assertSecret(import.meta.env.DEV); + export function literals( string: string, - literals?: Record + literals?: Record ) { for (const key in literals) { - string = string.replaceAll(`{${key}}`, `${literals[key]}`); + 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; diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index dbcc1f9a7..4fb891092 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -844,7 +844,7 @@ "other_projects": { "title": "Other cool projects" }, - "help_translate": "Please help translate {name} to {language_2}!", + "help_translate": "Please help translate {name} to {language}!", "seo_description": "Tools for everything World of Tanks Blitz" }, @@ -1293,7 +1293,7 @@ "no_results": "No players found" }, "controls": { - "title": "Tracking {count}", + "title": "Tracking {name}", "subtitle": "Since {time}", "reset": "Reset", "change": "Change" diff --git a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx index c90e36a0c..e27c1de5c 100644 --- a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx +++ b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx @@ -53,32 +53,38 @@ function LayerEntry({ {layer.type === null && ( - {literals(strings.common.units.mm, [ - Math.round(layer.distance * 1000).toLocaleString(locale), - ])} + {literals(strings.common.units.mm, { + value: Math.round(layer.distance * 1000).toLocaleString(locale), + })} )} {layer.type === ArmorType.External && ( - {literals(strings.common.units.mm, [ - Math.round(layer.thickness).toLocaleString(locale), - ])} + {literals(strings.common.units.mm, { + value: Math.round(layer.thickness).toLocaleString(locale), + })} )} {(layer.type === ArmorType.Primary || layer.type === ArmorType.Spaced) && ( - {literals( - angle === undefined - ? strings.common.units.mm - : strings.website.tools.tankopedia.sandbox.dynamic.shot_card + {angle === undefined + ? literals(strings.common.units.mm, { + value: Math.round(layer.thicknessAngled).toLocaleString( + locale + ), + }) + : literals( + strings.website.tools.tankopedia.sandbox.dynamic.shot_card .stats.thickness_and_angle, - [ - Math.round(layer.thicknessAngled).toLocaleString(locale), - radToDeg(angle ?? 0).toFixed(0), - ] - )} + { + thickness: Math.round(layer.thicknessAngled).toLocaleString( + locale + ), + angle: radToDeg(angle ?? 0).toFixed(0), + } + )} )} @@ -89,7 +95,7 @@ function LayerEntry({ {literals( strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats .ricochet_loss, - [Math.max(-100, -50 * layer.distance).toFixed(0)] + { loss: Math.max(-100, -50 * layer.distance).toFixed(0) } )} )} @@ -100,7 +106,7 @@ function LayerEntry({ {literals( strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats .nominal, - [Math.round(layer.thickness).toLocaleString(locale)] + { thickness: Math.round(layer.thickness).toLocaleString(locale) } )} )} @@ -132,7 +138,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { strings.website.tools.tankopedia.sandbox.dynamic.shot_card.status[ shot.in.status ], - [Math.round(shot.damage).toLocaleString(locale)] + { damage: Math.round(shot.damage).toLocaleString(locale) } )} @@ -202,7 +208,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { {literals( strings.website.tools.tankopedia.sandbox.dynamic.shot_card .status[shot.out.status], - [Math.round(shot.damage).toLocaleString(locale)] + { damage: Math.round(shot.damage).toLocaleString(locale) } )} diff --git a/packages/website/src/components/Embeds/GenerateURL.tsx b/packages/website/src/components/Embeds/GenerateURL.tsx index ba13ecea0..a02b010ee 100644 --- a/packages/website/src/components/Embeds/GenerateURL.tsx +++ b/packages/website/src/components/Embeds/GenerateURL.tsx @@ -155,7 +155,7 @@ export function GenerateURL({ embed }: GenerateURLProps) { {literals( strings.website.tools.embed.configuration.export.generate .success.info, - [`${copyUser?.nickname}`] + { name: copyUser?.nickname } )} diff --git a/packages/website/src/components/Gallery/List.tsx b/packages/website/src/components/Gallery/List.tsx index a7856d6e7..aa1ac26cf 100644 --- a/packages/website/src/components/Gallery/List.tsx +++ b/packages/website/src/components/Gallery/List.tsx @@ -50,9 +50,9 @@ export function GalleryList({ skeleton }: MaybeSkeletonComponentProps) { {skeleton ? ( ) : ( - literals(strings.website.tools.gallery.search.results, [ - filtered.length.toLocaleString(locale), - ]) + literals(strings.website.tools.gallery.search.results, { + count: filtered.length.toLocaleString(locale), + }) )} diff --git a/packages/website/src/components/Guesser.tsx b/packages/website/src/components/Guesser.tsx index 7520a49a2..5d78c0e43 100644 --- a/packages/website/src/components/Guesser.tsx +++ b/packages/website/src/components/Guesser.tsx @@ -139,10 +139,10 @@ export function Guesser() { - {literals(strings.website.tools.guess.stats, [ - `${correctGuesses}`, - `${totalGuesses}`, - ])} + {literals(strings.website.tools.guess.stats, { + correct: correctGuesses, + total: totalGuesses, + })} diff --git a/packages/website/src/components/HomeHero.astro b/packages/website/src/components/HomeHero.astro index b97d3e91b..56aabdf0c 100644 --- a/packages/website/src/components/HomeHero.astro +++ b/packages/website/src/components/HomeHero.astro @@ -1,8 +1,8 @@ --- -import { Code, Flex, Heading, Text } from '@radix-ui/themes'; -import { resolveBranchName } from '../core/blitzkit/branches'; -import { getStrings } from '../core/i18n/getStrings'; -import { literals } from '@blitzkit/i18n/src/literals'; +import { Code, Flex, Heading, Text } from "@radix-ui/themes"; +import { resolveBranchName } from "../core/blitzkit/branches"; +import { getStrings } from "../core/i18n/getStrings"; +import { literals } from "@blitzkit/i18n/src/literals"; const { locale } = Astro.params; const branchName = resolveBranchName(locale); @@ -45,7 +45,7 @@ const strings = getStrings(locale); ); } - #home-hero[data-variant='pride'] { + #home-hero[data-variant="pride"] { --g-1-dark: var(--crimson-3); --g-1-light: var(--crimson-7); --g-2-dark: var(--orange-3); @@ -58,7 +58,7 @@ const strings = getStrings(locale); --g-7-light: var(--purple-7); } - #home-hero[data-variant='spooky'] { + #home-hero[data-variant="spooky"] { --g-1-dark: var(--orange-3); --g-1-light: var(--orange-7); --g-2-dark: var(--tomato-3); @@ -73,23 +73,23 @@ const strings = getStrings(locale);