Skip to content

Commit

Permalink
translation revamp
Browse files Browse the repository at this point in the history
  • Loading branch information
Dougley committed Feb 18, 2022
1 parent a997c28 commit 67d126e
Show file tree
Hide file tree
Showing 45 changed files with 475 additions and 330 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ IMGUR_KEY=5i8rythbhtx654235hrs34
# WILDBEAST_SHARDING_START=0
# WILDBEAST_SHARDING_END=1
# WILDBEAST_SHARDING_TOTAL=2
# WILDBEAST_LANGUAGE=en-EN
# WILDBEAST_LANGUAGE=en-US
4 changes: 2 additions & 2 deletions crowdin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN

files:
- source: /src/languages/en-EN.ts
translation: /src/languages/%locale%.ts
- source: /src/languages/en-US/**/*.strings.ts
translation: /src/languages/%two_letters_code%/**/%original_file_name%
143 changes: 55 additions & 88 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
},
"homepage": "https://wildbeast.guide/",
"devDependencies": {
"@types/glob": "^7.2.0",
"@types/node": "^17.0.18",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"discord-api-types": "^0.27.1",
Expand All @@ -54,9 +53,10 @@
"@thesharks/jagtag-js": "^2.0.0",
"chalk": "^4.1.2",
"date-fns": "^2.28.0",
"deepmerge": "^4.2.2",
"detritus-client": "^0.17.0-beta.1",
"dotenv": "^16.0.0",
"glob": "^7.2.0",
"fast-glob": "^3.2.11",
"intl-messageformat": "^9.11.4",
"postgres": "^2.0.0-beta.11",
"tslib": "^2.3.1"
Expand Down
2 changes: 1 addition & 1 deletion src/database/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Row, RowList } from 'postgres'
import SQL from '../driver'
import { basename, extname, join, resolve } from 'path'
import { writeFile } from 'fs/promises'
import { glob } from 'glob'
import glob from 'fast-glob'
import { debug, error, info, trace } from '../../utils/logger'

const IS_TS_NODE = Symbol.for('ts-node.register.instance') in process
Expand Down
3 changes: 2 additions & 1 deletion src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RewriteFrames } from '@sentry/integrations'
import { promisify } from 'util'
import { exec } from 'child_process'
import dirImport from './utils/dir-import'
import initLangs from './languages'

info('Starting up...', 'Preflight');

Expand All @@ -25,7 +26,7 @@ info('Starting up...', 'Preflight');
},
release: revision
})
await dirImport('@(dist|src)/languages/**/*.[?jt]s')
await initLangs()
await dirImport('@(dist|src)/events/**/*.[?jt]s')
await client.addMultipleIn('./interactions')
await client.run()
Expand Down
117 changes: 106 additions & 11 deletions src/interactions/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,101 @@ import { error } from '../utils/logger'
const { ApplicationCommandTypes, ApplicationCommandOptionTypes, MessageFlags, Permissions } = Constants

export class BaseInteractionCommand<ParsedArgsFinished = Interaction.ParsedArgs> extends Interaction.InteractionCommand<ParsedArgsFinished> {
translate: typeof translate = translate
translateThis: typeof translate = (key, args) =>
this.translate(`slash-commands.${this.name.toLowerCase()}.${key}`, args)

async onBefore (context: Interaction.InteractionContext): Promise<boolean> {
if (context.locale !== undefined) {
this.translate = (key, args) => translate(key, args, context.locale)
}
return true
}

async onSuccess (context: Interaction.InteractionContext, args: ParsedArgsFinished): Promise<void> {
add({
type: 'command',
guildId: context.guildId,
inDm: context.inDm,
userId: context.user.id,
data: {
args,
interaction_id: context.interactionId
},
name: context.command.name
})
}

async onDmBlocked (context: Interaction.InteractionContext): Promise<unknown> {
return await context.editOrRespond({
content: this.translate('common.dmDisabled'),
flags: MessageFlags.EPHEMERAL
})
}

async onPermissionsFail (context: Interaction.InteractionContext, falied: bigint[]): Promise<void> {
const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value)))
await context.editOrRespond({
content: this.translate('common.permsMissingUser', {
perms: values.map(([key]) => key).join(', ')
}),
flags: MessageFlags.EPHEMERAL
})
}

async onPermissionsFailClient (context: Interaction.InteractionContext, falied: bigint[]): Promise<void> {
const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value)))
await context.editOrRespond({
content: this.translate('common.permsMissingOwn', {
perms: values.map(([key]) => key).join(', ')
}),
flags: MessageFlags.EPHEMERAL
})
}

async onRatelimit (context: Interaction.InteractionContext): Promise<void> {
await context.editOrRespond({
content: this.translate('common.cooldown'),
flags: MessageFlags.EPHEMERAL
})
}

async onRunError (context: Interaction.InteractionContext, args: ParsedArgsFinished, err: any): Promise<unknown> {
const uuid = error(err, context.name, {
user: {
id: context.user.id,
username: context.user.username
},
contexts: {
guild: {
id: context.guildId,
name: context.guild?.name
},
command: {
name: context.command.name,
args
}
}
})
return await context.editOrRespond({
content: translate('common.failedToRun', { uuid }),
flags: MessageFlags.EPHEMERAL
})
}
}

class OptionBase<ParsedArgsFinished = Interaction.ParsedArgs> extends Interaction.InteractionCommandOption<ParsedArgsFinished> {
translate: typeof translate = translate
translateThis: typeof translate = (key, args) =>
this.translate(`context-menu.${this.name.toLowerCase()}.${key}`, args)

async onBefore (context: Interaction.InteractionContext): Promise<boolean> {
if (context.locale !== undefined) {
this.translate = (key, args) => translate(key, args, context.locale)
}
return true
}

async onSuccess (context: Interaction.InteractionContext, args: ParsedArgsFinished): Promise<void> {
add({
type: 'command',
Expand All @@ -21,34 +116,34 @@ export class BaseInteractionCommand<ParsedArgsFinished = Interaction.ParsedArgs>

async onDmBlocked (context: Interaction.InteractionContext): Promise<unknown> {
return await context.editOrRespond({
content: translate('commands.common.dmDisabled'),
content: this.translate('common.dmDisabled'),
flags: MessageFlags.EPHEMERAL
})
}

async onPermissionsFail (context: Interaction.InteractionContext, falied: bigint[]): Promise<void> {
const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value)))
await context.editOrRespond({
content:
"You're missing the following permissions for this command to work:\n" +
values.map(([key, value]) => `\`${key}\``).join('\n'),
content: this.translate('common.permsMissingUser', {
perms: values.map(([key]) => key).join(', ')
}),
flags: MessageFlags.EPHEMERAL
})
}

async onPermissionsFailClient (context: Interaction.InteractionContext, falied: bigint[]): Promise<void> {
const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value)))
await context.editOrRespond({
content:
"I'm missing the following permissions for this command to work:\n" +
values.map(([key, value]) => `\`${key}\``).join('\n'),
content: this.translate('common.permsMissingOwn', {
perms: values.map(([key]) => key).join(', ')
}),
flags: MessageFlags.EPHEMERAL
})
}

async onRatelimit (context: Interaction.InteractionContext): Promise<void> {
await context.editOrRespond({
content: 'You are doing that too much, please wait a bit.',
content: this.translate('common.cooldown'),
flags: MessageFlags.EPHEMERAL
})
}
Expand All @@ -71,17 +166,17 @@ export class BaseInteractionCommand<ParsedArgsFinished = Interaction.ParsedArgs>
}
})
return await context.editOrRespond({
content: translate('commands.common.failedToRun', { uuid }),
content: translate('common.failedToRun', { uuid }),
flags: MessageFlags.EPHEMERAL
})
}
}

