Translate: add support for Kagi Translate (#3416)
Co-authored-by: Vending Machine <vendicated@riseup.net>
This commit is contained in:
parent
14508c1537
commit
9794ab1cba
10 changed files with 523 additions and 31 deletions
0
scripts/build/build.mjs
Executable file → Normal file
0
scripts/build/build.mjs
Executable file → Normal file
|
|
@ -107,7 +107,14 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||||
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
|
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
|
||||||
|
|
||||||
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
||||||
if (setting.type === OptionType.CUSTOM || setting.hidden) return null;
|
if (setting.type === OptionType.CUSTOM) return null;
|
||||||
|
|
||||||
|
const shouldHide = "hidden" in setting && (
|
||||||
|
typeof setting.hidden === "function"
|
||||||
|
? setting.hidden.call(plugin.settings)
|
||||||
|
: setting.hidden
|
||||||
|
);
|
||||||
|
if (shouldHide) return null;
|
||||||
|
|
||||||
function onChange(newValue: any) {
|
function onChange(newValue: any) {
|
||||||
const option = plugin.options?.[key];
|
const option = plugin.options?.[key];
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,9 @@ let tooltipTimeout: any;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "Translate",
|
name: "Translate",
|
||||||
description: "Translate messages with Google Translate or DeepL",
|
description: "Translate messages with Google Translate, DeepL or Kagi.",
|
||||||
tags: ["Chat", "Utility"],
|
tags: ["Chat", "Utility"],
|
||||||
authors: [Devs.Ven, Devs.AshtonMemer],
|
authors: [Devs.Ven, Devs.AshtonMemer, Devs.koish1],
|
||||||
settings,
|
settings,
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"message": messageCtxPatch
|
"message": messageCtxPatch
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ copy(Object.fromEntries(
|
||||||
|
|
||||||
export type GoogleLanguage = keyof typeof GoogleLanguages;
|
export type GoogleLanguage = keyof typeof GoogleLanguages;
|
||||||
export type DeeplLanguage = keyof typeof DeeplLanguages;
|
export type DeeplLanguage = keyof typeof DeeplLanguages;
|
||||||
|
export type KagiLanguage = keyof typeof KagiLanguages;
|
||||||
|
|
||||||
export const GoogleLanguages = {
|
export const GoogleLanguages = {
|
||||||
"auto": "Detect language",
|
"auto": "Detect language",
|
||||||
|
|
@ -318,3 +319,392 @@ export function deeplLanguageToGoogleLanguage(language: string) {
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// curl https://translate.kagi.com/api/list-languages | jq 'map({(.language | ascii_downcase): .name}) | add | to_entries | sort_by(.value) | from_entries'
|
||||||
|
// Add "auto": "Detect language", as kagi has no "language" for Detect Language, however anything not
|
||||||
|
// in the list will work as auto. Using "auto" will have this work using Google Translate defaults.
|
||||||
|
// Retrieved 2025.06.16
|
||||||
|
export const KagiLanguages = {
|
||||||
|
"auto": "Detect language",
|
||||||
|
"om": "(Afan)/Oromoor/Oriya",
|
||||||
|
"ab": "Abkhazian",
|
||||||
|
"ace": "Acehnese",
|
||||||
|
"ach": "Acholi",
|
||||||
|
"aa": "Afar",
|
||||||
|
"af": "Afrikaans",
|
||||||
|
"ak": "Akan",
|
||||||
|
"sq": "Albanian",
|
||||||
|
"alz": "Alur",
|
||||||
|
"am": "Amharic",
|
||||||
|
"ar": "Arabic",
|
||||||
|
"ar_dz": "Arabic (Algeria)",
|
||||||
|
"ar_eg": "Arabic (Egypt)",
|
||||||
|
"ar_iq": "Arabic (Iraq)",
|
||||||
|
"ar_jo": "Arabic (Jordan)",
|
||||||
|
"ar_kw": "Arabic (Kuwait)",
|
||||||
|
"ar_lb": "Arabic (Lebanon)",
|
||||||
|
"ar_ly": "Arabic (Libya)",
|
||||||
|
"ar_ma": "Arabic (Morocco)",
|
||||||
|
"ar_qa": "Arabic (Qatar)",
|
||||||
|
"ar_sa": "Arabic (Saudi Arabia)",
|
||||||
|
"ar_sy": "Arabic (Syria)",
|
||||||
|
"ar_tn": "Arabic (Tunisia)",
|
||||||
|
"ar_ae": "Arabic (UAE)",
|
||||||
|
"an": "Aragonese",
|
||||||
|
"hy": "Armenian",
|
||||||
|
"as": "Assamese",
|
||||||
|
"av": "Avaric",
|
||||||
|
"ae": "Avestan",
|
||||||
|
"ava": "Awadhi",
|
||||||
|
"ay": "Aymara",
|
||||||
|
"az": "Azeri",
|
||||||
|
"ban": "Balinese",
|
||||||
|
"bal": "Baluchi",
|
||||||
|
"bm": "Bambara",
|
||||||
|
"bci": "Baoulé",
|
||||||
|
"ba": "Bashkir",
|
||||||
|
"eu": "Basque",
|
||||||
|
"btk": "Batak",
|
||||||
|
"be": "Belarusian",
|
||||||
|
"bem": "Bemba",
|
||||||
|
"bn": "Bengali",
|
||||||
|
"bn_bd": "Bengali (Bangladesh)",
|
||||||
|
"bn_in": "Bengali (India)",
|
||||||
|
"bew": "Betawi",
|
||||||
|
"bho": "Bhojpuri",
|
||||||
|
"dz": "Bhutani",
|
||||||
|
"bh": "Bihari",
|
||||||
|
"bcl": "Bikol",
|
||||||
|
"bi": "Bislama",
|
||||||
|
"bs": "Bosnian",
|
||||||
|
"br": "Breton",
|
||||||
|
"bg": "Bulgarian",
|
||||||
|
"my": "Burmese",
|
||||||
|
"bua": "Buryat",
|
||||||
|
"km": "Cambodian",
|
||||||
|
"yue": "Cantonese",
|
||||||
|
"ca": "Catalan",
|
||||||
|
"ceb": "Cebuano",
|
||||||
|
"ch": "Chamorro",
|
||||||
|
"ce": "Chechen",
|
||||||
|
"chr": "Cherokee",
|
||||||
|
"ny": "Chichewa",
|
||||||
|
"zh": "Chinese",
|
||||||
|
"zh_hk": "Chinese (Hong Kong)",
|
||||||
|
"zh_mo": "Chinese (Macau)",
|
||||||
|
"zh_cn": "Chinese (Simplified)",
|
||||||
|
"zh_sg": "Chinese (Singapore)",
|
||||||
|
"zh_tw": "Chinese (Traditional)",
|
||||||
|
"cu": "Church Slavonic",
|
||||||
|
"chk": "Chuukese",
|
||||||
|
"cv": "Chuvash",
|
||||||
|
"kw": "Cornish",
|
||||||
|
"co": "Corsican",
|
||||||
|
"cr": "Cree",
|
||||||
|
"crh": "Crimean Tatar",
|
||||||
|
"hr": "Croatian",
|
||||||
|
"hr_ba": "Croatian (Bosnia and Herzegovina)",
|
||||||
|
"hr_hr": "Croatian (Croatia)",
|
||||||
|
"cs": "Czech",
|
||||||
|
"da": "Danish",
|
||||||
|
"din": "Dinka",
|
||||||
|
"div": "Divehi",
|
||||||
|
"dv": "Divehi",
|
||||||
|
"doi": "Dogri",
|
||||||
|
"dov": "Dombe",
|
||||||
|
"nl": "Dutch",
|
||||||
|
"nl_aw": "Dutch (Aruba)",
|
||||||
|
"nl_be": "Dutch (Belgium)",
|
||||||
|
"nl_cw": "Dutch (Curaçao)",
|
||||||
|
"nl_nl": "Dutch (Netherlands)",
|
||||||
|
"nl_sr": "Dutch (Suriname)",
|
||||||
|
"dyu": "Dyula",
|
||||||
|
"elvish": "Elvish (Sindarin)",
|
||||||
|
"emoji": "Emoji Speak",
|
||||||
|
"en": "English",
|
||||||
|
"en_au": "English (Australia)",
|
||||||
|
"en_ca": "English (Canada)",
|
||||||
|
"en_in": "English (India)",
|
||||||
|
"en_ie": "English (Ireland)",
|
||||||
|
"en_nz": "English (New Zealand)",
|
||||||
|
"en_ph": "English (Philippines)",
|
||||||
|
"en_sg": "English (Singapore)",
|
||||||
|
"en_za": "English (South Africa)",
|
||||||
|
"en_gb": "English (UK)",
|
||||||
|
"en_us": "English (US)",
|
||||||
|
"eo": "Esperanto",
|
||||||
|
"et": "Estonian",
|
||||||
|
"ee": "Ewe",
|
||||||
|
"mk": "FYRO Macedonian",
|
||||||
|
"fo": "Faroese",
|
||||||
|
"fa": "Farsi",
|
||||||
|
"fj": "Fiji",
|
||||||
|
"fil": "Filipino",
|
||||||
|
"fi": "Finnish",
|
||||||
|
"fon": "Fon",
|
||||||
|
"fr": "French",
|
||||||
|
"fr_be": "French (Belgium)",
|
||||||
|
"fr_ca": "French (Canada)",
|
||||||
|
"fr_ci": "French (Côte d'Ivoire)",
|
||||||
|
"fr_fr": "French (France)",
|
||||||
|
"fr_sn": "French (Senegal)",
|
||||||
|
"fr_ch": "French (Switzerland)",
|
||||||
|
"fy": "Frisian",
|
||||||
|
"fur": "Friulian",
|
||||||
|
"ff": "Fulah",
|
||||||
|
"gaa": "Ga",
|
||||||
|
"gd": "Gaelic",
|
||||||
|
"gl": "Galician",
|
||||||
|
"lg": "Ganda",
|
||||||
|
"ka": "Georgian",
|
||||||
|
"de": "German",
|
||||||
|
"de_at": "German (Austria)",
|
||||||
|
"de_de": "German (Germany)",
|
||||||
|
"de_li": "German (Liechtenstein)",
|
||||||
|
"de_lu": "German (Luxembourg)",
|
||||||
|
"el": "Greek",
|
||||||
|
"kl": "Greenlandic",
|
||||||
|
"gn": "Guarani",
|
||||||
|
"gu": "Gujarati",
|
||||||
|
"ht": "Haitian",
|
||||||
|
"ha": "Hausa",
|
||||||
|
"haw": "Hawaiian",
|
||||||
|
"he": "Hebrew",
|
||||||
|
"hz": "Herero",
|
||||||
|
"hil": "Hiligaynon",
|
||||||
|
"hi": "Hindi",
|
||||||
|
"hi_fj": "Hindi (Fiji)",
|
||||||
|
"hi_in": "Hindi (India)",
|
||||||
|
"ho": "Hiri Motu",
|
||||||
|
"hu": "Hungarian",
|
||||||
|
"hrx": "Hunsrik",
|
||||||
|
"iba": "Iban",
|
||||||
|
"is": "Icelandic",
|
||||||
|
"io": "Ido",
|
||||||
|
"ig": "Igbo",
|
||||||
|
"ilo": "Ilocano",
|
||||||
|
"id": "Indonesian",
|
||||||
|
"ia": "Interlingua",
|
||||||
|
"ie": "Interlingue",
|
||||||
|
"iu": "Inuktitut",
|
||||||
|
"ik": "Inupiak",
|
||||||
|
"ga": "Irish",
|
||||||
|
"it": "Italian",
|
||||||
|
"it_it": "Italian (Italy)",
|
||||||
|
"it_sm": "Italian (San Marino)",
|
||||||
|
"it_ch": "Italian (Switzerland)",
|
||||||
|
"it_va": "Italian (Vatican City)",
|
||||||
|
"jam": "Jamaican Patois",
|
||||||
|
"ja": "Japanese",
|
||||||
|
"jv": "Javanese",
|
||||||
|
"cpe": "Jingpo",
|
||||||
|
"kn": "Kannada",
|
||||||
|
"kr": "Kanuri",
|
||||||
|
"ks": "Kashmiri",
|
||||||
|
"kk": "Kazakh",
|
||||||
|
"kha": "Khasi",
|
||||||
|
"ki": "Kikuyu",
|
||||||
|
"rw": "Kinyarwanda",
|
||||||
|
"ky": "Kirghiz",
|
||||||
|
"rn": "Kirundi",
|
||||||
|
"ktu": "Kituba",
|
||||||
|
"klingon": "Klingon",
|
||||||
|
"kv": "Komi",
|
||||||
|
"kg": "Kongo",
|
||||||
|
"kok": "Konkani",
|
||||||
|
"ko": "Korean",
|
||||||
|
"ko_kp": "Korean (North Korea)",
|
||||||
|
"ko_kr": "Korean (South Korea)",
|
||||||
|
"kj": "Kuanyama",
|
||||||
|
"ku": "Kurdish",
|
||||||
|
"ckb": "Kurdish (Sorani)",
|
||||||
|
"lo": "Laothian",
|
||||||
|
"ltg": "Latgalian",
|
||||||
|
"la": "Latin",
|
||||||
|
"lv": "Latvian",
|
||||||
|
"lij": "Ligurian",
|
||||||
|
"li": "Limburgan",
|
||||||
|
"lim": "Limburgish",
|
||||||
|
"ln": "Lingala",
|
||||||
|
"lt": "Lithuanian",
|
||||||
|
"lmo": "Lombard",
|
||||||
|
"lu": "Luba_Katanga",
|
||||||
|
"luo": "Luo",
|
||||||
|
"lb": "Luxembourgish",
|
||||||
|
"mad": "Madurese",
|
||||||
|
"mai": "Maithili",
|
||||||
|
"mak": "Makassar",
|
||||||
|
"mg": "Malagasy",
|
||||||
|
"ms": "Malay",
|
||||||
|
"ms_bn": "Malay (Brunei Darussalam)",
|
||||||
|
"ms_id": "Malay (Indonesia)",
|
||||||
|
"ms_arab": "Malay (Jawi)",
|
||||||
|
"ms_my": "Malay (Malaysia)",
|
||||||
|
"ms_sg": "Malay (Singapore)",
|
||||||
|
"ml": "Malayalam",
|
||||||
|
"mt": "Maltese",
|
||||||
|
"gv": "Manx",
|
||||||
|
"mi": "Maori",
|
||||||
|
"mr": "Marathi",
|
||||||
|
"mh": "Marshallese",
|
||||||
|
"mwr": "Marwadi",
|
||||||
|
"middle_english": "Middle English",
|
||||||
|
"mo": "Moldavian",
|
||||||
|
"mn": "Mongolian",
|
||||||
|
"nqo": "NKo",
|
||||||
|
"nhe": "Nahuatl",
|
||||||
|
"na": "Nauru",
|
||||||
|
"nv": "Navajo",
|
||||||
|
"ndc": "Ndau",
|
||||||
|
"ng": "Ndonga",
|
||||||
|
"new": "Nepalbhasa",
|
||||||
|
"ne": "Nepali",
|
||||||
|
"nd": "North Ndebele",
|
||||||
|
"ns": "Northern Sotho",
|
||||||
|
"nb": "Norwegian (Bokmål)",
|
||||||
|
"nn": "Norwegian (Nynorsk)",
|
||||||
|
"nus": "Nuer",
|
||||||
|
"oc": "Occitan",
|
||||||
|
"oj": "Ojibwa",
|
||||||
|
"or": "Oriya",
|
||||||
|
"os": "Ossetian",
|
||||||
|
"pi": "Pali",
|
||||||
|
"pag": "Pangasinan",
|
||||||
|
"pap": "Papiamento",
|
||||||
|
"ps": "Pashto/Pushto",
|
||||||
|
"per": "Persian",
|
||||||
|
"fa_af": "Persian (Afghanistan)",
|
||||||
|
"fa_ir": "Persian (Iran)",
|
||||||
|
"fa_tj": "Persian (Tajikistan)",
|
||||||
|
"pirate": "Pirate Speak",
|
||||||
|
"pl": "Polish",
|
||||||
|
"pt": "Portuguese",
|
||||||
|
"pt_ao": "Portuguese (Angola)",
|
||||||
|
"pt_br": "Portuguese (Brazil)",
|
||||||
|
"pt_cv": "Portuguese (Cape Verde)",
|
||||||
|
"pt_mz": "Portuguese (Mozambique)",
|
||||||
|
"pt_pt": "Portuguese (Portugal)",
|
||||||
|
"pa": "Punjabi",
|
||||||
|
"qu": "Quechua",
|
||||||
|
"qu_bo": "Quechua (Bolivia)",
|
||||||
|
"qu_ec": "Quechua (Ecuador)",
|
||||||
|
"qu_pe": "Quechua (Peru)",
|
||||||
|
"kek": "Qʼeqchiʼ",
|
||||||
|
"rm": "Rhaeto_Romanic",
|
||||||
|
"rom": "Romani",
|
||||||
|
"ro": "Romanian",
|
||||||
|
"ru": "Russian",
|
||||||
|
"ru_by": "Russian (Belarus)",
|
||||||
|
"ru_kz": "Russian (Kazakhstan)",
|
||||||
|
"ru_kg": "Russian (Kyrgyzstan)",
|
||||||
|
"ru_ru": "Russian (Russia)",
|
||||||
|
"se": "Sami",
|
||||||
|
"se_fi": "Sami (Finland)",
|
||||||
|
"se_no": "Sami (Norway)",
|
||||||
|
"se_se": "Sami (Sweden)",
|
||||||
|
"sm": "Samoan",
|
||||||
|
"sg": "Sangro",
|
||||||
|
"sa": "Sanskrit",
|
||||||
|
"sat": "Santali",
|
||||||
|
"sc": "Sardinian",
|
||||||
|
"sr": "Serbian",
|
||||||
|
"sr_ba": "Serbian (Bosnia and Herzegovina)",
|
||||||
|
"sr_sp": "Serbian (Serbia and Montenegro)",
|
||||||
|
"sh": "Serbo_Croatian",
|
||||||
|
"st": "Sesotho",
|
||||||
|
"shn": "Shan",
|
||||||
|
"sn": "Shona",
|
||||||
|
"ii": "Sichuan Yi",
|
||||||
|
"scn": "Sicilian",
|
||||||
|
"szl": "Silesian",
|
||||||
|
"sd": "Sindhi",
|
||||||
|
"si": "Singhalese",
|
||||||
|
"ss": "Siswati",
|
||||||
|
"sk": "Slovak",
|
||||||
|
"sl": "Slovenian",
|
||||||
|
"so": "Somali",
|
||||||
|
"sb": "Sorbian",
|
||||||
|
"nr": "South Ndebele",
|
||||||
|
"es": "Spanish",
|
||||||
|
"es_ar": "Spanish (Argentina)",
|
||||||
|
"es_bo": "Spanish (Bolivia)",
|
||||||
|
"es_cl": "Spanish (Chile)",
|
||||||
|
"es_co": "Spanish (Colombia)",
|
||||||
|
"es_cr": "Spanish (Costa Rica)",
|
||||||
|
"es_cu": "Spanish (Cuba)",
|
||||||
|
"es_do": "Spanish (Dominican Republic)",
|
||||||
|
"es_ec": "Spanish (Ecuador)",
|
||||||
|
"es_sv": "Spanish (El Salvador)",
|
||||||
|
"es_gq": "Spanish (Equatorial Guinea)",
|
||||||
|
"es_gt": "Spanish (Guatemala)",
|
||||||
|
"es_hn": "Spanish (Honduras)",
|
||||||
|
"es_mx": "Spanish (Mexico)",
|
||||||
|
"es_ni": "Spanish (Nicaragua)",
|
||||||
|
"es_pa": "Spanish (Panama)",
|
||||||
|
"es_py": "Spanish (Paraguay)",
|
||||||
|
"es_pe": "Spanish (Peru)",
|
||||||
|
"es_pr": "Spanish (Puerto Rico)",
|
||||||
|
"es_es": "Spanish (Spain)",
|
||||||
|
"es_uy": "Spanish (Uruguay)",
|
||||||
|
"es_ve": "Spanish (Venezuela)",
|
||||||
|
"su": "Sundanese",
|
||||||
|
"sus": "Susu",
|
||||||
|
"sx": "Sutu",
|
||||||
|
"sw": "Swahili",
|
||||||
|
"sv": "Swedish",
|
||||||
|
"sv_fi": "Swedish (Finland)",
|
||||||
|
"sv_se": "Swedish (Sweden)",
|
||||||
|
"gsw": "Swiss German",
|
||||||
|
"de_ch": "Swiss High German",
|
||||||
|
"syr": "Syriac",
|
||||||
|
"tl": "Tagalog",
|
||||||
|
"ty": "Tahitian",
|
||||||
|
"tg": "Tajik",
|
||||||
|
"ta": "Tamil",
|
||||||
|
"ta_in": "Tamil (India)",
|
||||||
|
"ta_my": "Tamil (Malaysia)",
|
||||||
|
"ta_sg": "Tamil (Singapore)",
|
||||||
|
"ta_lk": "Tamil (Sri Lanka)",
|
||||||
|
"tt": "Tatar",
|
||||||
|
"te": "Telugu",
|
||||||
|
"tet": "Tetum",
|
||||||
|
"th": "Thai",
|
||||||
|
"bo": "Tibetan",
|
||||||
|
"ti": "Tigrinya",
|
||||||
|
"tiv": "Tiv",
|
||||||
|
"tpi": "Tok Pisin",
|
||||||
|
"to": "Tonga",
|
||||||
|
"ts": "Tsonga",
|
||||||
|
"tn": "Tswana",
|
||||||
|
"tum": "Tumbuka",
|
||||||
|
"tr": "Turkish",
|
||||||
|
"tr_cy": "Turkish (Cyprus)",
|
||||||
|
"tr_tr": "Turkish (Turkey)",
|
||||||
|
"tk": "Turkmen",
|
||||||
|
"tyv": "Tuvan",
|
||||||
|
"tw": "Twi",
|
||||||
|
"udm": "Udmurt",
|
||||||
|
"ug": "Uighur",
|
||||||
|
"uk": "Ukrainian",
|
||||||
|
"ur": "Urdu",
|
||||||
|
"ur_in": "Urdu (India)",
|
||||||
|
"ur_pk": "Urdu (Pakistan)",
|
||||||
|
"uz": "Uzbek",
|
||||||
|
"ve": "Venda",
|
||||||
|
"vec": "Venetian",
|
||||||
|
"vi": "Vietnamese",
|
||||||
|
"vo": "Volapuk",
|
||||||
|
"wa": "Walloon",
|
||||||
|
"war": "Waray",
|
||||||
|
"cy": "Welsh",
|
||||||
|
"wo": "Wolof",
|
||||||
|
"xh": "Xhosa",
|
||||||
|
"sah": "Yakut",
|
||||||
|
"yi": "Yiddish",
|
||||||
|
"yo": "Yoruba",
|
||||||
|
"yua": "Yucatec Maya",
|
||||||
|
"zap": "Zapotec",
|
||||||
|
"za": "Zhuang",
|
||||||
|
"zu": "Zulu"
|
||||||
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,28 @@ export async function makeDeeplTranslateRequest(_: IpcMainInvokeEvent, pro: bool
|
||||||
return { status: -1, data: String(e) };
|
return { status: -1, data: String(e) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function makeKagiTranslateRequest(_: IpcMainInvokeEvent, token: string, text: string, sourceLang: string, targetLang: string) {
|
||||||
|
const url = "https://translate.kagi.com/api/translate";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Cookie": `kagi_session=${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
text,
|
||||||
|
from: sourceLang,
|
||||||
|
to: targetLang,
|
||||||
|
model: "standard"
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return { status: res.status, data };
|
||||||
|
} catch (e) {
|
||||||
|
return { status: -1, data: String(e) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,24 +44,27 @@ export const settings = definePluginSettings({
|
||||||
default: "en",
|
default: "en",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
|
||||||
service: {
|
service: {
|
||||||
type: OptionType.SELECT,
|
type: OptionType.SELECT,
|
||||||
description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
|
description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
|
||||||
disabled: () => IS_WEB,
|
hidden: IS_WEB,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Google Translate", value: "google", default: true },
|
{ label: "Google Translate", value: "google", default: true },
|
||||||
{ label: "DeepL Free", value: "deepl" },
|
{ label: "DeepL Free — API key required", value: "deepl" },
|
||||||
{ label: "DeepL Pro", value: "deepl-pro" }
|
{ label: "DeepL Pro — API key required", value: "deepl-pro" },
|
||||||
|
{ label: "Kagi Translate — API key required", value: "kagi" }
|
||||||
] as const,
|
] as const,
|
||||||
onChange: resetLanguageDefaults
|
onChange: resetLanguageDefaults
|
||||||
},
|
},
|
||||||
deeplApiKey: {
|
deeplApiKey: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "DeepL API key",
|
description: "Get your API key from https://deepl.com/your-account",
|
||||||
default: "",
|
default: ""
|
||||||
placeholder: "Get your API key from https://deepl.com/your-account",
|
},
|
||||||
disabled: () => IS_WEB
|
kagiSession: {
|
||||||
|
type: OptionType.STRING,
|
||||||
|
description: "Get your session token from https://kagi.com/settings?p=user_details",
|
||||||
|
default: ""
|
||||||
},
|
},
|
||||||
autoTranslate: {
|
autoTranslate: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
|
|
@ -73,12 +76,19 @@ export const settings = definePluginSettings({
|
||||||
description: "Show a tooltip on the ChatBar button whenever a message is automatically translated",
|
description: "Show a tooltip on the ChatBar button whenever a message is automatically translated",
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
deeplApiKey: {
|
||||||
|
hidden() { return this.store.service !== "deepl" && this.store.service !== "deepl-pro"; }
|
||||||
|
},
|
||||||
|
kagiSession: {
|
||||||
|
hidden() { return this.store.service !== "kagi"; }
|
||||||
|
}
|
||||||
}).withPrivateSettings<{
|
}).withPrivateSettings<{
|
||||||
showAutoTranslateAlert: boolean;
|
showAutoTranslateAlert: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
export function resetLanguageDefaults() {
|
export function resetLanguageDefaults() {
|
||||||
if (IS_WEB || settings.store.service === "google") {
|
if (IS_WEB || settings.store.service === "google" || settings.store.service === "kagi") {
|
||||||
settings.store.receivedInput = "auto";
|
settings.store.receivedInput = "auto";
|
||||||
settings.store.receivedOutput = "en";
|
settings.store.receivedOutput = "en";
|
||||||
settings.store.sentInput = "auto";
|
settings.store.sentInput = "auto";
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { onlyOnce } from "@utils/onlyOnce";
|
||||||
import { PluginNative } from "@utils/types";
|
import { PluginNative } from "@utils/types";
|
||||||
import { showToast, Toasts } from "@webpack/common";
|
import { showToast, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
import { DeeplLanguages, deeplLanguageToGoogleLanguage, GoogleLanguages } from "./languages";
|
import { DeeplLanguages, deeplLanguageToGoogleLanguage, GoogleLanguages, KagiLanguages } from "./languages";
|
||||||
import { resetLanguageDefaults, settings } from "./settings";
|
import { resetLanguageDefaults, settings } from "./settings";
|
||||||
|
|
||||||
export const cl = classNameFactory("vc-trans-");
|
export const cl = classNameFactory("vc-trans-");
|
||||||
|
|
@ -40,19 +40,43 @@ interface DeeplData {
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface KagiData {
|
||||||
|
translation: string;
|
||||||
|
detected_language: {
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface TranslationValue {
|
export interface TranslationValue {
|
||||||
sourceLanguage: string;
|
sourceLanguage: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLanguages = () => IS_WEB || settings.store.service === "google"
|
export const getLanguages = () => {
|
||||||
? GoogleLanguages
|
if (IS_WEB) {
|
||||||
: DeeplLanguages;
|
return GoogleLanguages;
|
||||||
|
}
|
||||||
|
switch (settings.store.service) {
|
||||||
|
case "google":
|
||||||
|
return GoogleLanguages;
|
||||||
|
case "kagi":
|
||||||
|
return KagiLanguages;
|
||||||
|
default:
|
||||||
|
return DeeplLanguages;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
||||||
const translate = IS_WEB || settings.store.service === "google"
|
const translate = IS_WEB ? googleTranslate : (() => {
|
||||||
? googleTranslate
|
switch (settings.store.service) {
|
||||||
: deeplTranslate;
|
case "google":
|
||||||
|
return googleTranslate;
|
||||||
|
case "kagi":
|
||||||
|
return kagiTranslate;
|
||||||
|
default:
|
||||||
|
return deeplTranslate;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await translate(
|
return await translate(
|
||||||
|
|
@ -153,3 +177,25 @@ async function deeplTranslate(text: string, sourceLang: string, targetLang: stri
|
||||||
text: translations[0].text
|
text: translations[0].text
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function kagiTranslate(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
|
const { status, data } = await Native.makeKagiTranslateRequest(
|
||||||
|
settings.store.kagiSession, text, sourceLang, targetLang
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 200:
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
throw "Invalid or expired Kagi session token";
|
||||||
|
default:
|
||||||
|
throw new Error(`Failed to translate "${text}" (${sourceLang} -> ${targetLang})\n${status} ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { detected_language, translation }: KagiData = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceLanguage: detected_language.label,
|
||||||
|
text: translation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,12 @@ export function buildPluginMenuEntries(includeEmpty = false) {
|
||||||
let hasAnyOption = false;
|
let hasAnyOption = false;
|
||||||
|
|
||||||
if (p.options) for (const [key, option] of Object.entries(p.options)) {
|
if (p.options) for (const [key, option] of Object.entries(p.options)) {
|
||||||
if ("hidden" in option && option.hidden) continue;
|
const shouldHide = "hidden" in option && (
|
||||||
|
typeof option.hidden === "function"
|
||||||
|
? option.hidden.call(p.settings)
|
||||||
|
: option.hidden
|
||||||
|
);
|
||||||
|
if (shouldHide) continue;
|
||||||
|
|
||||||
hasAnyOption = true;
|
hasAnyOption = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -609,6 +609,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "Cootshk",
|
name: "Cootshk",
|
||||||
id: 921605971577548820n
|
id: 921605971577548820n
|
||||||
},
|
},
|
||||||
|
koish1: {
|
||||||
|
name: "koish1",
|
||||||
|
id: 291089948709486593n,
|
||||||
|
badge: false,
|
||||||
|
},
|
||||||
thororen: {
|
thororen: {
|
||||||
name: "thororen",
|
name: "thororen",
|
||||||
id: 848339671629299742n
|
id: 848339671629299742n
|
||||||
|
|
|
||||||
|
|
@ -259,8 +259,8 @@ export const enum OptionType {
|
||||||
|
|
||||||
export type SettingsDefinition = Record<string, PluginSettingDef>;
|
export type SettingsDefinition = Record<string, PluginSettingDef>;
|
||||||
export type SettingsChecks<D extends SettingsDefinition> = {
|
export type SettingsChecks<D extends SettingsDefinition> = {
|
||||||
[K in keyof D]?: D[K] extends PluginSettingComponentDef ? IsDisabled<DefinedSettings<D>> :
|
[K in keyof D]?: D[K] extends PluginSettingComponentDef ? IsDisabledOrHidden<DefinedSettings<D>> :
|
||||||
(IsDisabled<DefinedSettings<D>> & IsValid<PluginSettingType<D[K]>, DefinedSettings<D>>);
|
(IsDisabledOrHidden<DefinedSettings<D>> & IsValid<PluginSettingType<D[K]>, DefinedSettings<D>>);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginSettingDef =
|
export type PluginSettingDef =
|
||||||
|
|
@ -286,18 +286,22 @@ export interface PluginSettingCommon {
|
||||||
/**
|
/**
|
||||||
* Hide this setting from the settings UI
|
* Hide this setting from the settings UI
|
||||||
*/
|
*/
|
||||||
hidden?: boolean;
|
hidden?: boolean | (() => boolean);
|
||||||
/**
|
/**
|
||||||
* Set this if the setting only works on Browser or Desktop, not both
|
* Set this if the setting only works on Browser or Desktop, not both
|
||||||
*/
|
*/
|
||||||
target?: "WEB" | "DESKTOP" | "BOTH";
|
target?: "WEB" | "DESKTOP" | "BOTH";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IsDisabled<D = unknown> {
|
interface IsDisabledOrHidden<D = unknown> {
|
||||||
/**
|
/**
|
||||||
* Checks if this setting should be disabled
|
* Checks if this setting should be disabled
|
||||||
*/
|
*/
|
||||||
disabled?(this: D): boolean;
|
disabled?(this: D): boolean;
|
||||||
|
/**
|
||||||
|
* Checks if this setting should be hidden
|
||||||
|
*/
|
||||||
|
hidden?(this: D): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IsValid<T, D = unknown> {
|
interface IsValid<T, D = unknown> {
|
||||||
|
|
@ -432,7 +436,7 @@ export type IpcRes<V = any> = { ok: true; value: V; } | { ok: false, error: any;
|
||||||
/* Legacy Options Types */
|
/* Legacy Options Types */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
export type PluginOptionBase = PluginSettingCommon & IsDisabled;
|
export type PluginOptionBase = PluginSettingCommon & IsDisabledOrHidden;
|
||||||
export type PluginOptionsItem =
|
export type PluginOptionsItem =
|
||||||
| PluginOptionString
|
| PluginOptionString
|
||||||
| PluginOptionNumber
|
| PluginOptionNumber
|
||||||
|
|
@ -441,12 +445,12 @@ export type PluginOptionsItem =
|
||||||
| PluginOptionSlider
|
| PluginOptionSlider
|
||||||
| PluginOptionComponent
|
| PluginOptionComponent
|
||||||
| PluginOptionCustom;
|
| PluginOptionCustom;
|
||||||
export type PluginOptionString = PluginSettingStringDef & PluginSettingCommon & IsDisabled & IsValid<string>;
|
export type PluginOptionString = PluginSettingStringDef & PluginOptionBase & IsValid<string>;
|
||||||
export type PluginOptionNumber = (PluginSettingNumberDef | PluginSettingBigIntDef) & PluginSettingCommon & IsDisabled & IsValid<number | BigInt>;
|
export type PluginOptionNumber = (PluginSettingNumberDef | PluginSettingBigIntDef) & PluginOptionBase & IsValid<number | BigInt>;
|
||||||
export type PluginOptionBoolean = PluginSettingBooleanDef & PluginSettingCommon & IsDisabled & IsValid<boolean>;
|
export type PluginOptionBoolean = PluginSettingBooleanDef & PluginOptionBase & IsValid<boolean>;
|
||||||
export type PluginOptionSelect = PluginSettingSelectDef & PluginSettingCommon & IsDisabled & IsValid<PluginSettingSelectOption>;
|
export type PluginOptionSelect = PluginSettingSelectDef & PluginOptionBase & IsValid<PluginSettingSelectOption>;
|
||||||
export type PluginOptionSlider = PluginSettingSliderDef & PluginSettingCommon & IsDisabled & IsValid<number>;
|
export type PluginOptionSlider = PluginSettingSliderDef & PluginOptionBase & IsValid<number>;
|
||||||
export type PluginOptionComponent = PluginSettingComponentDef & Omit<PluginSettingCommon, "description" | "placeholder">;
|
export type PluginOptionComponent = PluginSettingComponentDef & Omit<PluginOptionBase, "description" | "placeholder">;
|
||||||
export type PluginOptionCustom = PluginSettingCustomDef & Pick<PluginSettingCommon, "onChange">;
|
export type PluginOptionCustom = PluginSettingCustomDef & Pick<PluginSettingCommon, "onChange">;
|
||||||
|
|
||||||
export type PluginNative<PluginExports extends Record<string, (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any>> = {
|
export type PluginNative<PluginExports extends Record<string, (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any>> = {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue