Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add source views to /translate and /recognise to see exactly where the translations and detections came from. #352

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,444 changes: 723 additions & 721 deletions assets/localisations/commands/eng-US.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions source/constants/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,12 @@ export default Object.freeze({
translation: localise("game.strings.translation", locale)(),
sourcedFrom: localise("game.strings.sourcedFrom", locale),
}),
translationsSourcedFrom: ({ localise, locale }) => ({
sourcedFrom: localise("translate.strings.sourcedFrom", locale),
}),
recognitionsMadeBy: ({ localise, locale }) => ({
recognitionsMadeBy: localise("recognise.strings.recognitionsMadeBy", locale)(),
}),
pardoned: ({ localise, locale }) => ({
title: localise("pardon.strings.pardoned.title", locale)(),
description: localise("pardon.strings.pardoned.description", locale),
Expand Down
42 changes: 37 additions & 5 deletions source/constants/licences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import apache from "logos:constants/licences/apache";
import bsd from "logos:constants/licences/bsd";
import mit from "logos:constants/licences/mit";

interface DictionaryLicence {
interface Licence {
readonly name: string;
readonly link: string;
readonly faviconLink?: string;
readonly notices: {
readonly notices?: {
readonly licence: string;
readonly copyright?: string;
readonly badgeLink?: string;
Expand Down Expand Up @@ -72,7 +72,39 @@ You must also include, in your app or site, wherever you provide attributions or
"You may cache and thus store API Data on your system for up to 24 hours, after which such cached API Data must be purged. Subject to that exception, you will not copy, store, archive, distribute to any third party (other than to End Users as contemplated in this Agreement) any API Data, any metadata or any Link. You agree that any cached API Data will be used by you only for the purpose of populating the Developer Application.",
},
},
} satisfies Record<string, DictionaryLicence>,
} satisfies Record<string, Licence>,
translators: {
deepl: {
name: "DeepL",
link: "https://www.deepl.com/translator/",
},
googleTranslate: {
name: "Google Translate",
link: "https://translate.google.com/",
},
lingvanex: {
name: "Lingvanex",
link: "https://lingvanex.com/translate/",
},
} satisfies Record<string, Licence>,
detectors: {
cld: {
name: "CLD",
link: "https://npmjs.com/package/cldpre",
},
eld: {
name: "ELD",
link: "https://github.com/vxern/efficient-language-detector-js",
},
fasttext: {
name: "fastText",
link: "https://npmjs.com/package/fasttext.wasm.js",
},
tinyld: {
name: "TinyLD",
link: "https://npmjs.com/package/tinyld",
},
} satisfies Record<string, Licence>,
software: {
"@discordeno/bot": apache("Copyright 2021 - 2023 Discordeno"),
cldpre: apache("Copyright (c) Authors of cldpre"),
Expand Down Expand Up @@ -102,10 +134,10 @@ function isValidDictionary(dictionary: string): dictionary is Dictionary {
return dictionary in licences.dictionaries;
}

function getDictionaryLicenceByDictionary(dictionary: Dictionary): DictionaryLicence {
function getDictionaryLicenceByDictionary(dictionary: Dictionary): Licence {
return licences.dictionaries[dictionary];
}

export default licences;
export { isValidDictionary, getDictionaryLicenceByDictionary };
export type { DictionaryLicence };
export type { Licence };
2 changes: 2 additions & 0 deletions source/library/adapters/detectors/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { DetectionLanguage } from "logos:constants/languages";
import type { Licence } from "logos:constants/licences.ts";
import type { Client } from "logos/client";
import { Logger } from "logos/logger";

interface SingleDetectionResult {
readonly language: DetectionLanguage;
readonly source: Licence;
}

abstract class DetectorAdapter {
Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/detectors/cld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class CLDAdapter extends DetectorAdapter {

const detectedLanguage = getCLDDetectionLanguageByLocale(detectedLocale);

return { language: detectedLanguage };
return { language: detectedLanguage, source: constants.licences.detectors.cld };
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/detectors/eld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ELDAdapter extends DetectorAdapter {

const detectedLanguage = getELDDetectionLanguageByLocale(result.language);

return { language: detectedLanguage };
return { language: detectedLanguage, source: constants.licences.detectors.eld };
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/detectors/fasttext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FastTextAdapter extends DetectorAdapter {

const detectedLanguage = getFastTextDetectionLanguageByLocale(result.alpha3);

return { language: detectedLanguage };
return { language: detectedLanguage, source: constants.licences.detectors.fasttext };
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/detectors/tinyld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class TinyLDAdapter extends DetectorAdapter {

const detectedLanguage = getTinyLDDetectionLanguageByLocale(detectedLocale);

return { language: detectedLanguage };
return { language: detectedLanguage, source: constants.licences.detectors.tinyld };
}
}

Expand Down
4 changes: 2 additions & 2 deletions source/library/adapters/dictionaries/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { LearningLanguage } from "logos:constants/languages";
import type { DictionaryLicence } from "logos:constants/licences";
import type { Licence } from "logos:constants/licences";
import type { PartOfSpeech } from "logos:constants/parts-of-speech";
import type { Client } from "logos/client";
import { Logger } from "logos/logger";
Expand Down Expand Up @@ -68,7 +68,7 @@ interface DictionaryEntry {
/** The inflection of the lemma. */
inflectionTable?: InflectionTable;

sources: [link: string, licence: DictionaryLicence][];
sources: [link: string, licence: Licence][];
}

abstract class DictionaryAdapter<DataType = unknown> {
Expand Down
4 changes: 4 additions & 0 deletions source/library/adapters/translators/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Languages, TranslationLanguage } from "logos:constants/languages";
import type { Licence } from "logos:constants/licences.ts";
import type { Client } from "logos/client";
import { Logger } from "logos/logger";

Expand All @@ -8,6 +9,9 @@ interface TranslationResult {

/** The translation result. */
readonly text: string;

/** The source of the result. */
readonly source: Licence;
}

abstract class TranslatorAdapter<Language extends string = TranslationLanguage> {
Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/translators/deepl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class DeepLAdapter extends TranslatorAdapter<DeepLLanguage> {
? getDeepLTranslationLanguageByLocale(detectedSourceLocale)
: undefined;

return { detectedSourceLanguage, text: translation.text };
return { detectedSourceLanguage, text: translation.text, source: constants.licences.translators.deepl };
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/translators/google-translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class GoogleTranslateAdapter extends TranslatorAdapter<GoogleTranslateLanguage>
? undefined
: getGoogleTranslateLanguageByLocale(detectedSourceLocale);

return { detectedSourceLanguage, text: translatedText };
return { detectedSourceLanguage, text: translatedText, source: constants.licences.translators.googleTranslate };
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/library/adapters/translators/lingvanex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class LingvanexAdapter extends TranslatorAdapter<LingvanexLanguage> {
? undefined
: getLingvanexTranslationLanguageByLocale(detectedSourceLocale);

return { detectedSourceLanguage, text: translatedText };
return { detectedSourceLanguage, text: translatedText, source: constants.licences.translators.lingvanex };
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Licence } from "logos:constants/licences.ts";
import type { Client } from "logos/client.ts";
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";

class RecognitionSourceNotice extends SourceNotice {
constructor(client: Client, { interaction, sources }: { interaction: Logos.Interaction; sources: Licence[] }) {
const strings = constants.contexts.recognitionsMadeBy({
localise: client.localise.bind(client),
locale: interaction.displayLocale,
});

super(client, {
interaction,
sources: sources.map((source) => `[${source.name}](${source.link})`),
notice: strings.recognitionsMadeBy,
});
}
}

export { RecognitionSourceNotice };
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ abstract class SourceNotice {
readonly #buttonPresses: InteractionCollector;
readonly #interaction: Logos.Interaction;
readonly #sources: string[];
readonly #notice: string;
readonly #notice?: string;

get button(): Discord.ButtonComponent {
const strings = constants.contexts.source({
Expand All @@ -25,7 +25,7 @@ abstract class SourceNotice {

constructor(
client: Client,
{ interaction, sources, notice }: { interaction: Logos.Interaction; sources: string[]; notice: string },
{ interaction, sources, notice }: { interaction: Logos.Interaction; sources: string[]; notice?: string },
) {
this.client = client;

Expand All @@ -45,7 +45,7 @@ abstract class SourceNotice {
{
description: `${constants.emojis.link} ${sourcesFormatted}`,
color: constants.colours.blue,
footer: { text: this.#notice },
footer: this.#notice !== undefined ? { text: this.#notice } : undefined,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";
import type { Client } from "logos/client.ts";
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";

class TatoebaSourceNotice extends SourceNotice {
constructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Licence } from "logos:constants/licences.ts";
import type { Client } from "logos/client.ts";
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";

class TranslationSourceNotice extends SourceNotice {
constructor(client: Client, { interaction, source }: { interaction: Logos.Interaction; source: Licence }) {
const strings = constants.contexts.translationsSourcedFrom({
localise: client.localise.bind(client),
locale: interaction.displayLocale,
});

super(client, {
interaction,
sources: [strings.sourcedFrom({ source: `[${source.name}](${source.link})` })],
});
}
}

export { TranslationSourceNotice };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";
import type { Client } from "logos/client.ts";
import { SourceNotice } from "logos/commands/components/source-notices/source-notice.ts";

class WordSourceNotice extends SourceNotice {
constructor(client: Client, { interaction, sources }: { interaction: Logos.Interaction; sources: string[] }) {
Expand Down
2 changes: 1 addition & 1 deletion source/library/commands/handlers/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { capitalise } from "logos:core/formatting";
import * as levenshtein from "fastest-levenshtein";
import type { Client } from "logos/client";
import { InteractionCollector } from "logos/collectors";
import { TatoebaSourceNotice } from "logos/commands/components/source-notices/tatoeba-source-notice.ts";
import { GuildStatistics } from "logos/models/guild-statistics";
import { User } from "logos/models/user";
import type { SentencePair } from "logos/stores/volatile";
import { TatoebaSourceNotice } from "logos/commands/components/source-notices/tatoeba-source-notice.ts";

function random(max: number): number {
return Math.floor(Math.random() * max);
Expand Down
6 changes: 3 additions & 3 deletions source/library/commands/handlers/licence/dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ async function handleDisplayDictionaryLicence(
iconUrl: licence.faviconLink,
url: licence.link,
},
description: `*${licence.notices.licence}*`,
image: licence.notices.badgeLink !== undefined ? { url: licence.notices.badgeLink } : undefined,
description: licence.notices !== undefined ? `*${licence.notices.licence}*` : undefined,
image: licence.notices?.badgeLink !== undefined ? { url: licence.notices.badgeLink } : undefined,
fields: [
{
name: strings.fields.source,
value: licence.link,
},
...(licence.notices.copyright !== undefined
...(licence.notices?.copyright !== undefined
? [
{
name: strings.fields.copyright,
Expand Down
28 changes: 26 additions & 2 deletions source/library/commands/handlers/recognise.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DetectionLanguage } from "logos:constants/languages";
import { list } from "logos:core/formatting";
import type { Client } from "logos/client";
import { RecognitionSourceNotice } from "logos/commands/components/source-notices/recognition-notice.ts";

async function handleRecogniseLanguageChatInput(
client: Client,
Expand Down Expand Up @@ -69,6 +70,10 @@ async function handleRecogniseLanguage(
return;
}

const sourceNotice = new RecognitionSourceNotice(client, { interaction, sources: detectedLanguages.sources });

await sourceNotice.register();

if (detectedLanguages.likely.length === 1 && detectedLanguages.possible.length === 0) {
const language = detectedLanguages.likely.at(0) as DetectionLanguage | undefined;
if (language === undefined) {
Expand All @@ -79,8 +84,19 @@ async function handleRecogniseLanguage(
...constants.contexts.likelyMatch({ localise: client.localise.bind(client), locale: interaction.locale }),
...constants.contexts.language({ localise: client.localise.bind(client), locale: interaction.locale }),
};

await client.noticed(interaction, {
description: strings.description({ language: strings.language(language) }),
embeds: [
{
description: strings.description({ language: strings.language(language) }),
},
],
components: [
{
type: Discord.MessageComponentTypes.ActionRow,
components: [sourceNotice.button],
},
],
});
return;
}
Expand Down Expand Up @@ -160,7 +176,15 @@ async function handleRecogniseLanguage(
});
}

await client.noticed(interaction, { fields });
await client.noticed(interaction, {
embeds: [{ fields }],
components: [
{
type: Discord.MessageComponentTypes.ActionRow,
components: [sourceNotice.button],
},
],
});
}
}

Expand Down
24 changes: 16 additions & 8 deletions source/library/commands/handlers/translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import languages, {
import { trim } from "logos:core/formatting";
import type { TranslationResult } from "logos/adapters/translators/adapter";
import type { Client } from "logos/client";
import { TranslationSourceNotice } from "logos/commands/components/source-notices/translation-source-notice.ts";

async function handleTranslateChatInputAutocomplete(
client: Client,
Expand Down Expand Up @@ -336,14 +337,21 @@ async function translateText(
];
}

const components: Discord.ActionRow[] | undefined = interaction.parameters.show
? undefined
: [
{
type: Discord.MessageComponentTypes.ActionRow,
components: [client.interactionRepetitionService.getShowButton(interaction)],
},
];
const sourceNotice = new TranslationSourceNotice(client, { interaction, source: translation.source });

await sourceNotice.register();

const components: Discord.ActionRow[] = [
{
type: Discord.MessageComponentTypes.ActionRow,
components: [
...(interaction.parameters.show
? []
: [client.interactionRepetitionService.getShowButton(interaction)]),
sourceNotice.button,
] as [Discord.ButtonComponent],
},
];

await client.noticed(interaction, { embeds, components });
}
Expand Down
Loading