export class BaseCommandOption<ParsedArgsFinished = Interaction.ParsedArgs> extends Interaction.InteractionCommandOption<ParsedArgsFinished> {
export class BaseCommandOption<ParsedArgsFinished = Interaction.ParsedArgs> extends OptionBase<ParsedArgsFinished> {
type = ApplicationCommandOptionTypes.SUB_COMMAND
}

export class BaseCommandOptionGroup<ParsedArgsFinished = Interaction.ParsedArgs> extends Interaction.InteractionCommandOption<ParsedArgsFinished> {
export class BaseCommandOptionGroup<ParsedArgsFinished = Interaction.ParsedArgs> extends OptionBase<ParsedArgsFinished> {
type = ApplicationCommandOptionTypes.SUB_COMMAND_GROUP
}

Expand Down
8 changes: 4 additions & 4 deletions src/interactions/slash-commands/8ball.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Interaction } from 'detritus-client'
import { translate, traverse } from '../../utils/i18n'
import { traverse } from '../../utils/i18n'

import { BaseSlashCommand } from '../base'

export default class EightBallCommand extends BaseSlashCommand {
description = 'Ask the magic 8-ball for advice'
name = '8ball'
description = this.translateThis('metadata.description')

async run (context: Interaction.InteractionContext): Promise<void> {
const length = traverse('commands.8ball.choices.length')
await context.editOrRespond(translate('commands.8ball.prefix', { response: translate(`commands.8ball.choices.${Math.floor(Math.random() * length)}`) }))
const length = traverse('slash-commands.8ball.choices.length')
await context.editOrRespond(this.translateThis('prefix', { response: this.translateThis(`choices.${Math.floor(Math.random() * length)}`) }))
}
}
2 changes: 1 addition & 1 deletion src/interactions/slash-commands/advice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import fetch from 'node-fetch'
import { BaseSlashCommand } from '../base'

export default class AdviceCommand extends BaseSlashCommand {
description = 'Get some helpful advice'
name = 'advice'
description = this.translateThis('metadata.description')

async run (context: Interaction.InteractionContext): Promise<void> {
const advice = await (await fetch('https://api.adviceslip.com/advice')).json()
Expand Down
10 changes: 5 additions & 5 deletions src/interactions/slash-commands/booru/booru.derpibooru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption {
async onBeforeRun (context: Interaction.InteractionContext): Promise<boolean> {
if (!context.inDm && !context.channel!.nsfw) {
await context.editOrRespond({
content: translate('commands.common.nsfwDisabled'),
content: translate('common.nsfwDisabled'),
flags: MessageFlags.EPHEMERAL
})
return false
Expand All @@ -34,7 +34,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption {
options: [
{
name: 'query',
description: 'What to search for',
description: translate('slash-commands.booru.metadata.options.query'),
required: true
}
]
Expand All @@ -53,14 +53,14 @@ export class BooruDerpibooruCommand extends BaseCommandOption {
}
})).json()
if (json.total === 0) {
await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query }))
await context.editOrRespond(translate('common.noResultsFor', { query: args.query }))
} else {
const post = json.images[position]
const artist: string = post.tags.filter((x: string) => x.startsWith('artist:'))[0].slice('artist:'.length) ?? 'Unknown'
const embed = new Embed()
.setAuthor(artist, 'https://i.imgur.com/f556NmB.png', post.source_url)
.setImage(post.representations.full)
.addField('Score', `${post.upvotes as string} 👍 ${post.downvotes as string} 👎`, true)
.addField(this.translateThis('score'), `${post.upvotes as string} 👍 ${post.downvotes as string} 👎`, true)
.setFooter('derpibooru.org', 'https://i.imgur.com/f556NmB.png')
const components = new Components({
timeout: 5 * (60 * 1000),
Expand Down Expand Up @@ -89,7 +89,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption {
// workaround: detritus sets customIds even when its not needed
const urlButton = new ComponentButton({
style: MessageComponentButtonStyles.LINK,
label: translate('commands.common.open'),
label: translate('common.open'),
url: `https://derpibooru.org/images/${post.id as string}`
})
delete urlButton.customId
Expand Down
10 changes: 5 additions & 5 deletions src/interactions/slash-commands/booru/booru.e621.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class BooruE621Command extends BaseCommandOption {
options: [
{
name: 'query',
description: 'What to search for',
description: translate('slash-commands.booru.metadata.options.query'),
required: true,
async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise<void> {
const chunks = context.value.split(' ')
Expand Down Expand Up @@ -63,15 +63,15 @@ export class BooruE621Command extends BaseCommandOption {
}
})).json()
if (json.posts.length === 0) {
await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query }))
await context.editOrRespond(translate('common.noResultsFor', { query: args.query }))
} else {
const post = json.posts[position]
const artist: string = post.tags.artist.filter((x: string) => !['conditional_dnp'].includes(x))[0] ?? 'Unknown'
const embed = new Embed()
.setAuthor(artist, 'https://en.wikifur.com/w/images/d/dd/E621Logo.png', artist === 'Unknown' ? undefined : `https://e621.net/artists/show_or_new?name=${encodeURIComponent(artist)}`)
.setImage(post.file.url)
.addField('Score', `${post.score.up as string} 👍 ${post.score.down as string} 👎`, true)
.addField('Favorites', post.fav_count, true)
.addField(this.translateThis('score'), `${post.score.up as string} 👍 ${post.score.down as string} 👎`, true)
.addField(this.translateThis('favorites'), post.fav_count, true)
if (context.channel !== null && !context.channel.nsfw) embed.setFooter('e926.net - NSFW disabled', 'https://en.wikifur.com/w/images/d/dd/E621Logo.png')
else embed.setFooter('e621.net', 'https://en.wikifur.com/w/images/d/dd/E621Logo.png')
const components = new Components({
Expand Down Expand Up @@ -101,7 +101,7 @@ export class BooruE621Command extends BaseCommandOption {
// workaround: detritus sets customIds even when its not needed
const urlButton = new ComponentButton({
style: MessageComponentButtonStyles.LINK,
label: translate('commands.common.open'),
label: translate('common.open'),
url: `https://e621.net/posts/${post.id as string}`
})
delete urlButton.customId
Expand Down
10 changes: 5 additions & 5 deletions src/interactions/slash-commands/booru/booru.gelbooru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class BooruGelbooruCommand extends BaseCommandOption {
async onBeforeRun (context: Interaction.InteractionContext): Promise<boolean> {
if (!context.inDm && !context.channel!.nsfw) {
await context.editOrRespond({
content: translate('commands.common.nsfwDisabled'),
content: translate('common.nsfwDisabled'),
flags: MessageFlags.EPHEMERAL
})
return false
Expand All @@ -34,7 +34,7 @@ export class BooruGelbooruCommand extends BaseCommandOption {
options: [
{
name: 'query',
description: 'What to search for',
description: translate('slash-commands.booru.metadata.options.query'),
required: true
}
]
Expand All @@ -58,12 +58,12 @@ export class BooruGelbooruCommand extends BaseCommandOption {
}
})).json()
if (json.length === 0) {
await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query }))
await context.editOrRespond(translate('common.noResultsFor', { query: args.query }))
} else {
const post = json[position]
const embed = new Embed()
.setImage(post.file_url)
.addField('Score', post.score, true)
.addField(this.translateThis('score'), post.score, true)
.setFooter('gelbooru.com')
const components = new Components({
timeout: 5 * (60 * 1000),
Expand Down Expand Up @@ -92,7 +92,7 @@ export class BooruGelbooruCommand extends BaseCommandOption {
// workaround: detritus sets customIds even when its not needed
const urlButton = new ComponentButton({
style: MessageComponentButtonStyles.LINK,
label: translate('commands.common.open'),
label: translate('common.open'),
url: `https://gelbooru.com/index.php?page=post&s=view&id=${post.id as string}`
})
delete urlButton.customId
Expand Down
Loading

0 comments on commit 67d126e

Please sign in to comment.