CVE-2024-29042 (JavaScript, published 2024-03-22T17:15Z)
Description: value: Translate is a package that allows users to convert text to different languages on Node.js and the browser. Prior to version 3.0.0, an attacker controlling the second variable of the `translate` function is able to perform a cache poisoning attack. They can change the outcome of translation requests made by subsequent users. The `opt.id` parameter allows the overwriting of the cache key. If an attacker sets the `id` variable to the cache key that would be generated by another user, they can choose the response that user gets served. Version 3.0.0 fixes this issue.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/franciscop/translate
Files changed:
README.md
index.d.ts
index.min.js
src/index.js
test/index.types.ts
Commit message: Made the usage options much simpler so that there's no chance of writing runtime vulnerabilities. Favored instances. Fixed types


Vulnerable code:
  src="https://cdn.jsdelivr.net/npm/translate@2/index.min.js"
Then you can finally use it. Putting it all together:
The first parameter is the **string** that you want to translate. Right now only a single string of text is accepted.
The second parameter is the options. It accepts either a `String` of the language to translate **to** or a simple `Object` with the keys `to` and `from`. However, in total there are more options, so here is a list of all of them:
- **`cache`** [instance]: a `Number` with the milliseconds that each translation should be cached. Leave it undefined to cache it indefinitely (until a server/browser restart).
- **`engine`** [instance]: a `String` containing the name of the engine to use for translation. Right now it defaults to `google`. Read more [in the engine section](#engines).
- **`key`** [instance]: the API Key for the engine of your choice. Read more [in the engine section](#engines).
- **`url`** [instance]: only available for those engines that you can install on your own server (like Libretranslate), allows you to specify a custom endpoint for the translations. [See this issue](https://github.com/franciscop/translate/issues/26#issuecomment-845038821) for more info.
> The options marked as [instance] can only be set to the root `translate.cache = 1000` or when creating a new instance `const myDeepL = translate.Translate()`

// INVALID:
const bar = await translate("Hello world", { to: "es", engine: "google" });
> On both `to` and `from` defaulting to `en`: while I _am_ Spanish and was quite tempted to set this as one of those, English is the main language of the Internet and the main secondary language for those who have a different native language. This is why most of the translations will happen either to or from English.

You can change the default options for anything by calling the root library and the option name:
You can also create a new instance with different default options:
const myLib = translate.Translate({ engine: 'deepl', from: 'es', ... });
// ... include translate
// ... include translate
const gTranslate = translate.Translate({
  engine: "google",
  key: "YOUR-KEY-HERE",
});
const dTranslate = translate.Translate({
  engine: "deepl",
  key: "YOUR-KEY-HERE",
});
const lTranslate = translate.Translate({
  engine: "libre",
  key: "YOUR-KEY-HERE",
});
const lTranslate = translate.Translate({
Working with Promises and specially with [async/await](https://ponyfoo.com/articles/understanding-javascript-async-await) reduces [Callback Hell](http://callbackhell.com/). To see it in action, first you'll need an `async` function. Then put your `await` calls inside:
type Options =
  | string
  | {
      // Basic types expected for everyday use
      from?: string;
      to?: string;
      key?: string;
      engine?: "google" | "deepl" | "libre" | "yandex";

      // More advanced types when tweaking the library
      keys?: { [name: string]: string };
      cache?: number;
      engines?: { [name: string]: Engine };
    };

export default function Translate(
  text: string,
  options?: Options
): Promise<string>;
function Cache(){var e=Object.create(null);function a(a){delete e[a]}this.set=function(n,i,r){if(void 0!==r&&("number"!=typeof r||isNaN(r)||r<=0))throw new Error("Cache timeout must be a positive number");var t=e[n];t&&clearTimeout(t.timeout);var o={value:i,expire:r+Date.now()};return isNaN(o.expire)||(o.timeout=setTimeout((()=>a(n)),r)),e[n]=o,i},this.del=function(n){var i=!0,r=e[n];return r?(clearTimeout(r.timeout),!isNaN(r.expire)&&r.expire<Date.now()&&(i=!1)):i=!1,i&&a(n),i},this.clear=function(){for(var a in e)clearTimeout(e[a].timeout);e=Object.create(null)},this.get=function(a){var n=e[a];if(void 0!==n){if(isNaN(n.expire)||n.expire>=Date.now())return n.value;delete e[a]}return null}}const exp$1=new Cache;exp$1.Cache=Cache;const base="https://translate.googleapis.com/translate_a/single";var google={fetch:({key:e,from:a,to:n,text:i})=>[`${base}?client=gtx&sl=${a}&tl=${n}&dt=t&q=${encodeURI(i)}`],parse:e=>e.json().then((e=>{if(!(e=e&&e[0]&&e[0][0]&&e[0].map((e=>e[0])).join("")))throw new Error("Translation not found");return e}))},yandex={needkey:!0,fetch:({key:e,from:a,to:n,text:i})=>[`https://translate.yandex.net/api/v1.5/tr.json/translate?key=${e}&lang=${a}-${n}&text=${encodeURIComponent(i)}`,{method:"POST",body:""}],parse:e=>e.json().then((e=>{if(200!==e.code)throw new Error(e.message);return e.text[0]}))};const libreUrl="https://libretranslate.com/translate";var libre={needkey:!1,fetch:({url:e=libreUrl,key:a,from:n,to:i,text:r})=>[e,{method:"POST",body:JSON.stringify({q:r,source:n,target:i,api_key:a}),headers:{"Content-Type":"application/json"}}],parse:e=>e.json().then((e=>{if(!e)throw new Error("No response found");if(e.error)throw new Error(e.error);if(!e.translatedText)throw new Error("No response found");return e.translatedText}))},deepl={needkey:!0,fetch:({key:e,from:a,to:n,text:i})=>[`https://api${e.endsWith(":fx")?"-free":""}.deepl.com/v2/translate?auth_key=${e}&source_lang=${a}&target_lang=${n}&text=${i=encodeURIComponent(i)}`,{method:"POST",body:""}],parse:async e=>{if(!e.ok){if(403===e.status)throw new Error("Auth Error, please review the key for DeepL");throw new Error(`Error ${e.status}`)}return e.json().then((e=>e.translations[0].text))}},engines={google:google,yandex:yandex,libre:libre,deepl:deepl},iso={aar:"aa",abk:"ab",afr:"af",aka:"ak",alb:"sq",amh:"am",ara:"ar",arg:"an",arm:"hy",asm:"as",ava:"av",ave:"ae",aym:"ay",aze:"az",bak:"ba",bam:"bm",baq:"eu",bel:"be",ben:"bn",bih:"bh",bis:"bi",bos:"bs",bre:"br",bul:"bg",bur:"my",cat:"ca",cha:"ch",che:"ce",chi:"zh",chu:"cu",chv:"cv",cor:"kw",cos:"co",cre:"cr",cze:"cs",dan:"da",div:"dv",dut:"nl",dzo:"dz",eng:"en",epo:"eo",est:"et",ewe:"ee",fao:"fo",fij:"fj",fin:"fi",fre:"fr",fry:"fy",ful:"ff",geo:"ka",ger:"de",gla:"gd",gle:"ga",glg:"gl",glv:"gv",gre:"el",grn:"gn",guj:"gu",hat:"ht",hau:"ha",heb:"he",her:"hz",hin:"hi",hmo:"ho",hrv:"hr",hun:"hu",ibo:"ig",ice:"is",ido:"io",iii:"ii",iku:"iu",ile:"ie",ina:"ia",ind:"id",ipk:"ik",ita:"it",jav:"jv",jpn:"ja",kal:"kl",kan:"kn",kas:"ks",kau:"kr",kaz:"kk",khm:"km",kik:"ki",kin:"rw",kir:"ky",kom:"kv",kon:"kg",kor:"ko",kua:"kj",kur:"ku",lao:"lo",lat:"la",lav:"lv",lim:"li",lin:"ln",lit:"lt",ltz:"lb",lub:"lu",lug:"lg",mac:"mk",mah:"mh",mal:"ml",mao:"mi",mar:"mr",may:"ms",mlg:"mg",mlt:"mt",mon:"mn",nau:"na",nav:"nv",nbl:"nr",nde:"nd",ndo:"ng",nep:"ne",nno:"nn",nob:"nb",nor:"no",nya:"ny",oci:"oc",oji:"oj",ori:"or",orm:"om",oss:"os",pan:"pa",per:"fa",pli:"pi",pol:"pl",por:"pt",pus:"ps",que:"qu",roh:"rm",rum:"ro",run:"rn",rus:"ru",sag:"sg",san:"sa",sin:"si",slo:"sk",slv:"sl",sme:"se",smo:"sm",sna:"sn",snd:"sd",som:"so",sot:"st",spa:"es",srd:"sc",srp:"sr",ssw:"ss",sun:"su",swa:"sw",swe:"sv",tah:"ty",tam:"ta",tat:"tt",tel:"te",tgk:"tg",tgl:"tl",tha:"th",tib:"bo",tir:"ti",ton:"to",tsn:"tn",tso:"ts",tuk:"tk",tur:"tr",twi:"tw",uig:"ug",ukr:"uk",urd:"ur",uzb:"uz",ven:"ve",vie:"vi",vol:"vo",wel:"cy",wln:"wa",wol:"wo",xho:"xh",yid:"yi",yor:"yo",zha:"za",zul:"zu"},names={afar:"aa",abkhazian:"ab",afrikaans:"af",akan:"ak",albanian:"sq",amharic:"am",arabic:"ar",aragonese:"an",armenian:"hy",assamese:"as",avaric:"av",avestan:"ae",aymara:"ay",azerbaijani:"az",bashkir:"ba",bambara:"bm",basque:"eu",belarusian:"be",bengali:"bn","bihari languages":"bh",bislama:"bi",tibetan:"bo",bosnian:"bs",breton:"br",bulgarian:"bg",burmese:"my",catalan:"ca",valencian:"ca",czech:"cs",chamorro:"ch",chechen:"ce",chinese:"zh","church slavic":"cu","old slavonic":"cu","church slavonic":"cu","old bulgarian":"cu","old church slavonic":"cu",chuvash:"cv",cornish:"kw",corsican:"co",cree:"cr",welsh:"cy",danish:"da",german:"de",divehi:"dv",dhivehi:"dv",maldivian:"dv",dutch:"nl",flemish:"nl",dzongkha:"dz",greek:"el",english:"en",esperanto:"eo",estonian:"et",ewe:"ee",faroese:"fo",persian:"fa",fijian:"fj",finnish:"fi",french:"fr","western frisian":"fy",fulah:"ff",georgian:"ka",gaelic:"gd","scottish gaelic":"gd",irish:"ga",galician:"gl",manx:"gv",guarani:"gn",gujarati:"gu",haitian:"ht","haitian creole":"ht",hausa:"ha",hebrew:"he",herero:"hz",hindi:"hi","hiri motu":"ho",croatian:"hr",hungarian:"hu",igbo:"ig",icelandic:"is",ido:"io","sichuan yi":"ii",nuosu:"ii",inuktitut:"iu",interlingue:"ie",occidental:"ie",interlingua:"ia",indonesian:"id",inupiaq:"ik",italian:"it",javanese:"jv",japanese:"ja",kalaallisut:"kl",greenlandic:"kl",kannada:"kn",kashmiri:"ks",kanuri:"kr",kazakh:"kk","central khmer":"km",kikuyu:"ki",gikuyu:"ki",kinyarwanda:"rw",kirghiz:"ky",kyrgyz:"ky",komi:"kv",kongo:"kg",korean:"ko",kuanyama:"kj",kwanyama:"kj",kurdish:"ku",lao:"lo",latin:"la",latvian:"lv",limburgan:"li",limburger:"li",limburgish:"li",lingala:"ln",lithuanian:"lt",luxembourgish:"lb",letzeburgesch:"lb","luba-katanga":"lu",ganda:"lg",macedonian:"mk",marshallese:"mh",malayalam:"ml",maori:"mi",marathi:"mr",malay:"ms",malagasy:"mg",maltese:"mt",mongolian:"mn",nauru:"na",navajo:"nv",navaho:"nv","ndebele, south":"nr","south ndebele":"nr","ndebele, north":"nd","north ndebele":"nd",ndonga:"ng",nepali:"ne","norwegian nynorsk":"nn","nynorsk, norwegian":"nn","norwegian bokmål":"nb","bokmål, norwegian":"nb",norwegian:"no",chichewa:"ny",chewa:"ny",nyanja:"ny",occitan:"oc",ojibwa:"oj",oriya:"or",oromo:"om",ossetian:"os",ossetic:"os",panjabi:"pa",punjabi:"pa",pali:"pi",polish:"pl",portuguese:"pt",pushto:"ps",pashto:"ps",quechua:"qu",romansh:"rm",romanian:"ro",moldavian:"ro",moldovan:"ro",rundi:"rn",russian:"ru",sango:"sg",sanskrit:"sa",sinhala:"si",sinhalese:"si",slovak:"sk",slovenian:"sl","northern sami":"se",samoan:"sm",shona:"sn",sindhi:"sd",somali:"so","sotho, southern":"st",spanish:"es",castilian:"es",sardinian:"sc",serbian:"sr",swati:"ss",sundanese:"su",swahili:"sw",swedish:"sv",tahitian:"ty",tamil:"ta",tatar:"tt",telugu:"te",tajik:"tg",tagalog:"tl",thai:"th",tigrinya:"ti",tonga:"to",tswana:"tn",tsonga:"ts",turkmen:"tk",turkish:"tr",twi:"tw",uighur:"ug",uyghur:"ug",ukrainian:"uk",urdu:"ur",uzbek:"uz",venda:"ve",vietnamese:"vi","volapük":"vo",walloon:"wa",wolof:"wo",xhosa:"xh",yiddish:"yi",yoruba:"yo",zhuang:"za",chuang:"za",zulu:"zu"};const isoKeys=Object.values(iso).sort();var languages=e=>{if("string"!=typeof e)throw new Error('The "language" must be a string, received '+typeof e);if(e.length>100)throw new Error(`The "language" is too long at ${e.length} characters`);if(e=e.toLowerCase(),e=names[e]||iso[e]||e,!isoKeys.includes(e))throw new Error(`The language "${e}" is not part of the ISO 639-1`);return e};const Translate=function(e={}){if(!(this instanceof Translate))return new Translate(e);const a={from:"en",to:"en",cache:void 0,engine:"google",key:void 0,url:void 0,languages:languages,engines:engines,keys:{}},n=async(e,a={})=>{"string"==typeof a&&(a={to:a});const i=Object.keys(a).find((e=>"from"!==e&&"to"!==e));if(i)throw new Error(`Invalid option with the name '${i}'`);a.text=e,a.from=languages(a.from||n.from),a.to=languages(a.to||n.to),a.cache=n.cache,a.engine=n.engine,a.url=n.url,a.id=`${a.url}:${a.from}:${a.to}:${a.engine}:${a.text}`,a.keys=n.keys||{};for(let e in n.keys)a.keys[e]=a.keys[e]||n.keys[e];a.key=a.key||n.key||a.keys[a.engine];const r=n.engines[a.engine],t=exp$1.get(a.id);if(t)return Promise.resolve(t);if(a.to===a.from)return Promise.resolve(a.text);if(r.needkey&&!a.key)throw new Error(`The engine "${a.engine}" needs a key, please provide it`);const o=r.fetch(a);return fetch(...o).then(r.parse).then((e=>exp$1.set(a.id,e,a.cache)))};for(let i in a)n[i]=void 0===e[i]?a[i]:e[i];return n},exp=new Translate;exp.Translate=Translate;export{exp as default};
import translate from "..";
  // translate("hello world", { abc: "def" });
  translate("hello world", { key: "abc" }); // <= not a real key
  translate("hello world", { engine: "google" });
  translate("hello world", { keys: { google: "abc" } }); // <= not a real key
  translate("hello world", { cache: undefined });
  translate("hello world", { cache: 1000 });
  translate("hello world", {
  const text: string = await translate("hello");
  console.log(text);

Fixed code:
  src="https://cdn.jsdelivr.net/npm/translate/index.min.js"
Then you can use it. Putting it all together:
Here is a list of all the options. They all can be set on the root instance like `translate[key] = value` and when creating a new instance `const t = Translate({ ... })`. The function has this signature:
```js
translate(text: string, to: string): Promise<string>
translate(text: string, { to: string, from: string }): Promise<string>
```

These are all of the options:
- **`cache`** \*: a `Number` with the milliseconds that each translation should be cached. Leave it undefined to cache it indefinitely (until a server/browser restart).
- **`engine`** \* (& `engines`): a `String` containing the name of the engine to use for translation. Right now it defaults to `google`. Read more [in the engine section](#engines).
- **`key`** \* (& `keys`): the API Key for the engine of your choice. Read more [in the engine section](#engines).
- **`url`** \*: only available for those engines that you can install on your own server (like Libretranslate), allows you to specify a custom endpoint for the translations. [See this issue](https://github.com/franciscop/translate/issues/26#issuecomment-845038821) for more info.
> \* The options marked as can only be set to the root `translate.cache = 1000` or when creating a new instance `const myDeepL = translate.Translate()`, but not as the second parameter.
You can change the options in 3 ways. First, for the simple `from` and `to` options, set them as the second argument:

```js
import translate from "translate";

translate("Hello world", "es");
translate("Hola mundo", { from: "en", to: "es" });
```

You can also change the default options straight on the base instance, which should be common for the shared options such as the API key, engine, etc:
import translate from "translate";

// Configure it, setting the "from" to "Spanish" as default!
translate.key = process.env.DEEPL_KEY;

// Use it
Finally, you can combine multiple instances of Translate, which is useful to have different caches, or use different engines at the same time:
// Import the constructor
import { Translate } from 'translate';

// Create an instance
const myLib = Translate({ engine: 'deepl', from: 'es', ... });

// Use it
import translate from "translate";
import translate from "translate";
import { Translate } from "translate";

const gTranslate = Translate({ engine: "google", key: "..." });
const dTranslate = Translate({ engine: "deepl", key: "..." });
const lTranslate = Translate({ engine: "libre", key: "..." });
const lTranslate = Translate({
This library uses promises which need to be awaited for the translated result:
type Options = string | { from?: string; to?: string };

declare const translate: {
  (text: string, opts?: Options): Promise<string>;

  key?: string;
  engine?: "google" | "deepl" | "libre" | "yandex";

  // More advanced types when tweaking the library
  keys?: { [name: string]: string };
  cache?: number;
  engines?: { [name: string]: Engine };

  Translate({}: {
    from?: string;
    to?: string;

    key?: string;
    engine?: "google" | "deepl" | "libre" | "yandex";

    // More advanced types when tweaking the library
    keys?: { [name: string]: string };
    cache?: number;
    engines?: { [name: string]: Engine };
  }): typeof translate;
};

declare const Translate: ({}: {
  from?: string;
  to?: string;

  key?: string;
  engine?: "google" | "deepl" | "libre" | "yandex";

  // More advanced types when tweaking the library
  keys?: { [name: string]: string };
  cache?: number;
  engines?: { [name: string]: Engine };
}) => typeof translate;

export default translate;
export { Translate };
function Cache(){var e=Object.create(null);function a(a){delete e[a]}this.set=function(n,i,r){if(void 0!==r&&("number"!=typeof r||isNaN(r)||r<=0))throw new Error("Cache timeout must be a positive number");var t=e[n];t&&clearTimeout(t.timeout);var o={value:i,expire:r+Date.now()};return isNaN(o.expire)||(o.timeout=setTimeout((()=>a(n)),r)),e[n]=o,i},this.del=function(n){var i=!0,r=e[n];return r?(clearTimeout(r.timeout),!isNaN(r.expire)&&r.expire<Date.now()&&(i=!1)):i=!1,i&&a(n),i},this.clear=function(){for(var a in e)clearTimeout(e[a].timeout);e=Object.create(null)},this.get=function(a){var n=e[a];if(void 0!==n){if(isNaN(n.expire)||n.expire>=Date.now())return n.value;delete e[a]}return null}}const exp$1=new Cache;exp$1.Cache=Cache;const base="https://translate.googleapis.com/translate_a/single";var google={fetch:({key:e,from:a,to:n,text:i})=>[`${base}?client=gtx&sl=${a}&tl=${n}&dt=t&q=${encodeURI(i)}`],parse:e=>e.json().then((e=>{if(!(e=e&&e[0]&&e[0][0]&&e[0].map((e=>e[0])).join("")))throw new Error("Translation not found");return e}))},yandex={needkey:!0,fetch:({key:e,from:a,to:n,text:i})=>[`https://translate.yandex.net/api/v1.5/tr.json/translate?key=${e}&lang=${a}-${n}&text=${encodeURIComponent(i)}`,{method:"POST",body:""}],parse:e=>e.json().then((e=>{if(200!==e.code)throw new Error(e.message);return e.text[0]}))};const libreUrl="https://libretranslate.com/translate";var libre={needkey:!1,fetch:({url:e=libreUrl,key:a,from:n,to:i,text:r})=>[e,{method:"POST",body:JSON.stringify({q:r,source:n,target:i,api_key:a}),headers:{"Content-Type":"application/json"}}],parse:e=>e.json().then((e=>{if(!e)throw new Error("No response found");if(e.error)throw new Error(e.error);if(!e.translatedText)throw new Error("No response found");return e.translatedText}))},deepl={needkey:!0,fetch:({key:e,from:a,to:n,text:i})=>[`https://api${e.endsWith(":fx")?"-free":""}.deepl.com/v2/translate?auth_key=${e}&source_lang=${a}&target_lang=${n}&text=${i=encodeURIComponent(i)}`,{method:"POST",body:""}],parse:async e=>{if(!e.ok){if(403===e.status)throw new Error("Auth Error, please review the key for DeepL");throw new Error(`Error ${e.status}`)}return e.json().then((e=>e.translations[0].text))}},engines={google:google,yandex:yandex,libre:libre,deepl:deepl},iso={aar:"aa",abk:"ab",afr:"af",aka:"ak",alb:"sq",amh:"am",ara:"ar",arg:"an",arm:"hy",asm:"as",ava:"av",ave:"ae",aym:"ay",aze:"az",bak:"ba",bam:"bm",baq:"eu",bel:"be",ben:"bn",bih:"bh",bis:"bi",bos:"bs",bre:"br",bul:"bg",bur:"my",cat:"ca",cha:"ch",che:"ce",chi:"zh",chu:"cu",chv:"cv",cor:"kw",cos:"co",cre:"cr",cze:"cs",dan:"da",div:"dv",dut:"nl",dzo:"dz",eng:"en",epo:"eo",est:"et",ewe:"ee",fao:"fo",fij:"fj",fin:"fi",fre:"fr",fry:"fy",ful:"ff",geo:"ka",ger:"de",gla:"gd",gle:"ga",glg:"gl",glv:"gv",gre:"el",grn:"gn",guj:"gu",hat:"ht",hau:"ha",heb:"he",her:"hz",hin:"hi",hmo:"ho",hrv:"hr",hun:"hu",ibo:"ig",ice:"is",ido:"io",iii:"ii",iku:"iu",ile:"ie",ina:"ia",ind:"id",ipk:"ik",ita:"it",jav:"jv",jpn:"ja",kal:"kl",kan:"kn",kas:"ks",kau:"kr",kaz:"kk",khm:"km",kik:"ki",kin:"rw",kir:"ky",kom:"kv",kon:"kg",kor:"ko",kua:"kj",kur:"ku",lao:"lo",lat:"la",lav:"lv",lim:"li",lin:"ln",lit:"lt",ltz:"lb",lub:"lu",lug:"lg",mac:"mk",mah:"mh",mal:"ml",mao:"mi",mar:"mr",may:"ms",mlg:"mg",mlt:"mt",mon:"mn",nau:"na",nav:"nv",nbl:"nr",nde:"nd",ndo:"ng",nep:"ne",nno:"nn",nob:"nb",nor:"no",nya:"ny",oci:"oc",oji:"oj",ori:"or",orm:"om",oss:"os",pan:"pa",per:"fa",pli:"pi",pol:"pl",por:"pt",pus:"ps",que:"qu",roh:"rm",rum:"ro",run:"rn",rus:"ru",sag:"sg",san:"sa",sin:"si",slo:"sk",slv:"sl",sme:"se",smo:"sm",sna:"sn",snd:"sd",som:"so",sot:"st",spa:"es",srd:"sc",srp:"sr",ssw:"ss",sun:"su",swa:"sw",swe:"sv",tah:"ty",tam:"ta",tat:"tt",tel:"te",tgk:"tg",tgl:"tl",tha:"th",tib:"bo",tir:"ti",ton:"to",tsn:"tn",tso:"ts",tuk:"tk",tur:"tr",twi:"tw",uig:"ug",ukr:"uk",urd:"ur",uzb:"uz",ven:"ve",vie:"vi",vol:"vo",wel:"cy",wln:"wa",wol:"wo",xho:"xh",yid:"yi",yor:"yo",zha:"za",zul:"zu"},names={afar:"aa",abkhazian:"ab",afrikaans:"af",akan:"ak",albanian:"sq",amharic:"am",arabic:"ar",aragonese:"an",armenian:"hy",assamese:"as",avaric:"av",avestan:"ae",aymara:"ay",azerbaijani:"az",bashkir:"ba",bambara:"bm",basque:"eu",belarusian:"be",bengali:"bn","bihari languages":"bh",bislama:"bi",tibetan:"bo",bosnian:"bs",breton:"br",bulgarian:"bg",burmese:"my",catalan:"ca",valencian:"ca",czech:"cs",chamorro:"ch",chechen:"ce",chinese:"zh","church slavic":"cu","old slavonic":"cu","church slavonic":"cu","old bulgarian":"cu","old church slavonic":"cu",chuvash:"cv",cornish:"kw",corsican:"co",cree:"cr",welsh:"cy",danish:"da",german:"de",divehi:"dv",dhivehi:"dv",maldivian:"dv",dutch:"nl",flemish:"nl",dzongkha:"dz",greek:"el",english:"en",esperanto:"eo",estonian:"et",ewe:"ee",faroese:"fo",persian:"fa",fijian:"fj",finnish:"fi",french:"fr","western frisian":"fy",fulah:"ff",georgian:"ka",gaelic:"gd","scottish gaelic":"gd",irish:"ga",galician:"gl",manx:"gv",guarani:"gn",gujarati:"gu",haitian:"ht","haitian creole":"ht",hausa:"ha",hebrew:"he",herero:"hz",hindi:"hi","hiri motu":"ho",croatian:"hr",hungarian:"hu",igbo:"ig",icelandic:"is",ido:"io","sichuan yi":"ii",nuosu:"ii",inuktitut:"iu",interlingue:"ie",occidental:"ie",interlingua:"ia",indonesian:"id",inupiaq:"ik",italian:"it",javanese:"jv",japanese:"ja",kalaallisut:"kl",greenlandic:"kl",kannada:"kn",kashmiri:"ks",kanuri:"kr",kazakh:"kk","central khmer":"km",kikuyu:"ki",gikuyu:"ki",kinyarwanda:"rw",kirghiz:"ky",kyrgyz:"ky",komi:"kv",kongo:"kg",korean:"ko",kuanyama:"kj",kwanyama:"kj",kurdish:"ku",lao:"lo",latin:"la",latvian:"lv",limburgan:"li",limburger:"li",limburgish:"li",lingala:"ln",lithuanian:"lt",luxembourgish:"lb",letzeburgesch:"lb","luba-katanga":"lu",ganda:"lg",macedonian:"mk",marshallese:"mh",malayalam:"ml",maori:"mi",marathi:"mr",malay:"ms",malagasy:"mg",maltese:"mt",mongolian:"mn",nauru:"na",navajo:"nv",navaho:"nv","ndebele, south":"nr","south ndebele":"nr","ndebele, north":"nd","north ndebele":"nd",ndonga:"ng",nepali:"ne","norwegian nynorsk":"nn","nynorsk, norwegian":"nn","norwegian bokmål":"nb","bokmål, norwegian":"nb",norwegian:"no",chichewa:"ny",chewa:"ny",nyanja:"ny",occitan:"oc",ojibwa:"oj",oriya:"or",oromo:"om",ossetian:"os",ossetic:"os",panjabi:"pa",punjabi:"pa",pali:"pi",polish:"pl",portuguese:"pt",pushto:"ps",pashto:"ps",quechua:"qu",romansh:"rm",romanian:"ro",moldavian:"ro",moldovan:"ro",rundi:"rn",russian:"ru",sango:"sg",sanskrit:"sa",sinhala:"si",sinhalese:"si",slovak:"sk",slovenian:"sl","northern sami":"se",samoan:"sm",shona:"sn",sindhi:"sd",somali:"so","sotho, southern":"st",spanish:"es",castilian:"es",sardinian:"sc",serbian:"sr",swati:"ss",sundanese:"su",swahili:"sw",swedish:"sv",tahitian:"ty",tamil:"ta",tatar:"tt",telugu:"te",tajik:"tg",tagalog:"tl",thai:"th",tigrinya:"ti",tonga:"to",tswana:"tn",tsonga:"ts",turkmen:"tk",turkish:"tr",twi:"tw",uighur:"ug",uyghur:"ug",ukrainian:"uk",urdu:"ur",uzbek:"uz",venda:"ve",vietnamese:"vi","volapük":"vo",walloon:"wa",wolof:"wo",xhosa:"xh",yiddish:"yi",yoruba:"yo",zhuang:"za",chuang:"za",zulu:"zu"};const isoKeys=Object.values(iso).sort();var languages=e=>{if("string"!=typeof e)throw new Error('The "language" must be a string, received '+typeof e);if(e.length>100)throw new Error(`The "language" is too long at ${e.length} characters`);if(e=e.toLowerCase(),e=names[e]||iso[e]||e,!isoKeys.includes(e))throw new Error(`The language "${e}" is not part of the ISO 639-1`);return e};const Translate=function(e={}){if(!(this instanceof Translate))return new Translate(e);const a={from:"en",to:"en",cache:void 0,engine:"google",key:void 0,url:void 0,languages:languages,engines:engines,keys:{}},n=async(e,a={})=>{"string"==typeof a&&(a={to:a});const i=Object.keys(a).find((e=>"from"!==e&&"to"!==e));if(i)throw new Error(`Invalid option with the name '${i}'`);a.text=e,a.from=languages(a.from||n.from),a.to=languages(a.to||n.to),a.cache=n.cache,a.engine=n.engine,a.url=n.url,a.id=`${a.url}:${a.from}:${a.to}:${a.engine}:${a.text}`,a.keys=n.keys||{};for(let e in n.keys)a.keys[e]=a.keys[e]||n.keys[e];a.key=a.key||n.key||a.keys[a.engine];const r=n.engines[a.engine],t=exp$1.get(a.id);if(t)return Promise.resolve(t);if(a.to===a.from)return Promise.resolve(a.text);if(r.needkey&&!a.key)throw new Error(`The engine "${a.engine}" needs a key, please provide it`);const o=r.fetch(a);return fetch(...o).then(r.parse).then((e=>exp$1.set(a.id,e,a.cache)))};for(let i in a)n[i]=void 0===e[i]?a[i]:e[i];return n},exp=new Translate;exp.Translate=Translate;export{Translate,exp as default};
export { Translate };
import translate, { Translate } from "..";
  // New stuff
  translate.key = "abc"; // <= not a real key
  translate.engine = "google";

  translate.keys = { google: "abc" }; // <= not a real key
  translate.cache = undefined;
  translate.cache = 1000;
  translate.engines = {
    google: {
      needkey: false,
      fetch: () => ["abc", {}],
      parse: (req) => "hello",
    },
  };

  // Instance
  const t = translate.Translate({
    from: "en",
    to: "es",

    // New stuff
    key: "abc", // <= not a real key
    engine: "google",

    keys: { google: "abc" }, // <= not a real key
    cache: 1000,
  t("hello world", "es");
  t("hello world", { from: "en" });
  t("hello world", { to: "es" });

  // New stuff
  t.key = "abc"; // <= not a real key
  t.engine = "google";
  t.keys = { google: "abc" }; // <= not a real key
  t.cache = undefined;
  t.cache = 1000;
  t.engines = {
    google: {
      needkey: false,
      fetch: () => ["abc", {}],
      parse: (req) => "hello",
    },
  };

  // Another instance
  const t2 = Translate({
    from: "en",
    to: "es",

    // New stuff
    key: "abc", // <= not a real key
    engine: "google",

    keys: { google: "abc" }, // <= not a real key
    cache: 1000,
    engines: {
      google: {
        needkey: false,
        fetch: () => ["abc", {}],
        parse: (req) => "hello",
      },
    },
  });
  t2("hello world", "es");
  t2("hello world", { from: "en" });
  t2("hello world", { to: "es" });

  // New stuff
  t2.key = "abc"; // <= not a real key
  t2.engine = "google";

  t2.keys = { google: "abc" }; // <= not a real key
  t2.cache = undefined;
  t2.cache = 1000;
  t2.engines = {
    google: {
      needkey: false,
      fetch: () => ["abc", {}],
      parse: (req) => "hello",
    },
  };