generated from nhcarrigan/template
feat: add translations for responses
All checks were successful
Node.js CI / Lint and Test (pull_request) Successful in 39s
All checks were successful
Node.js CI / Lint and Test (pull_request) Successful in 39s
This commit is contained in:
parent
42809c808a
commit
aa8bca7349
218
src/i18n/responses.ts
Normal file
218
src/i18n/responses.ts
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention -- This is the convention for these keys. */
|
||||||
|
/* eslint-disable stylistic/max-len -- These are going to be long strings and that's okay. */
|
||||||
|
import { Locale } from "discord.js";
|
||||||
|
|
||||||
|
export const responses: Record<string, { "no-message-content": string; "subscription-required": string; "translation": string; "unsupported-locale": string }> = {
|
||||||
|
en: {
|
||||||
|
"no-message-content": "No message content found.",
|
||||||
|
"subscription-required":
|
||||||
|
"You must be subscribed to translate messages.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detected {{language}} with {{confidence}}% confidence.",
|
||||||
|
"unsupported-locale": "Unsupported locale.",
|
||||||
|
},
|
||||||
|
[Locale.Indonesian]: {
|
||||||
|
"no-message-content": "Tidak ada konten pesan ditemukan.",
|
||||||
|
"subscription-required":
|
||||||
|
"Anda harus berlangganan untuk menerjemahkan pesan.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Mendeteksi {{language}} dengan kepercayaan {{confidence}}%.",
|
||||||
|
"unsupported-locale": "Lokal tidak didukung.",
|
||||||
|
},
|
||||||
|
es: {
|
||||||
|
"no-message-content": "No se encontró contenido del mensaje.",
|
||||||
|
"subscription-required":
|
||||||
|
"Debes estar suscrito para traducir mensajes.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detectado {{language}} con {{confidence}}% de confianza.",
|
||||||
|
"unsupported-locale": "Lugar no compatible.",
|
||||||
|
},
|
||||||
|
pt: {
|
||||||
|
"no-message-content": "Nenhum conteúdo de mensagem encontrado.",
|
||||||
|
"subscription-required":
|
||||||
|
"Você deve estar inscrito para traduzir mensagens.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detectado {{language}} com {{confidence}}% de confiança.",
|
||||||
|
"unsupported-locale": "Local não suportado.",
|
||||||
|
},
|
||||||
|
[Locale.Czech]: {
|
||||||
|
"no-message-content": "Nebyl nalezen žádný obsah zprávy.",
|
||||||
|
"subscription-required":
|
||||||
|
"Musíte být přihlášeni k překladu zpráv.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detekováno {{language}} s důvěrou {{confidence}}%.",
|
||||||
|
"unsupported-locale": "Nepodporované místo.",
|
||||||
|
},
|
||||||
|
[Locale.Danish]: {
|
||||||
|
"no-message-content": "Ingen beskedindhold fundet.",
|
||||||
|
"subscription-required":
|
||||||
|
"Du skal være tilmeldt for at oversætte beskeder.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detekteret {{language}} med {{confidence}}% tillid.",
|
||||||
|
"unsupported-locale": "Ikke understøttet sted.",
|
||||||
|
},
|
||||||
|
[Locale.Dutch]: {
|
||||||
|
"no-message-content": "Geen berichtinhoud gevonden.",
|
||||||
|
"subscription-required":
|
||||||
|
"U moet zijn geabonneerd om berichten te vertalen.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Gedetecteerd {{language}} met {{confidence}}% vertrouwen.",
|
||||||
|
"unsupported-locale": "Niet-ondersteunde locatie.",
|
||||||
|
},
|
||||||
|
[Locale.Finnish]: {
|
||||||
|
"no-message-content": "Ei viestisisältöä löytynyt.",
|
||||||
|
"subscription-required":
|
||||||
|
"Sinun on tilattava viestien kääntämiseksi.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Havaittu {{language}} {{confidence}}% luottamuksella.",
|
||||||
|
"unsupported-locale": "Ei tuettu paikka.",
|
||||||
|
},
|
||||||
|
[Locale.French]: {
|
||||||
|
"no-message-content": "Aucun contenu de message trouvé.",
|
||||||
|
"subscription-required":
|
||||||
|
"Vous devez être abonné pour traduire les messages.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Détecté {{language}} avec {{confidence}}% de confiance.",
|
||||||
|
"unsupported-locale": "Lieu non pris en charge.",
|
||||||
|
},
|
||||||
|
[Locale.German]: {
|
||||||
|
"no-message-content": "Kein Nachrichteninhalt gefunden.",
|
||||||
|
"subscription-required":
|
||||||
|
"Sie müssen abonniert sein, um Nachrichten zu übersetzen.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Erkannt {{language}} mit {{confidence}}% Vertrauen.",
|
||||||
|
"unsupported-locale": "Nicht unterstützter Ort.",
|
||||||
|
},
|
||||||
|
[Locale.Greek]: {
|
||||||
|
"no-message-content": "Δεν βρέθηκε περιεχόμενο μηνύματος.",
|
||||||
|
"subscription-required":
|
||||||
|
"Πρέπει να είστε συνδρομητής για να μεταφράσετε μηνύματα.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Ανιχνεύθηκε {{language}} με {{confidence}}% εμπιστοσύνη.",
|
||||||
|
"unsupported-locale": "Μη υποστηριζόμενη τοποθεσία.",
|
||||||
|
},
|
||||||
|
[Locale.Hindi]: {
|
||||||
|
"no-message-content": "कोई संदेश सामग्री नहीं मिली।",
|
||||||
|
"subscription-required":
|
||||||
|
"आपको संदेश अनुवाद करने के लिए सब्सक्राइब करना होगा।",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# {{confidence}}% विश्वास के साथ {{language}} का पता लगाया गया।",
|
||||||
|
"unsupported-locale": "असमर्थित स्थान।",
|
||||||
|
},
|
||||||
|
[Locale.Hungarian]: {
|
||||||
|
"no-message-content": "Nem található üzenettartalom.",
|
||||||
|
"subscription-required":
|
||||||
|
"Fel kell iratkoznia az üzenetek fordításához.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# {{language}} érzékelve {{confidence}}% bizalommal.",
|
||||||
|
"unsupported-locale": "Nem támogatott hely.",
|
||||||
|
},
|
||||||
|
[Locale.Italian]: {
|
||||||
|
"no-message-content": "Nessun contenuto del messaggio trovato.",
|
||||||
|
"subscription-required":
|
||||||
|
"Devi essere abbonato per tradurre i messaggi.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Rilevato {{language}} con {{confidence}}% di fiducia.",
|
||||||
|
"unsupported-locale": "Località non supportata.",
|
||||||
|
},
|
||||||
|
[Locale.Japanese]: {
|
||||||
|
"no-message-content": "メッセージコンテンツが見つかりません。",
|
||||||
|
"subscription-required":
|
||||||
|
"メッセージを翻訳するには購読する必要があります。",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# {{confidence}}% の信頼度で {{language}} を検出しました。",
|
||||||
|
"unsupported-locale": "サポートされていないロケール。",
|
||||||
|
},
|
||||||
|
[Locale.Korean]: {
|
||||||
|
"no-message-content": "메시지 내용을 찾을 수 없습니다.",
|
||||||
|
"subscription-required":
|
||||||
|
"메시지를 번역하려면 구독해야 합니다.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# {{confidence}}% 신뢰도로 {{language}} 감지.",
|
||||||
|
"unsupported-locale": "지원되지 않는 로케일.",
|
||||||
|
},
|
||||||
|
[Locale.Lithuanian]: {
|
||||||
|
"no-message-content": "Nerasta jokio pranešimo turinio.",
|
||||||
|
"subscription-required":
|
||||||
|
"Norint išversti žinutes, turite būti prenumeratorius.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Aptikta {{language}} su {{confidence}}% pasitikėjimu.",
|
||||||
|
"unsupported-locale": "Nepalaikomas vietovė.",
|
||||||
|
},
|
||||||
|
[Locale.Polish]: {
|
||||||
|
"no-message-content": "Nie znaleziono treści wiadomości.",
|
||||||
|
"subscription-required":
|
||||||
|
"Aby tłumaczyć wiadomości, musisz być subskrybentem.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Wykryto {{language}} z {{confidence}}% pewnością.",
|
||||||
|
"unsupported-locale": "Nieobsługiwane miejsce.",
|
||||||
|
},
|
||||||
|
sv: {
|
||||||
|
"no-message-content": "Inget meddelandeinnehåll hittades.",
|
||||||
|
"subscription-required":
|
||||||
|
"Du måste prenumerera för att översätta meddelanden.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Upptäckt {{language}} med {{confidence}}% förtroende.",
|
||||||
|
"unsupported-locale": "Ej understödd plats.",
|
||||||
|
},
|
||||||
|
[Locale.Romanian]: {
|
||||||
|
"no-message-content": "Nu s-a găsit niciun conținut de mesaj.",
|
||||||
|
"subscription-required":
|
||||||
|
"Trebuie să fiți abonat pentru a traduce mesajele.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Detectat {{language}} cu {{confidence}}% încredere.",
|
||||||
|
"unsupported-locale": "Locație neacceptată.",
|
||||||
|
},
|
||||||
|
[Locale.Russian]: {
|
||||||
|
"no-message-content": "Содержимое сообщения не найдено.",
|
||||||
|
"subscription-required":
|
||||||
|
"Вы должны быть подписаны на перевод сообщений.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Обнаружен {{language}} с {{confidence}}% уверенностью.",
|
||||||
|
"unsupported-locale": "Неподдерживаемое место.",
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
"no-message-content": "未找到消息内容。",
|
||||||
|
"subscription-required": "您必须订阅以翻译消息。",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# 检测到 {{language}},{{confidence}}% 的信心。",
|
||||||
|
"unsupported-locale": "不支持的区域。",
|
||||||
|
},
|
||||||
|
zt: {
|
||||||
|
"no-message-content": "未找到消息内容。",
|
||||||
|
"subscription-required": "您必须订阅以翻译消息。",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# 检测到 {{language}},{{confidence}}% 的信心。",
|
||||||
|
"unsupported-locale": "不支持的区域。",
|
||||||
|
},
|
||||||
|
[Locale.Thai]: {
|
||||||
|
"no-message-content": "ไม่พบเนื้อหาข้อความ",
|
||||||
|
"subscription-required":
|
||||||
|
"คุณต้องสมัครสมาชิกเพื่อแปลข้อความ",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# ตรวจพบ {{language}} ด้วยความมั่นใจ {{confidence}}%",
|
||||||
|
"unsupported-locale": "ที่ตั้งที่ไม่รองรับ",
|
||||||
|
},
|
||||||
|
[Locale.Turkish]: {
|
||||||
|
"no-message-content": "Hiçbir mesaj içeriği bulunamadı.",
|
||||||
|
"subscription-required":
|
||||||
|
"Mesajları çevirmek için abone olmalısınız.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# {{confidence}}% güvenle {{language}} tespit edildi.",
|
||||||
|
"unsupported-locale": "Desteklenmeyen yer.",
|
||||||
|
},
|
||||||
|
[Locale.Ukrainian]: {
|
||||||
|
"no-message-content": "Не знайдено вмісту повідомлення.",
|
||||||
|
"subscription-required":
|
||||||
|
"Ви повинні підписатися на переклад повідомлень.",
|
||||||
|
"translation":
|
||||||
|
"{{translation}}\n-# Виявлено {{language}} з {{confidence}}% впевненістю.",
|
||||||
|
"unsupported-locale": "Непідтримуване місце.",
|
||||||
|
},
|
||||||
|
};
|
@ -9,6 +9,7 @@ import {
|
|||||||
type MessageContextMenuCommandInteraction,
|
type MessageContextMenuCommandInteraction,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import { supportedLocales } from "../config/locales.js";
|
import { supportedLocales } from "../config/locales.js";
|
||||||
|
import { i18n } from "../utils/i18n.js";
|
||||||
import { getLocale } from "./getLocale.js";
|
import { getLocale } from "./getLocale.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,20 +28,22 @@ export const translate = async(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!isEntitled && interaction.user.id !== "465650873650118659") {
|
if (!isEntitled && interaction.user.id !== "465650873650118659") {
|
||||||
await interaction.editReply(
|
await interaction.editReply({
|
||||||
"You must be subscribed to translate messages.",
|
content: i18n("subscription-required", targetLocale),
|
||||||
);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportedLocales.includes(targetLocale)) {
|
if (!supportedLocales.includes(targetLocale)) {
|
||||||
await interaction.editReply("Unsupported locale.");
|
await interaction.editReply(i18n("unsupported-locale", targetLocale));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = interaction.options.getMessage("message", true);
|
const message = interaction.options.getMessage("message", true);
|
||||||
if (message.content === "") {
|
if (message.content === "") {
|
||||||
await interaction.editReply("No message content found.");
|
await interaction.editReply({
|
||||||
|
content: i18n("no-message-content", targetLocale),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sourceLocaleRequestParameters = new URLSearchParams();
|
const sourceLocaleRequestParameters = new URLSearchParams();
|
||||||
@ -66,7 +69,8 @@ export const translate = async(
|
|||||||
translationRequestParameters.append("source", sourceLocale?.language ?? "en");
|
translationRequestParameters.append("source", sourceLocale?.language ?? "en");
|
||||||
translationRequestParameters.append("target", targetLocale);
|
translationRequestParameters.append("target", targetLocale);
|
||||||
translationRequestParameters.append(
|
translationRequestParameters.append(
|
||||||
"api_key", process.env.TRANSLATE_TOKEN ?? "",
|
"api_key",
|
||||||
|
process.env.TRANSLATE_TOKEN ?? "",
|
||||||
);
|
);
|
||||||
const translationRequest = await fetch(
|
const translationRequest = await fetch(
|
||||||
"https://trans.nhcarrigan.com/translate",
|
"https://trans.nhcarrigan.com/translate",
|
||||||
@ -78,6 +82,11 @@ export const translate = async(
|
|||||||
};
|
};
|
||||||
|
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: `${translation.translatedText}\n-# Detected ${sourceLocale?.language ?? "unknown"} with ${sourceLocale?.confidence.toString() ?? "unknown"}% confidence.`,
|
content: i18n("translation", targetLocale, {
|
||||||
|
confidence: sourceLocale?.confidence,
|
||||||
|
language: sourceLocale?.language,
|
||||||
|
lng: targetLocale,
|
||||||
|
translation: translation.translatedText,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
28
src/utils/i18n.ts
Normal file
28
src/utils/i18n.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { responses } from "../i18n/responses.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a key to the specified locale, performing
|
||||||
|
* interpolation on the string.
|
||||||
|
* @param key -- The key to translate.
|
||||||
|
* @param locale -- The user's locale.
|
||||||
|
* @param interpolation -- An object of keys to replace with values.
|
||||||
|
* @returns The translated string.
|
||||||
|
*/
|
||||||
|
export const i18n = (
|
||||||
|
key: keyof (typeof responses)["en"],
|
||||||
|
locale: string,
|
||||||
|
interpolation: Record<string, unknown> = {},
|
||||||
|
): string => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- We know the en key exists, but having the loose type helps.
|
||||||
|
const string = responses[locale]?.[key] ?? responses.en![key];
|
||||||
|
// eslint-disable-next-line unicorn/no-array-reduce -- This is the cleanest way to do it, really.
|
||||||
|
return Object.entries(interpolation).reduce((accumulator, [ k, v ]) => {
|
||||||
|
return accumulator.replace(`{{${k}}}`, String(v));
|
||||||
|
}, string);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user