diff --git a/src/commands/about.ts b/src/commands/about.ts index 961d61abd..d328d9107 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,52 +1,33 @@ -import { - SlashCommandBuilder, - SlashCommandSubcommandsOnlyBuilder, -} from 'discord.js'; +import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { translator } from '../core/localization/translator'; import { COMMANDS_RAW, CommandRegistry } from '../events/interactionCreate'; -const RAW_PATH = `https://raw.githubusercontent.com/tresabhi/blitzkrieg/main/docs/`; -const DOCS = { +const subcommands = [ + 'embeds', + 'introduction', + 'invite', + 'timezones', + 'creators', +] as const; +type Subcommand = (typeof subcommands)[number]; + +const DOCS: Record = { embeds: 'guide/embeds', introduction: 'guide/introduction', invite: 'guide/invite', timezones: 'guide/timezones', creators: 'guide/creators', }; -const DOC_DESCRIPTIONS: Record = { - embeds: 'How embeds work', - introduction: 'About the bot', - invite: 'How to invite the bot', - timezones: 'How Blitzkrieg infers and uses your timezone', - creators: 'Who created the bot', -}; - -function addDocsSubcommands(option: SlashCommandSubcommandsOnlyBuilder) { - Object.entries(DOC_DESCRIPTIONS).forEach(([key, value]) => { - option.addSubcommand((sub) => sub.setName(key).setDescription(value)); - }); - - return option; -} export const aboutCommand = new Promise(async (resolve) => { resolve({ inProduction: true, inPublic: true, - command: addDocsSubcommands( - new SlashCommandBuilder() - .setName('about') - .setNameLocalizations({ - // "es-ES": - }) - .setDescription('All the info you need about the bot') - .addSubcommand((option) => - option - .setName('commands') - .setDescription('List of all commands and their descriptions'), - ), - ), + command: await createLocalizedCommand('about', [ + { subcommand: 'commands' }, + ...subcommands.map((subcommand) => ({ subcommand })), + ]), async handler(interaction) { const subcommand = interaction.options.getSubcommand(); @@ -57,15 +38,29 @@ export const aboutCommand = new Promise(async (resolve) => { await Promise.all(COMMANDS_RAW) ) .filter((registry) => registry.inPublic && registry.inProduction) - .sort((a, b) => (a.command.name < b.command.name ? -1 : 1)) + .sort((a, b) => + (a.command.name_localizations?.[interaction.locale] ?? + a.command.name) < + (b.command.name_localizations?.[interaction.locale] ?? + b.command.name) + ? -1 + : 1, + ) .map( (registry) => - `- \`/${registry.command.name}\`: ${registry.command.description}`, + `- \`/${ + registry.command.name_localizations?.[interaction.locale] ?? + registry.command.name + }\`: ${ + registry.command.description_localizations?.[ + interaction.locale + ] ?? registry.command.description + }`, ) .join('\n')}`; } - const url = `${RAW_PATH}${DOCS[subcommand as keyof typeof DOCS]}.md`; + const url = `https://raw.githubusercontent.com/tresabhi/blitzkrieg/main/docs/${DOCS[subcommand as Subcommand]}.md`; const content = fetch(url).then((response) => response.text()); return content; diff --git a/src/core/discord/createLocalizedCommand.ts b/src/core/discord/createLocalizedCommand.ts new file mode 100644 index 000000000..89e6fa1c1 --- /dev/null +++ b/src/core/discord/createLocalizedCommand.ts @@ -0,0 +1,48 @@ +import { Locale, SlashCommandBuilder } from 'discord.js'; +import { translator } from '../localization/translator'; +import { localizationObject } from './localizationObject'; + +interface CreateLocalizedCommandSubcommand { + subcommand: string; +} + +export async function createLocalizedCommand( + command: string, + subcommands?: CreateLocalizedCommandSubcommand[], +) { + const { translate } = await translator(Locale.EnglishUS); + const slashCommand = new SlashCommandBuilder() + .setName(command) + .setNameLocalizations(await localizationObject(`bot.commands.${command}`)) + .setDescription(translate(`bot.commands.${command}.description`)) + .setDescriptionLocalizations( + await localizationObject(`bot.commands.${command}.description`), + ); + + if (subcommands) { + await Promise.all( + subcommands.map(async ({ subcommand }) => { + const subcommandNameLocalizations = await localizationObject( + `bot.commands.${command}.subcommands.${subcommand}`, + ); + const subcommandDescriptionLocalizations = await localizationObject( + `bot.commands.${command}.subcommands.${subcommand}.description`, + ); + + slashCommand.addSubcommand((option) => + option + .setName(subcommand) + .setNameLocalizations(subcommandNameLocalizations) + .setDescription( + translate( + `bot.commands.${command}.subcommands.${subcommand}.description`, + ), + ) + .setDescriptionLocalizations(subcommandDescriptionLocalizations), + ); + }), + ); + } + + return slashCommand; +} diff --git a/src/core/discord/localizationObject.ts b/src/core/discord/localizationObject.ts new file mode 100644 index 000000000..0ba60c83b --- /dev/null +++ b/src/core/discord/localizationObject.ts @@ -0,0 +1,17 @@ +import { Locale } from 'discord.js'; +import { locales } from '../localization/strings'; +import { translator } from '../localization/translator'; + +export async function localizationObject(path: string) { + return ( + await Promise.all(locales.map(async (locale) => await translator(locale))) + ).reduce>>( + (localizations, { locale, translate }) => { + return { + ...localizations, + [locale]: translate(path), + }; + }, + {}, + ); +} diff --git a/src/core/localization/strings.ts b/src/core/localization/strings.ts index eb479c46b..c72f48624 100644 --- a/src/core/localization/strings.ts +++ b/src/core/localization/strings.ts @@ -1,6 +1,6 @@ import { Locale } from 'discord.js'; -const LOCALES: Locale[number][] = ['en-US', 'es-ES']; +export const locales: Locale[] = [Locale.EnglishUS, Locale.SpanishES]; export type TranslationFragmentTree = { [key: string]: TranslationFragment; @@ -10,7 +10,7 @@ export type TranslationFragmentTree = { export type TranslationFragment = string | TranslationFragmentTree; export const translations = Promise.all( - LOCALES.map(async (locale) => ({ + locales.map(async (locale) => ({ locale, translations: await import(`../../lang/${locale}.json`), })), @@ -22,5 +22,5 @@ export const translations = Promise.all( [locale]: translations, }), {}, - ) as Record, + ) as Record, ); diff --git a/src/core/localization/translator.ts b/src/core/localization/translator.ts index 81629e9e7..8271f765f 100644 --- a/src/core/localization/translator.ts +++ b/src/core/localization/translator.ts @@ -1,7 +1,8 @@ import { Locale } from 'discord.js'; -import { translations } from './strings'; +import { locales, translations } from './strings'; -export async function translator(locale: Locale) { +export async function translator(localeRaw: Locale) { + const locale = locales.includes(localeRaw) ? localeRaw : Locale.EnglishUS; const strings = (await translations)[locale]; function translate(path: string) { @@ -45,5 +46,5 @@ export async function translator(locale: Locale) { .join(''); } - return { translate, t }; + return { locale, translate, t }; }