@magic-translate/js 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,11 @@ the text on your page, and Magic Translate translates it on the fly. Works on
8
8
  any website: plain HTML, server-rendered pages, WordPress, or apps built with
9
9
  any framework.
10
10
 
11
+ No translation keys and no language files: the text stays in your HTML, and
12
+ adding a language is one line.
13
+
14
+ Full docs: [magictranslate.io/docs/javascript](https://magictranslate.io/docs/javascript)
15
+
11
16
  ## Setup with a script tag
12
17
 
13
18
  Add one script tag to your page. You can get an API key for free at
@@ -26,19 +31,21 @@ Then wrap the text to translate in `<t>`:
26
31
 
27
32
  ```html
28
33
  <h1><t>Welcome to our shop</t></h1>
29
- <p><t>Click <a href="/sale">here</a> to see <b>today's</b> deals.</t></p>
34
+ <p>
35
+ <t>Click <a href="/sale">here</a> to see <b>today's</b> deals.</t>
36
+ </p>
30
37
  ```
31
38
 
32
39
  Leave out `data-language` to translate to the visitor's browser language. The
33
40
  script tag accepts:
34
41
 
35
- | Attribute | Description |
36
- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
37
- | `data-api-key` | Required. Your Magic Translate API key. |
38
- | `data-language` | The language content is translated to, e.g. `de`. Defaults to the browser's language. |
42
+ | Attribute | Description |
43
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
44
+ | `data-api-key` | Required. Your Magic Translate API key. |
45
+ | `data-language` | The language content is translated to, e.g. `de`. Defaults to the browser's language. |
39
46
  | `data-source-language` | The language your page is written in. Detected automatically by default; only set it if automatic detection gives wrong results. |
40
- | `data-formality` | `formal` or `informal`. Not supported for all languages. |
41
- | `data-debug` | Logs requests to the console. |
47
+ | `data-formality` | `formal` or `informal`. Not supported for all languages. |
48
+ | `data-debug` | Logs requests to the console. |
42
49
 
43
50
  The script makes the running instance available as `window.magicTranslate`.
44
51
 
@@ -89,6 +96,49 @@ List the attributes to translate in `data-t-attrs`:
89
96
  <img src="team.jpg" alt="Our team" title="Say hi" data-t-attrs="alt title" />
90
97
  ```
91
98
 
99
+ ### Variables
100
+
101
+ Put values that change, such as names or counts, in `data-v` as a JSON object,
102
+ and reference them as `$name`. The text is translated once as a template,
103
+ `Hi {0}, you have {1} new messages`, and cached, however many different values
104
+ it is shown with. The values themselves are never sent to the API.
105
+
106
+ ```html
107
+ <t data-v='{"name": "Bob", "count": 7}'
108
+ >Hi $name, you have $count new messages</t
109
+ >
110
+
111
+ <input
112
+ placeholder="Search $count products"
113
+ data-t-attrs="placeholder"
114
+ data-v='{"count": 3}'
115
+ />
116
+ ```
117
+
118
+ To show new values, change `data-v`. The cached translation is reused and no
119
+ request is made:
120
+
121
+ ```js
122
+ element.dataset.v = JSON.stringify({ name: 'Bob', count: 8 });
123
+ ```
124
+
125
+ - Only keys of `data-v` are replaced, so text like `$5` stays as it is. If two
126
+ keys start the same way, the longest one wins.
127
+ - Values are inserted as text, never as HTML. Put markup in the text around
128
+ them instead: `Hi <b>$name</b>`.
129
+ - In content, variables are replaced in text only, not in attributes such as
130
+ `href`.
131
+ - If `data-v` is not a valid JSON object, the text is left untranslated and a
132
+ warning is logged. So is a key that is not found in the text.
133
+ - `$name` is visible until the script has loaded. Load it early, or hide the
134
+ elements until then.
135
+
136
+ `translate` takes the values in `v`:
137
+
138
+ ```js
139
+ await magicTranslate.translate('Your order $id has shipped', { v: { id } });
140
+ ```
141
+
92
142
  ### Options per element
93
143
 
94
144
  `data-lang` and `data-formality` override the target language and the
@@ -144,14 +194,14 @@ document.addEventListener('magictranslate:ready', (event) => {
144
194
 
145
195
  `createMagicTranslate` accepts:
146
196
 
147
- | Option | Description |
148
- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
149
- | `apiKey` | Required. Your Magic Translate API key. |
150
- | `language` | The language content is translated to, e.g. `'de'`. Defaults to the browser's language. |
197
+ | Option | Description |
198
+ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
199
+ | `apiKey` | Required. Your Magic Translate API key. |
200
+ | `language` | The language content is translated to, e.g. `'de'`. Defaults to the browser's language. |
151
201
  | `sourceLanguage` | The language your page is written in. Detected automatically by default; only set it if automatic detection gives wrong results. |
152
- | `formality` | `'formal'` or `'informal'`. Not supported for all languages. Can be overridden per element. |
153
- | `root` | The element whose content is translated. Defaults to the whole document. |
154
- | `debug` | Logs requests to the console. |
202
+ | `formality` | `'formal'` or `'informal'`. Not supported for all languages. Can be overridden per element. |
203
+ | `root` | The element whose content is translated. Defaults to the whole document. |
204
+ | `debug` | Logs requests to the console. |
155
205
 
156
206
  Call `destroy()` to stop translating the page.
157
207
 
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@magic-translate/core");var t=`t, mt-translate, [data-t]`,n=`[data-t-attrs]`;function r(e){let n=e;for(let e=n.parentElement?.closest(t);e;e=n.parentElement?.closest(t))n=e;return n}function i(e){let t={},n=e.getAttribute(`data-lang`),r=e.getAttribute(`data-formality`);return n&&(t.lang=n),r&&(t.formality=r),t}var a=class{constructor(t){if(this.options=t,this.contents=new Map,this.attributes=new Map,this.listeners=new Set,!t.apiKey)throw Error(`Missing API key. Please get one at ${e.libraryWebsite}`);this.currentLanguage=t.language??(0,e.utsLocaleToLanguage)(typeof navigator>`u`?void 0:navigator.language),this.dataLoader=this.createDataLoader(),typeof document<`u`&&(this.root=t.root??document.documentElement,this.start(this.root))}get language(){return this.currentLanguage}setLanguage(e){e!==this.currentLanguage&&(this.currentLanguage=e,this.dataLoader=this.createDataLoader(),this.forget(e=>!e.isConnected||!this.root?.contains(e)),this.contents.forEach((e,t)=>this.translateContent(t,!0)),this.attributes.forEach((e,t)=>this.translateAttributes(t,!0)),this.listeners.forEach(t=>t(e)))}onLanguageChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}translate(e,t={}){return this.dataLoader.load({text:e,...t})}destroy(){this.observer?.disconnect(),this.contents.clear(),this.attributes.clear(),this.listeners.clear()}createDataLoader(){let{debug:t,apiKey:n,sourceLanguage:r,_apiUrl:i,formality:a}=this.options,o=this.options._loaderFactory??e.defaultLoaderFactory;return new e.DataLoader(o({debug:!!t,apiKey:n,lang:this.currentLanguage,sourceLang:r,apiUrl:i,formality:a}),{cacheKeyFn:(0,e.cacheKeyFn)(n,r,this.currentLanguage)})}start(e){let t=new Set,n=new Set;this.collect(e,t,n),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e)),this.observer=new MutationObserver(e=>this.handleMutations(e)),this.observer.observe(e,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}collect(e,t,n){let i=[e,...Array.from(e.querySelectorAll(`*`))];for(let e of i)e.matches(`t, mt-translate, [data-t]`)?t.add(r(e)):e.matches(`[data-t-attrs]`)&&!e.closest(`t, mt-translate, [data-t]`)&&n.add(e)}handleMutations(e){let n=new Set,i=new Set;for(let a of e){let e=a.target.nodeType===Node.ELEMENT_NODE?a.target:a.target.parentElement;if(!e)continue;let o=e.closest(t);o?n.add(r(o)):(a.type===`attributes`&&e.matches(`[data-t-attrs]`)&&i.add(e),a.addedNodes.forEach(e=>{e.nodeType===Node.ELEMENT_NODE&&this.collect(e,n,i)}))}e.some(e=>e.removedNodes.length>0)&&this.forget(e=>!e.isConnected),n.forEach(e=>this.translateContent(e)),i.forEach(e=>this.translateAttributes(e))}forget(e){for(let t of[...this.contents.keys()])e(t)&&this.contents.delete(t);for(let t of[...this.attributes.keys()])e(t)&&this.attributes.delete(t)}translateContent(e,t=!1){let n=e.innerHTML,r=this.contents.get(e);if(!r)r={source:n,requestId:0},this.contents.set(e,r);else if(n!==r.rendered&&n!==r.source)r.source=n,r.rendered=void 0;else if(!t)return;let[,a,o,s]=/^(\s*)([\s\S]*?)(\s*)$/.exec(r.source.replace(/<!--[\s\S]*?-->/g,``));if(!o)return;let c=++r.requestId,l=r;this.translate(o,i(e)).then(t=>{if(c!==l.requestId||!e.isConnected)return;let n=`${a}${t}${s}`;e.innerHTML!==n&&(e.innerHTML=n),l.rendered=e.innerHTML})}translateAttributes(e,t=!1){let n=(e.getAttribute(`data-t-attrs`)??``).split(/[\s,]+/).filter(Boolean),r=this.attributes.get(e);r||(r=new Map,this.attributes.set(e,r));for(let a of n){let n=e.getAttribute(a);if(!n?.trim())continue;let o=r.get(a);if(!o)o={source:n,requestId:0},r.set(a,o);else if(n!==o.rendered&&n!==o.source)o.source=n,o.rendered=void 0;else if(!t)continue;let s=++o.requestId,c=o;this.translate(c.source,i(e)).then(t=>{s===c.requestId&&e.isConnected&&(c.rendered=t,e.getAttribute(a)!==t&&e.setAttribute(a,t))})}}};function o(e){return new a(e)}exports.ATTRIBUTES_SELECTOR=n,exports.CONTENT_SELECTOR=t,exports.MagicTranslate=a,exports.createMagicTranslate=o,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@magic-translate/core");var t=`t, mt-translate, [data-t]`,n=`[data-t-attrs]`;function r(e){let n=e;for(let e=n.parentElement?.closest(t);e;e=n.parentElement?.closest(t))n=e;return n}function i(e){let t={},n=e.getAttribute(`data-lang`),r=e.getAttribute(`data-formality`);return n&&(t.lang=n),r&&(t.formality=r),t}function a(t){let n=t.getAttribute(`data-v`);if(n!==null){try{let e=JSON.parse(n);if(e&&typeof e==`object`&&!Array.isArray(e))return e}catch{}return console.warn(`[${e.libraryName}]: data-v must be a JSON object, such as data-v='{"name": "Bob"}', but is ${n}`),null}}function o(t,n){if(n.length===0)return t;let r=document.createElement(`template`);return r.innerHTML=t,(0,e.fillElement)(r.content,n),r.innerHTML}var s=class{constructor(t){if(this.options=t,this.contents=new Map,this.attributes=new Map,this.listeners=new Set,!t.apiKey)throw Error(`Missing API key. Please get one at ${e.libraryWebsite}`);this.currentLanguage=t.language??(0,e.utsLocaleToLanguage)(typeof navigator>`u`?void 0:navigator.language),this.dataLoader=this.createDataLoader(),typeof document<`u`&&(this.root=t.root??document.documentElement,this.start(this.root))}get language(){return this.currentLanguage}setLanguage(e){e!==this.currentLanguage&&(this.currentLanguage=e,this.dataLoader=this.createDataLoader(),this.forget(e=>!e.isConnected||!this.root?.contains(e)),this.contents.forEach((e,t)=>this.translateContent(t,!0)),this.attributes.forEach((e,t)=>this.translateAttributes(t,!0)),this.listeners.forEach(t=>t(e)))}onLanguageChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}translate(t,{v:n,...r}={}){if(!n)return this.dataLoader.load({text:t,...r});let{template:i,values:a,unused:o}=(0,e.toTemplate)(t,n);return(0,e.warnAboutUnused)(o),this.dataLoader.load({text:i,...r}).then(t=>(0,e.fillPlaceholders)(t,a))}destroy(){this.observer?.disconnect(),this.contents.clear(),this.attributes.clear(),this.listeners.clear()}createDataLoader(){let{debug:t,apiKey:n,sourceLanguage:r,_apiUrl:i,formality:a}=this.options,o=this.options._loaderFactory??e.defaultLoaderFactory;return new e.DataLoader(o({debug:!!t,apiKey:n,lang:this.currentLanguage,sourceLang:r,apiUrl:i,formality:a}),{cacheKeyFn:(0,e.cacheKeyFn)(n,r,this.currentLanguage)})}start(e){let t=new Set,n=new Set;this.collect(e,t,n),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e)),this.observer=new MutationObserver(e=>this.handleMutations(e)),this.observer.observe(e,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}collect(e,t,n){let i=[e,...Array.from(e.querySelectorAll(`*`))];for(let e of i)e.matches(`t, mt-translate, [data-t]`)?t.add(r(e)):e.matches(`[data-t-attrs]`)&&!e.closest(`t, mt-translate, [data-t]`)&&n.add(e)}handleMutations(e){let n=new Set,i=new Set;for(let a of e){let e=a.target.nodeType===Node.ELEMENT_NODE?a.target:a.target.parentElement;if(!e)continue;let o=e.closest(t);o?n.add(r(o)):(a.type===`attributes`&&e.matches(`[data-t-attrs]`)&&i.add(e),a.addedNodes.forEach(e=>{e.nodeType===Node.ELEMENT_NODE&&this.collect(e,n,i)}))}e.some(e=>e.removedNodes.length>0)&&this.forget(e=>!e.isConnected),n.forEach(e=>this.translateContent(e)),i.forEach(e=>this.translateAttributes(e))}forget(e){for(let t of this.contents.keys())e(t)&&this.contents.delete(t);for(let t of this.attributes.keys())e(t)&&this.attributes.delete(t)}translateContent(t,n=!1){let r=t.innerHTML,s=t.getAttribute(`data-v`),c=this.contents.get(t);if(!c)c={source:r,requestId:0,variables:s},this.contents.set(t,c);else if(r!==c.rendered&&r!==c.source)c.source=r,c.rendered=void 0,c.variables=s;else if(s!==c.variables)c.variables=s;else if(!n)return;let[,l,u,d]=/^(\s*)([\s\S]*?)(\s*)$/.exec(c.source.replace(/<!--[\s\S]*?-->/g,``));if(!u)return;let f=a(t);if(f===null)return;let{template:p,values:m,unused:h}=f?(0,e.toHtmlTemplate)(u,f):{template:u,values:[],unused:[]};p!==c.template&&(c.template=p,(0,e.warnAboutUnused)(h));let g=c,_=e=>{let n=`${l}${o(e,m)}${d}`;t.innerHTML!==n&&(t.innerHTML=n),g.rendered=t.innerHTML};f&&g.rendered===void 0&&_(p);let v=++g.requestId;this.translate(p,i(t)).then(e=>{v===g.requestId&&t.isConnected&&_(e)})}translateAttributes(t,n=!1){let r=(t.getAttribute(`data-t-attrs`)??``).split(/[\s,]+/).filter(Boolean),o=t.getAttribute(`data-v`),s=this.attributes.get(t);s||(s=new Map,this.attributes.set(t,s));for(let c of r){let r=t.getAttribute(c);if(!r?.trim())continue;let l=s.get(c);if(!l)l={source:r,requestId:0,variables:o},s.set(c,l);else if(r!==l.rendered&&r!==l.source)l.source=r,l.rendered=void 0,l.variables=o;else if(o!==l.variables)l.variables=o;else if(!n)continue;let u=a(t);if(u===null)continue;let{template:d,values:f,unused:p}=u?(0,e.toTemplate)(l.source,u):{template:l.source,values:[],unused:[]};d!==l.template&&(l.template=d,(0,e.warnAboutUnused)(p));let m=l,h=n=>{let r=(0,e.fillPlaceholders)(n,f);m.rendered=r,t.getAttribute(c)!==r&&t.setAttribute(c,r)};u&&m.rendered===void 0&&h(d);let g=++m.requestId;this.translate(d,i(t)).then(e=>{g===m.requestId&&t.isConnected&&h(e)})}}};function c(e){return new s(e)}exports.ATTRIBUTES_SELECTOR=n,exports.CONTENT_SELECTOR=t,exports.MagicTranslate=s,exports.createMagicTranslate=c,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["libraryWebsite","utsLocaleToLanguage","defaultLoaderFactory","DataLoader","cacheKeyFn","_magic_translate_core","CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","MagicTranslate","language","listener","params","loaderFactory","debug","sourceLanguage","_apiUrl","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","html","translation","force","leadingWhitespace","text","trailingWhitespace","requestId","rendered","translated","names","translations","name","value","createMagicTranslate","k"],"sources":["../src/magic-translate.ts"],"sourcesContent":["import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n Language,\n libraryWebsite,\n MagicTranslateConfig,\n TranslateOptions,\n utsLocaleToLanguage,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport type TranslateParams = Omit<DataLoaderLoadParams, 'text'>;\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(text: string, params: TranslateParams = {}): Promise<string> {\n return this.dataLoader.load({ text, ...params });\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of [...this.contents.keys()]) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of [...this.attributes.keys()]) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n let translation = this.contents.get(element);\n\n if (!translation) {\n translation = { source: html, requestId: 0 };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(text, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n const rendered = `${leadingWhitespace}${translated}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0 };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n } else if (!force) {\n continue;\n }\n\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(current.source, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n current.rendered = translated;\n if (element.getAttribute(name) !== translated) {\n element.setAttribute(name, translated);\n }\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["libraryName","libraryWebsite","utsLocaleToLanguage","toTemplate","fillPlaceholders","defaultLoaderFactory","DataLoader","cacheKeyFn","toHtmlTemplate","_magic_translate_core","CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","variablesOf","json","variables","fillHtml","values","html","container","MagicTranslate","language","listener","params","v","text","unused","template","translation","loaderFactory","debug","sourceLanguage","_apiUrl","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","variablesJson","force","leadingWhitespace","trailingWhitespace","show","rendered","translated","requestId","names","translations","name","value","createMagicTranslate","k"],"sources":["../src/magic-translate.ts"],"sourcesContent":["import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n fillElement,\n fillPlaceholders,\n Language,\n libraryName,\n libraryWebsite,\n MagicTranslateConfig,\n toHtmlTemplate,\n toTemplate,\n TranslateOptions,\n utsLocaleToLanguage,\n Variables,\n warnAboutUnused,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport interface TranslateParams extends Omit<DataLoaderLoadParams, 'text'> {\n /**\n * Values that are not translated, referenced as `$name` in the text. The text\n * is translated once as a template and cached, whatever the values are.\n */\n v?: Variables;\n}\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n // the `data-v` attribute the source was last translated with\n variables: string | null;\n // to warn about unused variables once per text, not on every render\n template?: string;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\n/**\n * Reads the values in `data-v`, a JSON object such as `{\"name\": \"Bob\"}`.\n * Returns `null` if the attribute is not valid, so the text is left as it is\n * rather than translated with `$name` in it.\n */\nfunction variablesOf(element: Element): Variables | undefined | null {\n const json = element.getAttribute('data-v');\n if (json === null) {\n return undefined;\n }\n try {\n const variables: unknown = JSON.parse(json);\n if (\n variables &&\n typeof variables === 'object' &&\n !Array.isArray(variables)\n ) {\n return variables as Variables;\n }\n } catch {\n // reported below\n }\n console.warn(\n `[${libraryName}]: data-v must be a JSON object, such as data-v='{\"name\": \"Bob\"}', but is ${json}`\n );\n return null;\n}\n\nfunction fillHtml(html: string, values: readonly string[]): string {\n if (values.length === 0) {\n return html;\n }\n const container = document.createElement('template');\n container.innerHTML = html;\n fillElement(container.content, values);\n return container.innerHTML;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(\n text: string,\n { v, ...params }: TranslateParams = {}\n ): Promise<string> {\n if (!v) {\n return this.dataLoader.load({ text, ...params });\n }\n const { template, values, unused } = toTemplate(text, v);\n warnAboutUnused(unused);\n return this.dataLoader\n .load({ text: template, ...params })\n .then((translation) => fillPlaceholders(translation, values));\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of this.contents.keys()) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of this.attributes.keys()) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n const variablesJson = element.getAttribute('data-v');\n let translation = this.contents.get(element);\n if (!translation) {\n translation = { source: html, requestId: 0, variables: variablesJson };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n // only the values changed, so the cached translation is reused\n translation.variables = variablesJson;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n return;\n }\n const { template, values, unused } = variables\n ? toHtmlTemplate(text, variables)\n : { template: text, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = `${leadingWhitespace}${fillHtml(translated, values)}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n };\n // Until the first translation arrives, show the values instead of `$name`.\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n const variablesJson = element.getAttribute('data-v');\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0, variables: variablesJson };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n translation.variables = variablesJson;\n } else if (!force) {\n continue;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n continue;\n }\n const { template, values, unused } = variables\n ? toTemplate(translation.source, variables)\n : { template: translation.source, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = fillPlaceholders(translated, values);\n current.rendered = rendered;\n if (element.getAttribute(name) !== rendered) {\n element.setAttribute(name, rendered);\n }\n };\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,20 +1,35 @@
1
- import { DataLoader as e, cacheKeyFn as t, defaultLoaderFactory as n, libraryWebsite as r, utsLocaleToLanguage as i } from "@magic-translate/core";
1
+ import { DataLoader as e, cacheKeyFn as t, defaultLoaderFactory as n, fillElement as r, fillPlaceholders as i, libraryName as a, libraryWebsite as o, toHtmlTemplate as s, toTemplate as c, utsLocaleToLanguage as l, warnAboutUnused as u } from "@magic-translate/core";
2
2
  export * from "@magic-translate/core";
3
3
  // src/magic-translate.ts
4
- var a = "t, mt-translate, [data-t]", o = "[data-t-attrs]";
5
- function s(e) {
4
+ var d = "t, mt-translate, [data-t]", f = "[data-t-attrs]";
5
+ function p(e) {
6
6
  let t = e;
7
- for (let e = t.parentElement?.closest(a); e; e = t.parentElement?.closest(a)) t = e;
7
+ for (let e = t.parentElement?.closest(d); e; e = t.parentElement?.closest(d)) t = e;
8
8
  return t;
9
9
  }
10
- function c(e) {
10
+ function m(e) {
11
11
  let t = {}, n = e.getAttribute("data-lang"), r = e.getAttribute("data-formality");
12
12
  return n && (t.lang = n), r && (t.formality = r), t;
13
13
  }
14
- var l = class {
14
+ function h(e) {
15
+ let t = e.getAttribute("data-v");
16
+ if (t !== null) {
17
+ try {
18
+ let e = JSON.parse(t);
19
+ if (e && typeof e == "object" && !Array.isArray(e)) return e;
20
+ } catch {}
21
+ return console.warn(`[${a}]: data-v must be a JSON object, such as data-v='{"name": "Bob"}', but is ${t}`), null;
22
+ }
23
+ }
24
+ function g(e, t) {
25
+ if (t.length === 0) return e;
26
+ let n = document.createElement("template");
27
+ return n.innerHTML = e, r(n.content, t), n.innerHTML;
28
+ }
29
+ var _ = class {
15
30
  constructor(e) {
16
- if (this.options = e, this.contents = /* @__PURE__ */ new Map(), this.attributes = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Set(), !e.apiKey) throw Error(`Missing API key. Please get one at ${r}`);
17
- this.currentLanguage = e.language ?? i(typeof navigator > "u" ? void 0 : navigator.language), this.dataLoader = this.createDataLoader(), typeof document < "u" && (this.root = e.root ?? document.documentElement, this.start(this.root));
31
+ if (this.options = e, this.contents = /* @__PURE__ */ new Map(), this.attributes = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Set(), !e.apiKey) throw Error(`Missing API key. Please get one at ${o}`);
32
+ this.currentLanguage = e.language ?? l(typeof navigator > "u" ? void 0 : navigator.language), this.dataLoader = this.createDataLoader(), typeof document < "u" && (this.root = e.root ?? document.documentElement, this.start(this.root));
18
33
  }
19
34
  get language() {
20
35
  return this.currentLanguage;
@@ -25,11 +40,16 @@ var l = class {
25
40
  onLanguageChange(e) {
26
41
  return this.listeners.add(e), () => this.listeners.delete(e);
27
42
  }
28
- translate(e, t = {}) {
29
- return this.dataLoader.load({
43
+ translate(e, { v: t, ...n } = {}) {
44
+ if (!t) return this.dataLoader.load({
30
45
  text: e,
31
- ...t
46
+ ...n
32
47
  });
48
+ let { template: r, values: a, unused: o } = c(e, t);
49
+ return u(o), this.dataLoader.load({
50
+ text: r,
51
+ ...n
52
+ }).then((e) => i(e, a));
33
53
  }
34
54
  destroy() {
35
55
  this.observer?.disconnect(), this.contents.clear(), this.attributes.clear(), this.listeners.clear();
@@ -56,64 +76,92 @@ var l = class {
56
76
  }
57
77
  collect(e, t, n) {
58
78
  let r = [e, ...Array.from(e.querySelectorAll("*"))];
59
- for (let e of r) e.matches("t, mt-translate, [data-t]") ? t.add(s(e)) : e.matches("[data-t-attrs]") && !e.closest("t, mt-translate, [data-t]") && n.add(e);
79
+ for (let e of r) e.matches("t, mt-translate, [data-t]") ? t.add(p(e)) : e.matches("[data-t-attrs]") && !e.closest("t, mt-translate, [data-t]") && n.add(e);
60
80
  }
61
81
  handleMutations(e) {
62
82
  let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
63
83
  for (let r of e) {
64
84
  let e = r.target.nodeType === Node.ELEMENT_NODE ? r.target : r.target.parentElement;
65
85
  if (!e) continue;
66
- let i = e.closest(a);
67
- i ? t.add(s(i)) : (r.type === "attributes" && e.matches("[data-t-attrs]") && n.add(e), r.addedNodes.forEach((e) => {
86
+ let i = e.closest(d);
87
+ i ? t.add(p(i)) : (r.type === "attributes" && e.matches("[data-t-attrs]") && n.add(e), r.addedNodes.forEach((e) => {
68
88
  e.nodeType === Node.ELEMENT_NODE && this.collect(e, t, n);
69
89
  }));
70
90
  }
71
91
  e.some((e) => e.removedNodes.length > 0) && this.forget((e) => !e.isConnected), t.forEach((e) => this.translateContent(e)), n.forEach((e) => this.translateAttributes(e));
72
92
  }
73
93
  forget(e) {
74
- for (let t of [...this.contents.keys()]) e(t) && this.contents.delete(t);
75
- for (let t of [...this.attributes.keys()]) e(t) && this.attributes.delete(t);
94
+ for (let t of this.contents.keys()) e(t) && this.contents.delete(t);
95
+ for (let t of this.attributes.keys()) e(t) && this.attributes.delete(t);
76
96
  }
77
97
  translateContent(e, t = !1) {
78
- let n = e.innerHTML, r = this.contents.get(e);
79
- if (!r) r = {
98
+ let n = e.innerHTML, r = e.getAttribute("data-v"), i = this.contents.get(e);
99
+ if (!i) i = {
80
100
  source: n,
81
- requestId: 0
82
- }, this.contents.set(e, r);
83
- else if (n !== r.rendered && n !== r.source) r.source = n, r.rendered = void 0;
101
+ requestId: 0,
102
+ variables: r
103
+ }, this.contents.set(e, i);
104
+ else if (n !== i.rendered && n !== i.source) i.source = n, i.rendered = void 0, i.variables = r;
105
+ else if (r !== i.variables) i.variables = r;
84
106
  else if (!t) return;
85
- let [, i, a, o] = /^(\s*)([\s\S]*?)(\s*)$/.exec(r.source.replace(/<!--[\s\S]*?-->/g, ""));
86
- if (!a) return;
87
- let s = ++r.requestId, l = r;
88
- this.translate(a, c(e)).then((t) => {
89
- if (s !== l.requestId || !e.isConnected) return;
90
- let n = `${i}${t}${o}`;
91
- e.innerHTML !== n && (e.innerHTML = n), l.rendered = e.innerHTML;
107
+ let [, a, o, c] = /^(\s*)([\s\S]*?)(\s*)$/.exec(i.source.replace(/<!--[\s\S]*?-->/g, ""));
108
+ if (!o) return;
109
+ let l = h(e);
110
+ if (l === null) return;
111
+ let { template: d, values: f, unused: p } = l ? s(o, l) : {
112
+ template: o,
113
+ values: [],
114
+ unused: []
115
+ };
116
+ d !== i.template && (i.template = d, u(p));
117
+ let _ = i, v = (t) => {
118
+ let n = `${a}${g(t, f)}${c}`;
119
+ e.innerHTML !== n && (e.innerHTML = n), _.rendered = e.innerHTML;
120
+ };
121
+ l && _.rendered === void 0 && v(d);
122
+ let y = ++_.requestId;
123
+ this.translate(d, m(e)).then((t) => {
124
+ y === _.requestId && e.isConnected && v(t);
92
125
  });
93
126
  }
94
127
  translateAttributes(e, t = !1) {
95
- let n = (e.getAttribute("data-t-attrs") ?? "").split(/[\s,]+/).filter(Boolean), r = this.attributes.get(e);
96
- r || (r = /* @__PURE__ */ new Map(), this.attributes.set(e, r));
97
- for (let i of n) {
98
- let n = e.getAttribute(i);
128
+ let n = (e.getAttribute("data-t-attrs") ?? "").split(/[\s,]+/).filter(Boolean), r = e.getAttribute("data-v"), a = this.attributes.get(e);
129
+ a || (a = /* @__PURE__ */ new Map(), this.attributes.set(e, a));
130
+ for (let o of n) {
131
+ let n = e.getAttribute(o);
99
132
  if (!n?.trim()) continue;
100
- let a = r.get(i);
101
- if (!a) a = {
133
+ let s = a.get(o);
134
+ if (!s) s = {
102
135
  source: n,
103
- requestId: 0
104
- }, r.set(i, a);
105
- else if (n !== a.rendered && n !== a.source) a.source = n, a.rendered = void 0;
136
+ requestId: 0,
137
+ variables: r
138
+ }, a.set(o, s);
139
+ else if (n !== s.rendered && n !== s.source) s.source = n, s.rendered = void 0, s.variables = r;
140
+ else if (r !== s.variables) s.variables = r;
106
141
  else if (!t) continue;
107
- let o = ++a.requestId, s = a;
108
- this.translate(s.source, c(e)).then((t) => {
109
- o === s.requestId && e.isConnected && (s.rendered = t, e.getAttribute(i) !== t && e.setAttribute(i, t));
142
+ let l = h(e);
143
+ if (l === null) continue;
144
+ let { template: d, values: f, unused: p } = l ? c(s.source, l) : {
145
+ template: s.source,
146
+ values: [],
147
+ unused: []
148
+ };
149
+ d !== s.template && (s.template = d, u(p));
150
+ let g = s, _ = (t) => {
151
+ let n = i(t, f);
152
+ g.rendered = n, e.getAttribute(o) !== n && e.setAttribute(o, n);
153
+ };
154
+ l && g.rendered === void 0 && _(d);
155
+ let v = ++g.requestId;
156
+ this.translate(d, m(e)).then((t) => {
157
+ v === g.requestId && e.isConnected && _(t);
110
158
  });
111
159
  }
112
160
  }
113
161
  };
114
- function u(e) {
115
- return new l(e);
162
+ function v(e) {
163
+ return new _(e);
116
164
  }
117
- export { o as ATTRIBUTES_SELECTOR, a as CONTENT_SELECTOR, l as MagicTranslate, u as createMagicTranslate };
165
+ export { f as ATTRIBUTES_SELECTOR, d as CONTENT_SELECTOR, _ as MagicTranslate, v as createMagicTranslate };
118
166
 
119
167
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","MagicTranslate","libraryWebsite","utsLocaleToLanguage","language","listener","params","loaderFactory","defaultLoaderFactory","DataLoader","debug","sourceLanguage","_apiUrl","cacheKeyFn","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","html","translation","force","leadingWhitespace","text","trailingWhitespace","requestId","rendered","translated","names","translations","name","value","createMagicTranslate"],"sources":["../src/magic-translate.ts"],"sourcesContent":["import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n Language,\n libraryWebsite,\n MagicTranslateConfig,\n TranslateOptions,\n utsLocaleToLanguage,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport type TranslateParams = Omit<DataLoaderLoadParams, 'text'>;\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(text: string, params: TranslateParams = {}): Promise<string> {\n return this.dataLoader.load({ text, ...params });\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of [...this.contents.keys()]) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of [...this.attributes.keys()]) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n let translation = this.contents.get(element);\n\n if (!translation) {\n translation = { source: html, requestId: 0 };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(text, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n const rendered = `${leadingWhitespace}${translated}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0 };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n } else if (!force) {\n continue;\n }\n\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(current.source, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n current.rendered = translated;\n if (element.getAttribute(name) !== translated) {\n element.setAttribute(name, translated);\n }\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","variablesOf","json","variables","libraryName","fillHtml","values","html","container","fillElement","MagicTranslate","libraryWebsite","utsLocaleToLanguage","language","listener","params","v","toTemplate","text","warnAboutUnused","unused","template","fillPlaceholders","translation","loaderFactory","defaultLoaderFactory","DataLoader","debug","sourceLanguage","_apiUrl","cacheKeyFn","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","variablesJson","force","leadingWhitespace","trailingWhitespace","toHtmlTemplate","show","rendered","translated","requestId","names","translations","name","value","createMagicTranslate"],"sources":["../src/magic-translate.ts"],"sourcesContent":["import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n fillElement,\n fillPlaceholders,\n Language,\n libraryName,\n libraryWebsite,\n MagicTranslateConfig,\n toHtmlTemplate,\n toTemplate,\n TranslateOptions,\n utsLocaleToLanguage,\n Variables,\n warnAboutUnused,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport interface TranslateParams extends Omit<DataLoaderLoadParams, 'text'> {\n /**\n * Values that are not translated, referenced as `$name` in the text. The text\n * is translated once as a template and cached, whatever the values are.\n */\n v?: Variables;\n}\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n // the `data-v` attribute the source was last translated with\n variables: string | null;\n // to warn about unused variables once per text, not on every render\n template?: string;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\n/**\n * Reads the values in `data-v`, a JSON object such as `{\"name\": \"Bob\"}`.\n * Returns `null` if the attribute is not valid, so the text is left as it is\n * rather than translated with `$name` in it.\n */\nfunction variablesOf(element: Element): Variables | undefined | null {\n const json = element.getAttribute('data-v');\n if (json === null) {\n return undefined;\n }\n try {\n const variables: unknown = JSON.parse(json);\n if (\n variables &&\n typeof variables === 'object' &&\n !Array.isArray(variables)\n ) {\n return variables as Variables;\n }\n } catch {\n // reported below\n }\n console.warn(\n `[${libraryName}]: data-v must be a JSON object, such as data-v='{\"name\": \"Bob\"}', but is ${json}`\n );\n return null;\n}\n\nfunction fillHtml(html: string, values: readonly string[]): string {\n if (values.length === 0) {\n return html;\n }\n const container = document.createElement('template');\n container.innerHTML = html;\n fillElement(container.content, values);\n return container.innerHTML;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(\n text: string,\n { v, ...params }: TranslateParams = {}\n ): Promise<string> {\n if (!v) {\n return this.dataLoader.load({ text, ...params });\n }\n const { template, values, unused } = toTemplate(text, v);\n warnAboutUnused(unused);\n return this.dataLoader\n .load({ text: template, ...params })\n .then((translation) => fillPlaceholders(translation, values));\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of this.contents.keys()) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of this.attributes.keys()) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n const variablesJson = element.getAttribute('data-v');\n let translation = this.contents.get(element);\n if (!translation) {\n translation = { source: html, requestId: 0, variables: variablesJson };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n // only the values changed, so the cached translation is reused\n translation.variables = variablesJson;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n return;\n }\n const { template, values, unused } = variables\n ? toHtmlTemplate(text, variables)\n : { template: text, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = `${leadingWhitespace}${fillHtml(translated, values)}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n };\n // Until the first translation arrives, show the values instead of `$name`.\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n const variablesJson = element.getAttribute('data-v');\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0, variables: variablesJson };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n translation.variables = variablesJson;\n } else if (!force) {\n continue;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n continue;\n }\n const { template, values, unused } = variables\n ? toTemplate(translation.source, variables)\n : { template: translation.source, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = fillPlaceholders(translated, values);\n current.rendered = rendered;\n if (element.getAttribute(name) !== rendered) {\n element.setAttribute(name, rendered);\n }\n };\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n"],"mappings":"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"}
@@ -1,10 +1,16 @@
1
- import { DataLoaderLoadParams, Language, MagicTranslateConfig } from '@magic-translate/core';
1
+ import { DataLoaderLoadParams, Language, MagicTranslateConfig, Variables } from '@magic-translate/core';
2
2
  /**
3
3
  * Type alias to make the language enum more flexible to also allow passing language strings
4
4
  * instead of just the enum.
5
5
  */
6
6
  export type Languages = `${Language}`;
7
- export type TranslateParams = Omit<DataLoaderLoadParams, 'text'>;
7
+ export interface TranslateParams extends Omit<DataLoaderLoadParams, 'text'> {
8
+ /**
9
+ * Values that are not translated, referenced as `$name` in the text. The text
10
+ * is translated once as a template and cached, whatever the values are.
11
+ */
12
+ v?: Variables;
13
+ }
8
14
  export interface MagicTranslateOptions extends Omit<MagicTranslateConfig, 'language'> {
9
15
  /**
10
16
  * The language content is translated to. Defaults to the browser's language.
@@ -45,7 +51,7 @@ export declare class MagicTranslate {
45
51
  /**
46
52
  * Translates a string to the current language.
47
53
  */
48
- translate(text: string, params?: TranslateParams): Promise<string>;
54
+ translate(text: string, { v, ...params }?: TranslateParams): Promise<string>;
49
55
  /**
50
56
  * Stops translating the page. Content that is already translated stays
51
57
  * translated.
@@ -4,5 +4,5 @@ Keys:
4
4
  `+String(t.keys)+`
5
5
 
6
6
  Values:
7
- `+String(e));i(t);for(var n=0;n<t.callbacks.length;n++){var r=e[n];r instanceof Error?t.callbacks[n].reject(r):t.callbacks[n].resolve(r)}}).catch(function(n){r(e,t,n)})})(e,n)}),n}(this),o=this._cacheMap;if(o){t=this._cacheKeyFn(e);var s=o.get(t);if(s){var c=n.cacheHits||=[];return new Promise(function(e){c.push(function(){e(s)})})}}n.keys.push(e);var l=new Promise(function(e,t){n.callbacks.push({resolve:e,reject:t})});return o&&o.set(t,l),l},t.loadMany=function(e){if(!a(e))throw TypeError(`The loader.loadMany() function must be called with Array<key> but got: `+e+`.`);for(var t=[],n=0;n<e.length;n++)t.push(this.load(e[n]).catch(function(e){return e}));return Promise.all(t)},t.clear=function(e){var t=this._cacheMap;if(t){var n=this._cacheKeyFn(e);t.delete(n)}return this},t.clearAll=function(){var e=this._cacheMap;return e&&e.clear(),this},t.prime=function(e,t){var n=this._cacheMap;if(n){var r,i=this._cacheKeyFn(e);n.get(i)===void 0&&(t instanceof Error?(r=Promise.reject(t)).catch(function(){}):r=Promise.resolve(t),n.set(i,r))}return this},e}(),n=typeof process==`object`&&typeof process.nextTick==`function`?function(t){e||=Promise.resolve(),e.then(function(){process.nextTick(t)})}:typeof setImmediate==`function`?function(e){setImmediate(e)}:function(e){setTimeout(e)};function r(e,t,n){i(t);for(var r=0;r<t.keys.length;r++)e.clear(t.keys[r]),t.callbacks[r].reject(n)}function i(e){if(e.cacheHits)for(var t=0;t<e.cacheHits.length;t++)e.cacheHits[t]()}function a(e){return typeof e==`object`&&!!e&&typeof e.length==`number`&&(e.length===0||e.length>0&&Object.prototype.hasOwnProperty.call(e,e.length-1))}return d=t}());function m({debug:e,apiKey:t,sourceLanguage:n,_apiUrl:i,formality:a,_loaderFactory:o}){let s={debug:!!e,apiKey:t,lang:r.EN,sourceLang:n,apiUrl:i,formality:a},u=new p((o??l)(s),{cacheKeyFn:c(t,n)});return(e,t,n)=>u.load({text:t,...n,lang:e})}var h=m,g=r.EN;function _(e,t){if(!e)return t??g;if([r.FA_AF,r.FR_CA,r.PT_PT,r.ES_MX].includes(e))return e;let n=e.includes(`-`)?e.split(`-`)[0]:e;return Object.values(r).includes(n)?n:t||(console.warn(`Language not found for UTS locale "${e}" and no fallback provided was provided. Falling back to "${g}"`),g)}var v=`t, mt-translate, [data-t]`,y=`[data-t-attrs]`;function b(e){let t=e;for(let e=t.parentElement?.closest(v);e;e=t.parentElement?.closest(v))t=e;return t}function x(e){let t={},n=e.getAttribute(`data-lang`),r=e.getAttribute(`data-formality`);return n&&(t.lang=n),r&&(t.formality=r),t}var S=class{constructor(e){if(this.options=e,this.contents=new Map,this.attributes=new Map,this.listeners=new Set,!e.apiKey)throw Error(`Missing API key. Please get one at ${n}`);this.currentLanguage=e.language??_(typeof navigator>`u`?void 0:navigator.language),this.dataLoader=this.createDataLoader(),typeof document<`u`&&(this.root=e.root??document.documentElement,this.start(this.root))}get language(){return this.currentLanguage}setLanguage(e){e!==this.currentLanguage&&(this.currentLanguage=e,this.dataLoader=this.createDataLoader(),this.forget(e=>!e.isConnected||!this.root?.contains(e)),this.contents.forEach((e,t)=>this.translateContent(t,!0)),this.attributes.forEach((e,t)=>this.translateAttributes(t,!0)),this.listeners.forEach(t=>t(e)))}onLanguageChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}translate(e,t={}){return this.dataLoader.load({text:e,...t})}destroy(){this.observer?.disconnect(),this.contents.clear(),this.attributes.clear(),this.listeners.clear()}createDataLoader(){let{debug:e,apiKey:t,sourceLanguage:n,_apiUrl:r,formality:i}=this.options;return new p((this.options._loaderFactory??l)({debug:!!e,apiKey:t,lang:this.currentLanguage,sourceLang:n,apiUrl:r,formality:i}),{cacheKeyFn:c(t,n,this.currentLanguage)})}start(e){let t=new Set,n=new Set;this.collect(e,t,n),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e)),this.observer=new MutationObserver(e=>this.handleMutations(e)),this.observer.observe(e,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}collect(e,t,n){let r=[e,...Array.from(e.querySelectorAll(`*`))];for(let e of r)e.matches(`t, mt-translate, [data-t]`)?t.add(b(e)):e.matches(`[data-t-attrs]`)&&!e.closest(`t, mt-translate, [data-t]`)&&n.add(e)}handleMutations(e){let t=new Set,n=new Set;for(let r of e){let e=r.target.nodeType===Node.ELEMENT_NODE?r.target:r.target.parentElement;if(!e)continue;let i=e.closest(v);i?t.add(b(i)):(r.type===`attributes`&&e.matches(`[data-t-attrs]`)&&n.add(e),r.addedNodes.forEach(e=>{e.nodeType===Node.ELEMENT_NODE&&this.collect(e,t,n)}))}e.some(e=>e.removedNodes.length>0)&&this.forget(e=>!e.isConnected),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e))}forget(e){for(let t of[...this.contents.keys()])e(t)&&this.contents.delete(t);for(let t of[...this.attributes.keys()])e(t)&&this.attributes.delete(t)}translateContent(e,t=!1){let n=e.innerHTML,r=this.contents.get(e);if(!r)r={source:n,requestId:0},this.contents.set(e,r);else if(n!==r.rendered&&n!==r.source)r.source=n,r.rendered=void 0;else if(!t)return;let[,i,a,o]=/^(\s*)([\s\S]*?)(\s*)$/.exec(r.source.replace(/<!--[\s\S]*?-->/g,``));if(!a)return;let s=++r.requestId,c=r;this.translate(a,x(e)).then(t=>{if(s!==c.requestId||!e.isConnected)return;let n=`${i}${t}${o}`;e.innerHTML!==n&&(e.innerHTML=n),c.rendered=e.innerHTML})}translateAttributes(e,t=!1){let n=(e.getAttribute(`data-t-attrs`)??``).split(/[\s,]+/).filter(Boolean),r=this.attributes.get(e);r||(r=new Map,this.attributes.set(e,r));for(let i of n){let n=e.getAttribute(i);if(!n?.trim())continue;let a=r.get(i);if(!a)a={source:n,requestId:0},r.set(i,a);else if(n!==a.rendered&&n!==a.source)a.source=n,a.rendered=void 0;else if(!t)continue;let o=++a.requestId,s=a;this.translate(s.source,x(e)).then(t=>{o===s.requestId&&e.isConnected&&(s.rendered=t,e.getAttribute(i)!==t&&e.setAttribute(i,t))})}}};function C(e){return new S(e)}var w=`magic-translate-language`;function T(){try{return localStorage.getItem(w)??void 0}catch{return}}var E=document.currentScript,D=E?.dataset.apiKey;if(E&&D){let{language:e,sourceLanguage:t,formality:n}=E.dataset,r=()=>{let r=C({apiKey:D,language:T()??e,sourceLanguage:t,formality:n,debug:E.hasAttribute(`data-debug`)});r.onLanguageChange(e=>{try{localStorage.setItem(w,e)}catch{}}),window.magicTranslate=r,document.dispatchEvent(new CustomEvent(`magictranslate:ready`,{detail:r}))};document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,r):r()}return e.ATTRIBUTES_SELECTOR=y,e.CONTENT_SELECTOR=v,e.DataLoader=p,Object.defineProperty(e,"Language",{enumerable:!0,get:function(){return r}}),e.MagicTranslate=S,e.cacheKeyFn=c,e.createMagicTranslate=C,e.defaultDataLoaderOptions=o,e.defaultLoaderFactory=l,e.libraryName=t,e.libraryWebsite=n,e.setupT=h,e.setupTranslate=m,e.utsLocaleToLanguage=_,e})({});
7
+ `+String(e));i(t);for(var n=0;n<t.callbacks.length;n++){var r=e[n];r instanceof Error?t.callbacks[n].reject(r):t.callbacks[n].resolve(r)}}).catch(function(n){r(e,t,n)})})(e,n)}),n}(this),o=this._cacheMap;if(o){t=this._cacheKeyFn(e);var s=o.get(t);if(s){var c=n.cacheHits||=[];return new Promise(function(e){c.push(function(){e(s)})})}}n.keys.push(e);var l=new Promise(function(e,t){n.callbacks.push({resolve:e,reject:t})});return o&&o.set(t,l),l},t.loadMany=function(e){if(!a(e))throw TypeError(`The loader.loadMany() function must be called with Array<key> but got: `+e+`.`);for(var t=[],n=0;n<e.length;n++)t.push(this.load(e[n]).catch(function(e){return e}));return Promise.all(t)},t.clear=function(e){var t=this._cacheMap;if(t){var n=this._cacheKeyFn(e);t.delete(n)}return this},t.clearAll=function(){var e=this._cacheMap;return e&&e.clear(),this},t.prime=function(e,t){var n=this._cacheMap;if(n){var r,i=this._cacheKeyFn(e);n.get(i)===void 0&&(t instanceof Error?(r=Promise.reject(t)).catch(function(){}):r=Promise.resolve(t),n.set(i,r))}return this},e}(),n=typeof process==`object`&&typeof process.nextTick==`function`?function(t){e||=Promise.resolve(),e.then(function(){process.nextTick(t)})}:typeof setImmediate==`function`?function(e){setImmediate(e)}:function(e){setTimeout(e)};function r(e,t,n){i(t);for(var r=0;r<t.keys.length;r++)e.clear(t.keys[r]),t.callbacks[r].reject(n)}function i(e){if(e.cacheHits)for(var t=0;t<e.cacheHits.length;t++)e.cacheHits[t]()}function a(e){return typeof e==`object`&&!!e&&typeof e.length==`number`&&(e.length===0||e.length>0&&Object.prototype.hasOwnProperty.call(e,e.length-1))}return d=t}());function m({debug:e,apiKey:t,sourceLanguage:n,_apiUrl:i,formality:a,_loaderFactory:o}){let s={debug:!!e,apiKey:t,lang:r.EN,sourceLang:n,apiUrl:i,formality:a},u=new p((o??l)(s),{cacheKeyFn:c(t,n)});return(e,t,n)=>u.load({text:t,...n,lang:e})}var h=m,g=r.EN;function _(e,t){if(!e)return t??g;if([r.FA_AF,r.FR_CA,r.PT_PT,r.ES_MX].includes(e))return e;let n=e.includes(`-`)?e.split(`-`)[0]:e;return Object.values(r).includes(n)?n:t||(console.warn(`Language not found for UTS locale "${e}" and no fallback provided was provided. Falling back to "${g}"`),g)}var v=/\{(\d+)\}/g,y=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);function b(e){let t=Object.keys(e).sort((e,t)=>t.length-e.length),n=t.length>0?RegExp(`\\$(${t.map(y).join(`|`)})`,`g`):null,r=new Map,i=[];return{values:i,replace:t=>n?t.replace(n,(t,n)=>{let a=r.get(n);return a===void 0&&(a=i.push(String(e[n]??``))-1,r.set(n,a)),`{${a}}`}):t,unused:()=>t.filter(e=>!r.has(e))}}function x(e){e.length>0&&console.warn(`[Magic Translate]: ${e.map(e=>`$${e}`).join(`, `)} not found in the text. Check the spelling, or remove ${e.length===1?`it`:`them`} from the variables.`)}function S(e,t){let{replace:n,values:r,unused:i}=b(t);return{template:n(e),values:r,unused:i()}}function C(e,t){return e.replace(v,(e,n)=>Number(n)<t.length?t[Number(n)]:e)}function w(e){let t=document.createTreeWalker(e,NodeFilter.SHOW_TEXT),n=[];for(let e=t.nextNode();e;e=t.nextNode())n.push(e);return n}function T(e,t){let n=document.createElement(`template`);n.innerHTML=e;let{replace:r,values:i,unused:a}=b(t);return w(n.content).forEach(e=>{e.data=r(e.data)}),{template:n.innerHTML,values:i,unused:a()}}function E(e,t){w(e).forEach(e=>{let n=C(e.data,t);n!==e.data&&(e.data=n)})}var D=`t, mt-translate, [data-t]`,O=`[data-t-attrs]`;function k(e){let t=e;for(let e=t.parentElement?.closest(D);e;e=t.parentElement?.closest(D))t=e;return t}function A(e){let t={},n=e.getAttribute(`data-lang`),r=e.getAttribute(`data-formality`);return n&&(t.lang=n),r&&(t.formality=r),t}function j(e){let n=e.getAttribute(`data-v`);if(n!==null){try{let e=JSON.parse(n);if(e&&typeof e==`object`&&!Array.isArray(e))return e}catch{}return console.warn(`[${t}]: data-v must be a JSON object, such as data-v='{"name": "Bob"}', but is ${n}`),null}}function M(e,t){if(t.length===0)return e;let n=document.createElement(`template`);return n.innerHTML=e,E(n.content,t),n.innerHTML}var N=class{constructor(e){if(this.options=e,this.contents=new Map,this.attributes=new Map,this.listeners=new Set,!e.apiKey)throw Error(`Missing API key. Please get one at ${n}`);this.currentLanguage=e.language??_(typeof navigator>`u`?void 0:navigator.language),this.dataLoader=this.createDataLoader(),typeof document<`u`&&(this.root=e.root??document.documentElement,this.start(this.root))}get language(){return this.currentLanguage}setLanguage(e){e!==this.currentLanguage&&(this.currentLanguage=e,this.dataLoader=this.createDataLoader(),this.forget(e=>!e.isConnected||!this.root?.contains(e)),this.contents.forEach((e,t)=>this.translateContent(t,!0)),this.attributes.forEach((e,t)=>this.translateAttributes(t,!0)),this.listeners.forEach(t=>t(e)))}onLanguageChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}translate(e,{v:t,...n}={}){if(!t)return this.dataLoader.load({text:e,...n});let{template:r,values:i,unused:a}=S(e,t);return x(a),this.dataLoader.load({text:r,...n}).then(e=>C(e,i))}destroy(){this.observer?.disconnect(),this.contents.clear(),this.attributes.clear(),this.listeners.clear()}createDataLoader(){let{debug:e,apiKey:t,sourceLanguage:n,_apiUrl:r,formality:i}=this.options;return new p((this.options._loaderFactory??l)({debug:!!e,apiKey:t,lang:this.currentLanguage,sourceLang:n,apiUrl:r,formality:i}),{cacheKeyFn:c(t,n,this.currentLanguage)})}start(e){let t=new Set,n=new Set;this.collect(e,t,n),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e)),this.observer=new MutationObserver(e=>this.handleMutations(e)),this.observer.observe(e,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}collect(e,t,n){let r=[e,...Array.from(e.querySelectorAll(`*`))];for(let e of r)e.matches(`t, mt-translate, [data-t]`)?t.add(k(e)):e.matches(`[data-t-attrs]`)&&!e.closest(`t, mt-translate, [data-t]`)&&n.add(e)}handleMutations(e){let t=new Set,n=new Set;for(let r of e){let e=r.target.nodeType===Node.ELEMENT_NODE?r.target:r.target.parentElement;if(!e)continue;let i=e.closest(D);i?t.add(k(i)):(r.type===`attributes`&&e.matches(`[data-t-attrs]`)&&n.add(e),r.addedNodes.forEach(e=>{e.nodeType===Node.ELEMENT_NODE&&this.collect(e,t,n)}))}e.some(e=>e.removedNodes.length>0)&&this.forget(e=>!e.isConnected),t.forEach(e=>this.translateContent(e)),n.forEach(e=>this.translateAttributes(e))}forget(e){for(let t of this.contents.keys())e(t)&&this.contents.delete(t);for(let t of this.attributes.keys())e(t)&&this.attributes.delete(t)}translateContent(e,t=!1){let n=e.innerHTML,r=e.getAttribute(`data-v`),i=this.contents.get(e);if(!i)i={source:n,requestId:0,variables:r},this.contents.set(e,i);else if(n!==i.rendered&&n!==i.source)i.source=n,i.rendered=void 0,i.variables=r;else if(r!==i.variables)i.variables=r;else if(!t)return;let[,a,o,s]=/^(\s*)([\s\S]*?)(\s*)$/.exec(i.source.replace(/<!--[\s\S]*?-->/g,``));if(!o)return;let c=j(e);if(c===null)return;let{template:l,values:u,unused:d}=c?T(o,c):{template:o,values:[],unused:[]};l!==i.template&&(i.template=l,x(d));let f=i,p=t=>{let n=`${a}${M(t,u)}${s}`;e.innerHTML!==n&&(e.innerHTML=n),f.rendered=e.innerHTML};c&&f.rendered===void 0&&p(l);let m=++f.requestId;this.translate(l,A(e)).then(t=>{m===f.requestId&&e.isConnected&&p(t)})}translateAttributes(e,t=!1){let n=(e.getAttribute(`data-t-attrs`)??``).split(/[\s,]+/).filter(Boolean),r=e.getAttribute(`data-v`),i=this.attributes.get(e);i||(i=new Map,this.attributes.set(e,i));for(let a of n){let n=e.getAttribute(a);if(!n?.trim())continue;let o=i.get(a);if(!o)o={source:n,requestId:0,variables:r},i.set(a,o);else if(n!==o.rendered&&n!==o.source)o.source=n,o.rendered=void 0,o.variables=r;else if(r!==o.variables)o.variables=r;else if(!t)continue;let s=j(e);if(s===null)continue;let{template:c,values:l,unused:u}=s?S(o.source,s):{template:o.source,values:[],unused:[]};c!==o.template&&(o.template=c,x(u));let d=o,f=t=>{let n=C(t,l);d.rendered=n,e.getAttribute(a)!==n&&e.setAttribute(a,n)};s&&d.rendered===void 0&&f(c);let p=++d.requestId;this.translate(c,A(e)).then(t=>{p===d.requestId&&e.isConnected&&f(t)})}}};function P(e){return new N(e)}var F=`magic-translate-language`;function I(){try{return localStorage.getItem(F)??void 0}catch{return}}var L=document.currentScript,R=L?.dataset.apiKey;if(L&&R){let{language:e,sourceLanguage:t,formality:n}=L.dataset,r=()=>{let r=P({apiKey:R,language:I()??e,sourceLanguage:t,formality:n,debug:L.hasAttribute(`data-debug`)});r.onLanguageChange(e=>{try{localStorage.setItem(F,e)}catch{}}),window.magicTranslate=r,document.dispatchEvent(new CustomEvent(`magictranslate:ready`,{detail:r}))};document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,r):r()}return e.ATTRIBUTES_SELECTOR=O,e.CONTENT_SELECTOR=D,e.DataLoader=p,Object.defineProperty(e,"Language",{enumerable:!0,get:function(){return r}}),e.MagicTranslate=N,e.cacheKeyFn=c,e.createMagicTranslate=P,e.defaultDataLoaderOptions=o,e.defaultLoaderFactory=l,e.fillElement=E,e.fillPlaceholders=C,e.libraryName=t,e.libraryWebsite=n,e.setupT=h,e.setupTranslate=m,e.templater=b,e.toHtmlTemplate=T,e.toTemplate=S,e.utsLocaleToLanguage=_,e.warnAboutUnused=x,e})({});
8
8
  //# sourceMappingURL=magic-translate.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"magic-translate.min.js","names":["libraryWebsite","utsLocaleToLanguage","defaultLoaderFactory","DataLoader","cacheKeyFn","exports","e","t","n","r","c","i","a","u","s","l","h","o","y","d","m","g","CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","MagicTranslate","language","listener","params","debug","sourceLanguage","_apiUrl","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","html","translation","force","leadingWhitespace","text","trailingWhitespace","requestId","rendered","translated","names","translations","name","value","createMagicTranslate","STORAGE_KEY","storedLanguage","script","start","magicTranslate"],"sources":["../../core/dist/index.es.js","../src/magic-translate.ts","../src/script.ts"],"sourcesContent":["/*\n * 'Magic Translate Core'\n * {@link https://https://magictranslate.io\n * @copyright i42 GmbH\n */\nconst e=\"Magic Translate\",t=\"https://magictranslate.io\";var r;function n(e){return Object.keys(e).filter(t=>void 0!==e[t]).reduce((t,r)=>({...t,[r]:e[r]}),{})}function a(e){const t=new URLSearchParams,r=(e,n=\"\")=>{Array.isArray(e)?e.forEach((e,t)=>{r(e,n?`${n}[${t}]`:`[${t}]`)}):\"object\"==typeof e&&null!==e?Object.entries(e).forEach(([e,t])=>{r(t,n?`${n}.${e}`:e)}):t.append(n,e)};return r(e),t}!function(e){e.AF=\"af\",e.SQ=\"sq\",e.AM=\"am\",e.AR=\"ar\",e.HY=\"hy\",e.AZ=\"az\",e.BN=\"bn\",e.BS=\"bs\",e.BG=\"bg\",e.CA=\"ca\",e.ZH=\"zh\",e.ZH_TW=\"zh-TW\",e.HR=\"hr\",e.CS=\"cs\",e.DA=\"da\",e.FA_AF=\"fa-AF\",e.NL=\"nl\",e.EN=\"en\",e.ET=\"et\",e.FA=\"fa\",e.TL=\"tl\",e.FI=\"fi\",e.FR=\"fr\",e.FR_CA=\"fr-CA\",e.KA=\"ka\",e.DE=\"de\",e.EL=\"el\",e.GU=\"gu\",e.HT=\"ht\",e.HA=\"ha\",e.HE=\"he\",e.HI=\"hi\",e.HU=\"hu\",e.IS=\"is\",e.ID=\"id\",e.GA=\"ga\",e.IT=\"it\",e.JA=\"ja\",e.KN=\"kn\",e.KK=\"kk\",e.KO=\"ko\",e.LV=\"lv\",e.LT=\"lt\",e.MK=\"mk\",e.MS=\"ms\",e.ML=\"ml\",e.MT=\"mt\",e.MR=\"mr\",e.MN=\"mn\",e.NO=\"no\",e.PS=\"ps\",e.PL=\"pl\",e.PT=\"pt\",e.PT_PT=\"pt-PT\",e.PA=\"pa\",e.RO=\"ro\",e.RU=\"ru\",e.SR=\"sr\",e.SI=\"si\",e.SK=\"sk\",e.SL=\"sl\",e.SO=\"so\",e.ES=\"es\",e.ES_MX=\"es-MX\",e.SW=\"sw\",e.SV=\"sv\",e.TA=\"ta\",e.TE=\"te\",e.TH=\"th\",e.TR=\"tr\",e.UK=\"uk\",e.UR=\"ur\",e.UZ=\"uz\",e.VI=\"vi\",e.CY=\"cy\"}(r||(r={}));const o={debug:!1,apiUrl:\"https://api.magictranslate.io\",lang:r.DE};var c;!function(e){e.QUOTA_EXCEEDED_ERROR=\"QUOTA_EXCEEDED_ERROR\"}(c||(c={}));const i=(e,t,r)=>a=>Object.entries(n({apiKey:e,language:r,sourceLanguage:a.sourceLang??t??\"auto\",...a})).sort(([e])=>e.localeCompare(e)).reduce((e,t)=>`${e};${t.join(\"=\")}`,\"\"),u=t=>async r=>{if(t.debug&&console.debug(`[${e}]: batch function called with: `,r),t.lang===t.sourceLang)return r.map(e=>e.text);const{apiKey:c,lang:i,formality:u}=t,s=n({apiKey:c,lang:i,formality:u,items:r.map(({text:e,sourceLang:t,lang:r,...a})=>n({text:e,lang:r,sourceLang:t,...a}))});return fetch(`${t.apiUrl??o.apiUrl}/v1/translate`,{method:\"POST\",body:t.useJson?JSON.stringify(s):a(s),headers:{\"Content-Type\":t.useJson?\"application/json\":\"application/x-www-form-urlencoded\"}}).then(e=>e.json()).then(t=>\"results\"in t?t.results:(\"code\"in t?console.error(`[${e}]: ${t.message}`):console.error(`[${e}]: Unexpected payload from magic translate server: ${JSON.stringify(t)}`),r.map(e=>e.text))).catch(t=>(console.error(`[${e}]: Error while loading translations: `,t),r.map(e=>e.text)))};function s(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,\"default\")?e.default:e}var l,h;var f=function(){if(h)return l;h=1;var e,t=function(){function e(e,t){if(\"function\"!=typeof e)throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but got: \"+e+\".\");this._batchLoadFn=e,this._maxBatchSize=function(e){var t=!e||!1!==e.batch;if(!t)return 1;var r=e&&e.maxBatchSize;if(void 0===r)return 1/0;if(\"number\"!=typeof r||r<1)throw new TypeError(\"maxBatchSize must be a positive number: \"+r);return r}(t),this._batchScheduleFn=function(e){var t=e&&e.batchScheduleFn;if(void 0===t)return r;if(\"function\"!=typeof t)throw new TypeError(\"batchScheduleFn must be a function: \"+t);return t}(t),this._cacheKeyFn=function(e){var t=e&&e.cacheKeyFn;if(void 0===t)return function(e){return e};if(\"function\"!=typeof t)throw new TypeError(\"cacheKeyFn must be a function: \"+t);return t}(t),this._cacheMap=function(e){var t=!e||!1!==e.cache;if(!t)return null;var r=e&&e.cacheMap;if(void 0===r)return new Map;if(null!==r){var n=[\"get\",\"set\",\"delete\",\"clear\"].filter(function(e){return r&&\"function\"!=typeof r[e]});if(0!==n.length)throw new TypeError(\"Custom cacheMap missing methods: \"+n.join(\", \"))}return r}(t),this._batch=null,this.name=function(e){if(e&&e.name)return e.name;return null}(t)}var t=e.prototype;return t.load=function(e){if(null==e)throw new TypeError(\"The loader.load() function must be called with a value, but got: \"+String(e)+\".\");var t,r=function(e){var t=e._batch;if(null!==t&&!t.hasDispatched&&t.keys.length<e._maxBatchSize)return t;var r={hasDispatched:!1,keys:[],callbacks:[]};return e._batch=r,e._batchScheduleFn(function(){!function(e,t){if(t.hasDispatched=!0,0===t.keys.length)return void a(t);var r;try{r=e._batchLoadFn(t.keys)}catch(r){return n(e,t,new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function errored synchronously: \"+String(r)+\".\"))}if(!r||\"function\"!=typeof r.then)return n(e,t,new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise: \"+String(r)+\".\"));r.then(function(e){if(!o(e))throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise of an Array: \"+String(e)+\".\");if(e.length!==t.keys.length)throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise of an Array of the same length as the Array of keys.\\n\\nKeys:\\n\"+String(t.keys)+\"\\n\\nValues:\\n\"+String(e));a(t);for(var r=0;r<t.callbacks.length;r++){var n=e[r];n instanceof Error?t.callbacks[r].reject(n):t.callbacks[r].resolve(n)}}).catch(function(r){n(e,t,r)})}(e,r)}),r}(this),c=this._cacheMap;if(c){t=this._cacheKeyFn(e);var i=c.get(t);if(i){var u=r.cacheHits||(r.cacheHits=[]);return new Promise(function(e){u.push(function(){e(i)})})}}r.keys.push(e);var s=new Promise(function(e,t){r.callbacks.push({resolve:e,reject:t})});return c&&c.set(t,s),s},t.loadMany=function(e){if(!o(e))throw new TypeError(\"The loader.loadMany() function must be called with Array<key> but got: \"+e+\".\");for(var t=[],r=0;r<e.length;r++)t.push(this.load(e[r]).catch(function(e){return e}));return Promise.all(t)},t.clear=function(e){var t=this._cacheMap;if(t){var r=this._cacheKeyFn(e);t.delete(r)}return this},t.clearAll=function(){var e=this._cacheMap;return e&&e.clear(),this},t.prime=function(e,t){var r=this._cacheMap;if(r){var n,a=this._cacheKeyFn(e);if(void 0===r.get(a))t instanceof Error?(n=Promise.reject(t)).catch(function(){}):n=Promise.resolve(t),r.set(a,n)}return this},e}(),r=\"object\"==typeof process&&\"function\"==typeof process.nextTick?function(t){e||(e=Promise.resolve()),e.then(function(){process.nextTick(t)})}:\"function\"==typeof setImmediate?function(e){setImmediate(e)}:function(e){setTimeout(e)};function n(e,t,r){a(t);for(var n=0;n<t.keys.length;n++)e.clear(t.keys[n]),t.callbacks[n].reject(r)}function a(e){if(e.cacheHits)for(var t=0;t<e.cacheHits.length;t++)e.cacheHits[t]()}function o(e){return\"object\"==typeof e&&null!==e&&\"number\"==typeof e.length&&(0===e.length||e.length>0&&Object.prototype.hasOwnProperty.call(e,e.length-1))}return l=t}(),p=s(f);function y({debug:e,apiKey:t,sourceLanguage:n,_apiUrl:a,formality:o,_loaderFactory:c}){const s={debug:!!e,apiKey:t,lang:r.EN,sourceLang:n,apiUrl:a,formality:o},l=new p((c??u)(s),{cacheKeyFn:i(t,n)});return(e,t,r)=>l.load({text:t,...r,lang:e})}const d=y,m=r.EN;function g(e,t){if(!e)return t??m;if([r.FA_AF,r.FR_CA,r.PT_PT,r.ES_MX].includes(e))return e;const n=e.includes(\"-\")?e.split(\"-\")[0]:e;return Object.values(r).includes(n)?n:t||(console.warn(`Language not found for UTS locale \"${e}\" and no fallback provided was provided. Falling back to \"${m}\"`),m)}export{p as DataLoader,r as Language,i as cacheKeyFn,o as defaultDataLoaderOptions,u as defaultLoaderFactory,e as libraryName,t as libraryWebsite,d as setupT,y as setupTranslate,g as utsLocaleToLanguage};\n//# sourceMappingURL=index.es.js.map\n","import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n Language,\n libraryWebsite,\n MagicTranslateConfig,\n TranslateOptions,\n utsLocaleToLanguage,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport type TranslateParams = Omit<DataLoaderLoadParams, 'text'>;\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(text: string, params: TranslateParams = {}): Promise<string> {\n return this.dataLoader.load({ text, ...params });\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of [...this.contents.keys()]) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of [...this.attributes.keys()]) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n let translation = this.contents.get(element);\n\n if (!translation) {\n translation = { source: html, requestId: 0 };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(text, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n const rendered = `${leadingWhitespace}${translated}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0 };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n } else if (!force) {\n continue;\n }\n\n const requestId = ++translation.requestId;\n const current = translation;\n this.translate(current.source, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n current.rendered = translated;\n if (element.getAttribute(name) !== translated) {\n element.setAttribute(name, translated);\n }\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n","import {\n createMagicTranslate,\n Languages,\n MagicTranslate,\n TranslateParams,\n} from './magic-translate';\n\nexport * from './index';\n\ndeclare global {\n interface Window {\n magicTranslate?: MagicTranslate;\n }\n}\n\nconst STORAGE_KEY = 'magic-translate-language';\n\nfunction storedLanguage(): Languages | undefined {\n try {\n return (localStorage.getItem(STORAGE_KEY) as Languages | null) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n// `<script src=\".../magic-translate.min.js\" data-api-key=\"...\"></script>`\n// starts translating the page without any further code.\nconst script = document.currentScript as HTMLScriptElement | null;\nconst apiKey = script?.dataset.apiKey;\n\nif (script && apiKey) {\n const { language, sourceLanguage, formality } = script.dataset;\n\n const start = () => {\n const magicTranslate = createMagicTranslate({\n apiKey,\n // a language picked with the language switcher outlives page loads\n language: storedLanguage() ?? (language as Languages | undefined),\n sourceLanguage: sourceLanguage as Languages | undefined,\n formality: formality as TranslateParams['formality'],\n debug: script.hasAttribute('data-debug'),\n });\n magicTranslate.onLanguageChange((language) => {\n try {\n localStorage.setItem(STORAGE_KEY, language);\n } catch {\n // storage is unavailable, e.g. in some private windows\n }\n });\n window.magicTranslate = magicTranslate;\n document.dispatchEvent(\n new CustomEvent('magictranslate:ready', { detail: magicTranslate })\n );\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', start);\n } else {\n start();\n }\n}\n"],"mappings":"A,6FAKA,IAAMM,EAAE,kBAAkBC,EAAE,4BAAgC,EAAE,SAASC,EAAE,EAAE,CAAC,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,GAAY,EAAE,KAAX,IAAK,EAAQ,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,GAAGC,GAAG,EAAEA,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAM,EAAE,IAAI,gBAAgBA,GAAG,EAAE,EAAE,KAAK,CAAC,MAAM,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAACA,EAAE,EAAED,EAAE,GAAGA,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,EAAY,OAAO,GAAjB,UAA2B,EAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,CAACC,EAAE,EAAED,EAAE,GAAGA,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,OAAOA,EAAE,CAAC,CAAC,EAAE,OAAOC,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAA,CAAE,AAAI,IAAE,CAAC,CAAE,EAAE,IAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,gCAAgC,KAAK,EAAE,EAAE,EAAMC,GAAG,SAAS,EAAE,CAAC,EAAE,qBAAqB,sBAAsB,EAAA,CAAE,AAAIA,IAAE,CAAC,CAAE,EAAE,IAAMC,GAAG,EAAE,EAAE,IAAI,GAAG,OAAO,QAAQH,EAAE,CAAC,OAAO,EAAE,SAASC,EAAE,eAAeG,EAAE,YAAY,GAAG,OAAO,GAAGA,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,EAAEC,EAAE,GAAG,KAAM,IAAG,CAAC,GAAGN,EAAE,OAAO,QAAQ,MAAM,kDAAuCE,CAAC,EAAEF,EAAE,OAAOA,EAAE,WAAW,OAAOE,EAAE,IAAI,GAAG,EAAE,IAAI,EAAE,GAAK,CAAC,OAAOC,EAAE,KAAKC,EAAE,UAAUE,GAAGN,EAAEO,EAAEN,EAAE,CAAC,OAAOE,EAAE,KAAKC,EAAE,UAAUE,EAAE,MAAMJ,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,KAAKA,EAAE,GAAGG,KAAKJ,EAAE,CAAC,KAAK,EAAE,KAAKC,EAAE,WAAW,EAAE,GAAGG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,MAAM,GAAGL,EAAE,QAAQ,EAAE,OAAO,eAAe,CAAC,OAAO,OAAO,KAAKA,EAAE,QAAQ,KAAK,UAAUO,CAAC,EAAE,EAAEA,CAAC,EAAE,QAAQ,CAAC,eAAeP,EAAE,QAAQ,mBAAmB,mCAAmC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,YAAYA,EAAEA,EAAE,SAAS,SAASA,EAAE,QAAQ,MAAM,IAAID,EAAE,KAAKC,EAAE,SAAS,EAAE,QAAQ,MAAM,IAAID,EAAE,qDAAqD,KAAK,UAAUC,CAAC,GAAG,EAAEE,EAAE,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,QAAQ,MAAM,IAAIH,EAAE,uCAAuCC,CAAC,EAAEE,EAAE,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,SAASK,EAAE,EAAE,CAAC,OAAO,GAAG,EAAE,YAAY,OAAO,UAAU,eAAe,KAAK,EAAE,SAAS,EAAE,EAAE,QAAQ,CAAC,CAAC,IAAIC,EAAEC,EAA6uI,EAAEF,EAAvuI,UAAU,CAAC,GAAGE,EAAE,OAAOD,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,CAAC,SAAS,EAAE,EAAE,EAAE,CAAC,GAAe,OAAO,GAAnB,WAAqB,MAAU,UAAU,uHAAuH,EAAE,GAAG,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,SAAS,EAAE,CAAwB,GAAhB,GAAG,CAAC,IAAI,EAAE,MAAY,MAAO,GAAE,IAAIN,EAAE,GAAG,EAAE,aAAa,GAAYA,IAAT,IAAK,GAAM,MAAO,GAAE,EAAE,GAAa,OAAOA,GAAjB,UAAoBA,EAAE,EAAE,MAAU,UAAU,2CAA2CA,CAAC,EAAE,OAAOA,CAAC,EAAE,CAAC,EAAE,KAAK,iBAAiB,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,gBAAgB,GAAY,IAAT,IAAK,GAAM,OAAOA,EAAE,GAAe,OAAO,GAAnB,WAAqB,MAAU,UAAU,uCAAuC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,KAAK,YAAY,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAY,IAAT,IAAK,GAAM,OAAO,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,GAAe,OAAO,GAAnB,WAAqB,MAAU,UAAU,kCAAkC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,KAAK,UAAU,SAAS,EAAE,CAAwB,GAAhB,GAAG,CAAC,IAAI,EAAE,MAAY,OAAO,KAAK,IAAIA,EAAE,GAAG,EAAE,SAAS,GAAYA,IAAT,IAAK,GAAM,OAAO,IAAI,IAAI,GAAUA,IAAP,KAAS,CAAC,IAAI,EAAE,CAAC,MAAM,MAAM,SAAS,OAAO,CAAC,CAAC,OAAO,SAAS,EAAE,CAAC,OAAOA,GAAe,OAAOA,EAAE,IAArB,UAAuB,CAAC,EAAE,GAAO,EAAE,SAAN,EAAa,MAAU,UAAU,oCAAoC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,OAAOA,CAAC,EAAE,CAAC,EAAE,KAAK,OAAO,KAAK,KAAK,KAAK,SAAS,EAAE,CAA4B,OAAxB,GAAG,EAAE,KAAY,EAAE,KAAY,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,UAAU,MAAO,GAAE,KAAK,SAAS,EAAE,CAAC,GAAS,GAAN,KAAQ,MAAU,UAAU,oEAAoE,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAEA,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,OAAO,GAAU,IAAP,MAAU,CAAC,EAAE,eAAe,EAAE,KAAK,OAAO,EAAE,cAAc,OAAO,EAAE,IAAIA,EAAE,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,MAAO,GAAE,OAAOA,EAAE,EAAE,iBAAiB,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC,EAAM,EAAE,KAAK,SAAX,EAAkB,OAAO,KAAKG,EAAE,CAAC,EAAE,IAAIH,EAAE,GAAG,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,OAAOA,EAAE,CAAC,OAAOD,EAAE,EAAE,EAAM,UAAU,sJAAsJ,OAAOC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAACA,GAAe,OAAOA,EAAE,MAArB,WAA0B,OAAOD,EAAE,EAAE,EAAM,UAAU,yJAAyJ,OAAOC,CAAC,EAAE,GAAG,CAAC,EAAEA,EAAE,KAAK,SAAS,EAAE,CAAC,GAAG,CAACQ,EAAE,CAAC,EAAE,MAAU,UAAU,qKAAqK,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,OAAO,MAAU,UAAU;;;EAAuN,OAAO,EAAE,IAAI,EAAE;;;EAAgB,OAAO,CAAC,CAAC,EAAEL,EAAE,CAAC,EAAE,IAAI,IAAIH,EAAE,EAAEA,EAAE,EAAE,UAAU,OAAO,IAAI,CAAC,IAAID,EAAE,EAAEC,GAAGD,aAAa,MAAM,EAAE,UAAUC,EAAE,CAAC,OAAOD,CAAC,EAAE,EAAE,UAAUC,EAAE,CAAC,QAAQD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,SAAS,EAAE,CAACA,EAAE,EAAE,EAAEC,CAAC,CAAC,CAAC,CAAC,EAAA,CAAE,EAAEA,CAAC,CAAC,CAAC,EAAEA,CAAC,EAAE,IAAI,EAAEC,EAAE,KAAK,UAAU,GAAGA,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,EAAE,IAAIC,EAAED,EAAE,IAAI,CAAC,EAAE,GAAGC,EAAE,CAAC,IAAIE,EAAE,AAAcJ,EAAE,YAAU,CAAC,EAAG,OAAO,IAAI,QAAQ,SAAS,EAAE,CAACI,EAAE,KAAK,UAAU,CAAC,EAAEF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAACF,EAAE,KAAK,KAAK,CAAC,EAAE,IAAIK,EAAE,IAAI,QAAQ,SAAS,EAAE,EAAE,CAACL,EAAE,UAAU,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAOC,GAAGA,EAAE,IAAI,EAAEI,CAAC,EAAEA,CAAC,EAAE,EAAE,SAAS,SAAS,EAAE,CAAC,GAAG,CAACG,EAAE,CAAC,EAAE,MAAU,UAAU,0EAA0E,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,EAAER,EAAE,EAAEA,EAAE,EAAE,OAAO,IAAI,EAAE,KAAK,KAAK,KAAK,EAAEA,EAAE,CAAC,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,QAAQ,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,CAAC,IAAIA,EAAE,KAAK,YAAY,CAAC,EAAE,EAAE,OAAOA,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,SAAS,UAAU,CAAC,IAAI,EAAE,KAAK,UAAU,OAAO,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,SAAS,EAAE,EAAE,CAAC,IAAIA,EAAE,KAAK,UAAU,GAAGA,EAAE,CAAC,IAAID,EAAEI,EAAE,KAAK,YAAY,CAAC,EAAcH,EAAE,IAAIG,CAAC,IAAhB,IAAK,KAAa,aAAa,OAAO,EAAE,QAAQ,OAAO,CAAC,EAAA,CAAG,MAAM,UAAU,CAAC,CAAC,EAAE,EAAE,QAAQ,QAAQ,CAAC,EAAEH,EAAE,IAAIG,EAAEJ,CAAC,EAAC,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,EAAEC,EAAY,OAAO,SAAjB,UAAsC,OAAO,QAAQ,UAA3B,WAAoC,SAAS,EAAE,CAAC,AAAI,IAAE,QAAQ,QAAQ,EAAG,EAAE,KAAK,UAAU,CAAC,QAAQ,SAAS,CAAC,CAAC,CAAC,CAAC,EAAc,OAAO,cAAnB,WAAgC,SAAS,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,SAASD,EAAE,EAAE,EAAE,EAAE,CAACI,EAAE,CAAC,EAAE,IAAI,IAAIJ,EAAE,EAAEA,EAAE,EAAE,KAAK,OAAO,IAAI,EAAE,MAAM,EAAE,KAAKA,EAAE,EAAE,EAAE,UAAUA,EAAE,CAAC,OAAOC,CAAC,CAAC,CAAC,SAASG,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,IAAI,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,OAAO,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,SAASK,EAAE,EAAE,CAAC,OAAgB,OAAO,GAAjB,YAA2B,GAAa,OAAO,EAAE,QAAnB,WAAgC,EAAE,SAAN,GAAc,EAAE,OAAO,GAAG,OAAO,UAAU,eAAe,KAAK,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,MAAO,GAAE,CAAC,EAAQ,CAAC,EAAE,SAASC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQN,EAAE,UAAUK,EAAE,eAAeP,GAAG,CAAC,IAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,WAAW,EAAE,OAAOE,EAAE,UAAUK,CAAC,EAAEF,EAAE,IAAI,GAAGL,GAAGG,EAAA,CAAG,CAAC,EAAE,CAAC,WAAWF,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,IAAII,EAAE,KAAK,CAAC,KAAK,EAAE,GAAGN,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAMU,EAAED,EAAEE,EAAE,EAAE,GAAG,SAASC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,GAAGD,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAM,EAAE,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,QAAQ,KAAK,sCAAsC,EAAE,4DAA4DA,EAAE,EAAE,EAAEA,EAAE,CC8BnpO,IAAaE,EAAmB,4BAEnBC,EAAsB,iBASnC,SAASC,EAAU,EAA2B,CAC5C,IAAIC,EAAUC,EACd,IACE,IAAIC,EAASF,EAAQ,eAAe,QAAQH,CAAgB,EAC5DK,EACA,EAASF,EAAQ,eAAe,QAAQH,CAAgB,EAExD,EAAUK,EAEZ,OAAOF,CACT,CAEA,SAASG,EAAU,EAAoC,CACrD,IAAMC,EAA4B,CAAC,EAC7BC,EAAOJ,EAAQ,aAAa,WAAW,EACvCK,EAAYL,EAAQ,aAAa,gBAAgB,EAOvD,OANII,IACF,EAAQ,KAAOA,GAEbC,IACF,EAAQ,UAAYA,GAEfF,CACT,CAEA,IAAaG,EAAb,KAA4B,CAS1B,YAAY,EAAiD,CAC3D,GAD2B,KAAA,QAAAH,EALD,KAAA,SAAA,IAAI,IACF,KAAA,WAAA,IAAI,IACL,KAAA,UAAA,IAAI,IAI3B,CAACA,EAAQ,OACX,MAAU,MAAM,sCAAsC7B,GAAgB,EAGxE,KAAK,gBACH6B,EAAQ,UACR5B,EACE,OAAO,UAAc,IAAc,IAAA,GAAY,UAAU,QAC3D,EACF,KAAK,WAAa,KAAK,iBAAiB,EAEpC,OAAO,SAAa,MACtB,KAAK,KAAO4B,EAAQ,MAAQ,SAAS,gBACrC,KAAK,MAAM,KAAK,IAAI,EAExB,CAKA,IAAI,UAAsB,CACxB,OAAO,KAAK,eACd,CAKA,YAAY,EAA2B,CACjCI,IAAa,KAAK,kBAItB,KAAK,gBAAkBA,EACvB,KAAK,WAAa,KAAK,iBAAiB,EAExC,KAAK,OACF,GAAY,CAACP,EAAQ,aAAe,CAAC,KAAK,MAAM,SAASA,CAAO,CACnE,EACA,KAAK,SAAS,SAAS,EAAG,IAAY,KAAK,iBAAiBA,EAAS,EAAI,CAAC,EAC1E,KAAK,WAAW,SAAS,EAAG,IAC1B,KAAK,oBAAoBA,EAAS,EAAI,CACxC,EACA,KAAK,UAAU,QAAS,GAAaQ,EAASD,CAAQ,CAAC,EACzD,CAMA,iBAAiB,EAAqD,CAEpE,OADA,KAAK,UAAU,IAAIC,CAAQ,MACd,KAAK,UAAU,OAAOA,CAAQ,CAC7C,CAKA,UAAU,EAAc,EAA0B,CAAC,EAAoB,CACrE,OAAO,KAAK,WAAW,KAAK,CAAE,OAAM,GAAGC,CAAO,CAAC,CACjD,CAMA,SAAgB,CACd,KAAK,UAAU,WAAW,EAC1B,KAAK,SAAS,MAAM,EACpB,KAAK,WAAW,MAAM,EACtB,KAAK,UAAU,MAAM,CACvB,CAEA,kBAA2B,CACzB,GAAM,CAAE,QAAO,SAAQ,iBAAgB,UAAS,aAAc,KAAK,QAGnE,OAAO,IAAIhC,GAFW,KAAK,QAAQ,gBAAkBD,EAGnD,CAAc,CACZ,MAAO,CAAC,CAACkC,EACT,SACA,KAAM,KAAK,gBACX,WAAYC,EACZ,OAAQC,EACR,WACF,CAAC,EACD,CACE,WAAYlC,EAAWmC,EAAQF,EAAgB,KAAK,eAAe,CACrE,CACF,CACF,CAEA,MAAc,EAAe,CAC3B,IAAMG,EAAW,IAAI,IACfC,EAAa,IAAI,IACvB,KAAK,QAAQC,EAAMF,EAAUC,CAAU,EACvCD,EAAS,QAAS,GAAY,KAAK,iBAAiBd,CAAO,CAAC,EAC5De,EAAW,QAAS,GAAY,KAAK,oBAAoBf,CAAO,CAAC,EAGjE,KAAK,SAAW,IAAI,iBAAkB,GACpC,KAAK,gBAAgBiB,CAAO,CAC9B,EACA,KAAK,SAAS,QAAQD,EAAM,CAC1B,QAAS,GACT,UAAW,GACX,cAAe,GACf,WAAY,EACd,CAAC,CACH,CAEA,QACE,EACA,EACA,EACA,CACA,IAAME,EAAM,CAACF,EAAM,GAAG,MAAM,KAAKA,EAAK,iBAAiB,GAAG,CAAC,CAAC,EAC5D,IAAK,IAAMhB,KAAWkB,EAChBlB,EAAQ,QAAA,2BAAwB,EAClCc,EAAS,IAAIhB,EAAUE,CAAO,CAAC,EAE/BA,EAAQ,QAAA,gBAA2B,GACnC,CAACA,EAAQ,QAAA,2BAAwB,GAEjCe,EAAW,IAAIf,CAAO,CAG5B,CAEA,gBAAwB,EAA2B,CACjD,IAAMc,EAAW,IAAI,IACfC,EAAa,IAAI,IAEvB,IAAK,IAAMI,KAAUF,EAAS,CAC5B,IAAMG,EACJD,EAAO,OAAO,WAAa,KAAK,aAC3BA,EAAO,OACRA,EAAO,OAAO,cACpB,GAAI,CAACC,EACH,SAGF,IAAMC,EAAUD,EAAO,QAAQxB,CAAgB,EAC3CyB,EACFP,EAAS,IAAIhB,EAAUuB,CAAO,CAAC,GAI7BF,EAAO,OAAS,cAAgBC,EAAO,QAAA,gBAA2B,GACpEL,EAAW,IAAIK,CAAM,EAEvBD,EAAO,WAAW,QAAS,GAAS,CAC9BG,EAAK,WAAa,KAAK,cACzB,KAAK,QAAQA,EAAiBR,EAAUC,CAAU,CAEtD,CAAC,EACH,CAEIE,EAAQ,KAAM,GAAWE,EAAO,aAAa,OAAS,CAAC,GACzD,KAAK,OAAQ,GAAY,CAACnB,EAAQ,WAAW,EAE/Cc,EAAS,QAAS,GAAY,KAAK,iBAAiBd,CAAO,CAAC,EAC5De,EAAW,QAAS,GAAY,KAAK,oBAAoBf,CAAO,CAAC,CACnE,CAEA,OAAe,EAA0C,CACvD,IAAK,IAAMA,IAAW,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EACxCuB,EAAUvB,CAAO,GACnB,KAAK,SAAS,OAAOA,CAAO,EAGhC,IAAK,IAAMA,IAAW,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC,EAC1CuB,EAAUvB,CAAO,GACnB,KAAK,WAAW,OAAOA,CAAO,CAGpC,CAEA,iBAAyB,EAAkB,EAAQ,GAAO,CACxD,IAAMwB,EAAOxB,EAAQ,UACjByB,EAAc,KAAK,SAAS,IAAIzB,CAAO,EAE3C,GAAI,CAACyB,EACH,EAAc,CAAE,OAAQD,EAAM,UAAW,CAAE,EAC3C,KAAK,SAAS,IAAIxB,EAASyB,CAAW,OACjC,GAAID,IAASC,EAAY,UAAYD,IAASC,EAAY,OAE/D,EAAY,OAASD,EACrB,EAAY,SAAW,IAAA,QAClB,GAAI,CAACE,EACV,OAGF,GAAM,EAAGC,EAAmBC,EAAMC,GAChC,yBAAyB,KACvBJ,EAAY,OAAO,QAAQ,mBAAoB,EAAE,CACnD,EACF,GAAI,CAACG,EACH,OAIF,IAAME,EAAY,EAAEL,EAAY,UAC1B1B,EAAU0B,EAChB,KAAK,UAAUG,EAAM1B,EAAUF,CAAO,CAAC,CAAC,CAAC,KAAM,GAAe,CAC5D,GAAI8B,IAAc/B,EAAQ,WAAa,CAACC,EAAQ,YAC9C,OAEF,IAAM+B,EAAW,GAAGJ,IAAoBK,IAAaH,IACjD7B,EAAQ,YAAc+B,IACxB,EAAQ,UAAYA,GAEtB,EAAQ,SAAW/B,EAAQ,SAC7B,CAAC,CACH,CAEA,oBAA4B,EAAkB,EAAQ,GAAO,CAC3D,IAAMiC,GAASjC,EAAQ,aAAa,cAAc,GAAK,GAAA,CACpD,MAAM,QAAQ,CAAC,CACf,OAAO,OAAO,EACbkC,EAAe,KAAK,WAAW,IAAIlC,CAAO,EACzCkC,IACH,EAAe,IAAI,IACnB,KAAK,WAAW,IAAIlC,EAASkC,CAAY,GAG3C,IAAK,IAAMC,KAAQF,EAAO,CACxB,IAAMG,EAAQpC,EAAQ,aAAamC,CAAI,EACvC,GAAI,CAACC,GAAO,KAAK,EACf,SAGF,IAAIX,EAAcS,EAAa,IAAIC,CAAI,EACvC,GAAI,CAACV,EACH,EAAc,CAAE,OAAQW,EAAO,UAAW,CAAE,EAC5CF,EAAa,IAAIC,EAAMV,CAAW,OAC7B,GACLW,IAAUX,EAAY,UACtBW,IAAUX,EAAY,OAEtB,EAAY,OAASW,EACrB,EAAY,SAAW,IAAA,QAClB,GAAI,CAACV,EACV,SAGF,IAAMI,EAAY,EAAEL,EAAY,UAC1B1B,EAAU0B,EAChB,KAAK,UAAU1B,EAAQ,OAAQG,EAAUF,CAAO,CAAC,CAAC,CAAC,KAAM,GAAe,CAClE8B,IAAc/B,EAAQ,WAAcC,EAAQ,cAGhD,EAAQ,SAAWgC,EACfhC,EAAQ,aAAamC,CAAI,IAAMH,GACjChC,EAAQ,aAAamC,EAAMH,CAAU,EAEzC,CAAC,CACH,CACF,CACF,EAOA,SAAgBK,EACd,EACgB,CAChB,OAAO,IAAI/B,EAAeH,CAAO,CACnC,CC/UA,IAAMmC,EAAc,2BAEpB,SAASC,GAAwC,CAC/C,GAAI,CACF,OAAQ,aAAa,QAAQD,CAAW,GAA0B,IAAA,EACpE,MAAQ,CACN,MACF,CACF,CAIA,IAAME,EAAS,SAAS,cAClB3B,EAAS2B,GAAQ,QAAQ,OAE/B,GAAIA,GAAU3B,EAAQ,CACpB,GAAM,CAAE,WAAU,iBAAgB,aAAc2B,EAAO,QAEjDC,MAAc,CAClB,IAAMC,EAAiBL,EAAqB,CAC1C,SAEA,SAAUE,EAAe,GAAMhC,EACf,iBACL,YACX,MAAOiC,EAAO,aAAa,YAAY,CACzC,CAAC,EACDE,EAAe,iBAAkB,GAAa,CAC5C,GAAI,CACF,aAAa,QAAQJ,EAAa/B,CAAQ,CAC5C,MAAQ,CAER,CACF,CAAC,EACD,OAAO,eAAiBmC,EACxB,SAAS,cACP,IAAI,YAAY,uBAAwB,CAAE,OAAQA,CAAe,CAAC,CACpE,CACF,EAEI,SAAS,aAAe,UAC1B,SAAS,iBAAiB,mBAAoBD,CAAK,EAEnDA,EAAM,CAEV,C"}
1
+ {"version":3,"file":"magic-translate.min.js","names":["libraryName","libraryWebsite","utsLocaleToLanguage","toTemplate","fillPlaceholders","defaultLoaderFactory","DataLoader","cacheKeyFn","toHtmlTemplate","exports","e","t","n","r","c","i","a","u","s","l","h","o","d","y","m","g","b","w","T","E","A","k","S","_","CONTENT_SELECTOR","ATTRIBUTES_SELECTOR","outermost","current","element","parent","optionsOf","options","lang","formality","variablesOf","json","variables","fillHtml","values","html","container","MagicTranslate","language","listener","params","v","text","unused","template","translation","debug","sourceLanguage","_apiUrl","apiKey","contents","attributes","root","records","all","record","target","content","node","predicate","variablesJson","force","leadingWhitespace","trailingWhitespace","show","rendered","translated","requestId","names","translations","name","value","createMagicTranslate","STORAGE_KEY","storedLanguage","script","start","magicTranslate"],"sources":["../../core/dist/index.es.js","../src/magic-translate.ts","../src/script.ts"],"sourcesContent":["/*\n * 'Magic Translate Core'\n * {@link https://https://magictranslate.io\n * @copyright i42 GmbH\n */\nconst e=\"Magic Translate\",t=\"https://magictranslate.io\";var n;function r(e){return Object.keys(e).filter(t=>void 0!==e[t]).reduce((t,n)=>({...t,[n]:e[n]}),{})}function a(e){const t=new URLSearchParams,n=(e,r=\"\")=>{Array.isArray(e)?e.forEach((e,t)=>{n(e,r?`${r}[${t}]`:`[${t}]`)}):\"object\"==typeof e&&null!==e?Object.entries(e).forEach(([e,t])=>{n(t,r?`${r}.${e}`:e)}):t.append(r,e)};return n(e),t}!function(e){e.AF=\"af\",e.SQ=\"sq\",e.AM=\"am\",e.AR=\"ar\",e.HY=\"hy\",e.AZ=\"az\",e.BN=\"bn\",e.BS=\"bs\",e.BG=\"bg\",e.CA=\"ca\",e.ZH=\"zh\",e.ZH_TW=\"zh-TW\",e.HR=\"hr\",e.CS=\"cs\",e.DA=\"da\",e.FA_AF=\"fa-AF\",e.NL=\"nl\",e.EN=\"en\",e.ET=\"et\",e.FA=\"fa\",e.TL=\"tl\",e.FI=\"fi\",e.FR=\"fr\",e.FR_CA=\"fr-CA\",e.KA=\"ka\",e.DE=\"de\",e.EL=\"el\",e.GU=\"gu\",e.HT=\"ht\",e.HA=\"ha\",e.HE=\"he\",e.HI=\"hi\",e.HU=\"hu\",e.IS=\"is\",e.ID=\"id\",e.GA=\"ga\",e.IT=\"it\",e.JA=\"ja\",e.KN=\"kn\",e.KK=\"kk\",e.KO=\"ko\",e.LV=\"lv\",e.LT=\"lt\",e.MK=\"mk\",e.MS=\"ms\",e.ML=\"ml\",e.MT=\"mt\",e.MR=\"mr\",e.MN=\"mn\",e.NO=\"no\",e.PS=\"ps\",e.PL=\"pl\",e.PT=\"pt\",e.PT_PT=\"pt-PT\",e.PA=\"pa\",e.RO=\"ro\",e.RU=\"ru\",e.SR=\"sr\",e.SI=\"si\",e.SK=\"sk\",e.SL=\"sl\",e.SO=\"so\",e.ES=\"es\",e.ES_MX=\"es-MX\",e.SW=\"sw\",e.SV=\"sv\",e.TA=\"ta\",e.TE=\"te\",e.TH=\"th\",e.TR=\"tr\",e.UK=\"uk\",e.UR=\"ur\",e.UZ=\"uz\",e.VI=\"vi\",e.CY=\"cy\"}(n||(n={}));const o={debug:!1,apiUrl:\"https://api.magictranslate.io\",lang:n.DE};var c;!function(e){e.QUOTA_EXCEEDED_ERROR=\"QUOTA_EXCEEDED_ERROR\"}(c||(c={}));const i=(e,t,n)=>a=>Object.entries(r({apiKey:e,language:n,sourceLanguage:a.sourceLang??t??\"auto\",...a})).sort(([e])=>e.localeCompare(e)).reduce((e,t)=>`${e};${t.join(\"=\")}`,\"\"),u=t=>async n=>{if(t.debug&&console.debug(`[${e}]: batch function called with: `,n),t.lang===t.sourceLang)return n.map(e=>e.text);const{apiKey:c,lang:i,formality:u}=t,s=r({apiKey:c,lang:i,formality:u,items:n.map(({text:e,sourceLang:t,lang:n,...a})=>r({text:e,lang:n,sourceLang:t,...a}))});return fetch(`${t.apiUrl??o.apiUrl}/v1/translate`,{method:\"POST\",body:t.useJson?JSON.stringify(s):a(s),headers:{\"Content-Type\":t.useJson?\"application/json\":\"application/x-www-form-urlencoded\"}}).then(e=>e.json()).then(t=>\"results\"in t?t.results:(\"code\"in t?console.error(`[${e}]: ${t.message}`):console.error(`[${e}]: Unexpected payload from magic translate server: ${JSON.stringify(t)}`),n.map(e=>e.text))).catch(t=>(console.error(`[${e}]: Error while loading translations: `,t),n.map(e=>e.text)))};function s(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,\"default\")?e.default:e}var l,h;var f=function(){if(h)return l;h=1;var e,t=function(){function e(e,t){if(\"function\"!=typeof e)throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but got: \"+e+\".\");this._batchLoadFn=e,this._maxBatchSize=function(e){var t=!e||!1!==e.batch;if(!t)return 1;var n=e&&e.maxBatchSize;if(void 0===n)return 1/0;if(\"number\"!=typeof n||n<1)throw new TypeError(\"maxBatchSize must be a positive number: \"+n);return n}(t),this._batchScheduleFn=function(e){var t=e&&e.batchScheduleFn;if(void 0===t)return n;if(\"function\"!=typeof t)throw new TypeError(\"batchScheduleFn must be a function: \"+t);return t}(t),this._cacheKeyFn=function(e){var t=e&&e.cacheKeyFn;if(void 0===t)return function(e){return e};if(\"function\"!=typeof t)throw new TypeError(\"cacheKeyFn must be a function: \"+t);return t}(t),this._cacheMap=function(e){var t=!e||!1!==e.cache;if(!t)return null;var n=e&&e.cacheMap;if(void 0===n)return new Map;if(null!==n){var r=[\"get\",\"set\",\"delete\",\"clear\"].filter(function(e){return n&&\"function\"!=typeof n[e]});if(0!==r.length)throw new TypeError(\"Custom cacheMap missing methods: \"+r.join(\", \"))}return n}(t),this._batch=null,this.name=function(e){if(e&&e.name)return e.name;return null}(t)}var t=e.prototype;return t.load=function(e){if(null==e)throw new TypeError(\"The loader.load() function must be called with a value, but got: \"+String(e)+\".\");var t,n=function(e){var t=e._batch;if(null!==t&&!t.hasDispatched&&t.keys.length<e._maxBatchSize)return t;var n={hasDispatched:!1,keys:[],callbacks:[]};return e._batch=n,e._batchScheduleFn(function(){!function(e,t){if(t.hasDispatched=!0,0===t.keys.length)return void a(t);var n;try{n=e._batchLoadFn(t.keys)}catch(n){return r(e,t,new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function errored synchronously: \"+String(n)+\".\"))}if(!n||\"function\"!=typeof n.then)return r(e,t,new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise: \"+String(n)+\".\"));n.then(function(e){if(!o(e))throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise of an Array: \"+String(e)+\".\");if(e.length!==t.keys.length)throw new TypeError(\"DataLoader must be constructed with a function which accepts Array<key> and returns Promise<Array<value>>, but the function did not return a Promise of an Array of the same length as the Array of keys.\\n\\nKeys:\\n\"+String(t.keys)+\"\\n\\nValues:\\n\"+String(e));a(t);for(var n=0;n<t.callbacks.length;n++){var r=e[n];r instanceof Error?t.callbacks[n].reject(r):t.callbacks[n].resolve(r)}}).catch(function(n){r(e,t,n)})}(e,n)}),n}(this),c=this._cacheMap;if(c){t=this._cacheKeyFn(e);var i=c.get(t);if(i){var u=n.cacheHits||(n.cacheHits=[]);return new Promise(function(e){u.push(function(){e(i)})})}}n.keys.push(e);var s=new Promise(function(e,t){n.callbacks.push({resolve:e,reject:t})});return c&&c.set(t,s),s},t.loadMany=function(e){if(!o(e))throw new TypeError(\"The loader.loadMany() function must be called with Array<key> but got: \"+e+\".\");for(var t=[],n=0;n<e.length;n++)t.push(this.load(e[n]).catch(function(e){return e}));return Promise.all(t)},t.clear=function(e){var t=this._cacheMap;if(t){var n=this._cacheKeyFn(e);t.delete(n)}return this},t.clearAll=function(){var e=this._cacheMap;return e&&e.clear(),this},t.prime=function(e,t){var n=this._cacheMap;if(n){var r,a=this._cacheKeyFn(e);if(void 0===n.get(a))t instanceof Error?(r=Promise.reject(t)).catch(function(){}):r=Promise.resolve(t),n.set(a,r)}return this},e}(),n=\"object\"==typeof process&&\"function\"==typeof process.nextTick?function(t){e||(e=Promise.resolve()),e.then(function(){process.nextTick(t)})}:\"function\"==typeof setImmediate?function(e){setImmediate(e)}:function(e){setTimeout(e)};function r(e,t,n){a(t);for(var r=0;r<t.keys.length;r++)e.clear(t.keys[r]),t.callbacks[r].reject(n)}function a(e){if(e.cacheHits)for(var t=0;t<e.cacheHits.length;t++)e.cacheHits[t]()}function o(e){return\"object\"==typeof e&&null!==e&&\"number\"==typeof e.length&&(0===e.length||e.length>0&&Object.prototype.hasOwnProperty.call(e,e.length-1))}return l=t}(),p=s(f);function d({debug:e,apiKey:t,sourceLanguage:r,_apiUrl:a,formality:o,_loaderFactory:c}){const s={debug:!!e,apiKey:t,lang:n.EN,sourceLang:r,apiUrl:a,formality:o},l=new p((c??u)(s),{cacheKeyFn:i(t,r)});return(e,t,n)=>l.load({text:t,...n,lang:e})}const y=d,m=n.EN;function g(e,t){if(!e)return t??m;if([n.FA_AF,n.FR_CA,n.PT_PT,n.ES_MX].includes(e))return e;const r=e.includes(\"-\")?e.split(\"-\")[0]:e;return Object.values(n).includes(r)?r:t||(console.warn(`Language not found for UTS locale \"${e}\" and no fallback provided was provided. Falling back to \"${m}\"`),m)}const v=/\\{(\\d+)\\}/g,b=e=>e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\");function w(e){const t=Object.keys(e).sort((e,t)=>t.length-e.length),n=t.length>0?new RegExp(`\\\\$(${t.map(b).join(\"|\")})`,\"g\"):null,r=new Map,a=[];return{values:a,replace:t=>n?t.replace(n,(t,n)=>{let o=r.get(n);return void 0===o&&(o=a.push(String(e[n]??\"\"))-1,r.set(n,o)),`{${o}}`}):t,unused:()=>t.filter(e=>!r.has(e))}}function T(t){t.length>0&&console.warn(`[${e}]: ${t.map(e=>`$${e}`).join(\", \")} not found in the text. Check the spelling, or remove ${1===t.length?\"it\":\"them\"} from the variables.`)}function E(e,t){const{replace:n,values:r,unused:a}=w(t);return{template:n(e),values:r,unused:a()}}function A(e,t){return e.replace(v,(e,n)=>Number(n)<t.length?t[Number(n)]:e)}function k(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_TEXT),n=[];for(let e=t.nextNode();e;e=t.nextNode())n.push(e);return n}function S(e,t){const n=document.createElement(\"template\");n.innerHTML=e;const{replace:r,values:a,unused:o}=w(t);return k(n.content).forEach(e=>{e.data=r(e.data)}),{template:n.innerHTML,values:a,unused:o()}}function _(e,t){k(e).forEach(e=>{const n=A(e.data,t);n!==e.data&&(e.data=n)})}export{p as DataLoader,n as Language,i as cacheKeyFn,o as defaultDataLoaderOptions,u as defaultLoaderFactory,_ as fillElement,A as fillPlaceholders,e as libraryName,t as libraryWebsite,y as setupT,d as setupTranslate,w as templater,S as toHtmlTemplate,E as toTemplate,g as utsLocaleToLanguage,T as warnAboutUnused};\n//# sourceMappingURL=index.es.js.map\n","import {\n cacheKeyFn,\n DataLoader,\n DataLoaderLoadParams,\n defaultLoaderFactory,\n fillElement,\n fillPlaceholders,\n Language,\n libraryName,\n libraryWebsite,\n MagicTranslateConfig,\n toHtmlTemplate,\n toTemplate,\n TranslateOptions,\n utsLocaleToLanguage,\n Variables,\n warnAboutUnused,\n} from '@magic-translate/core';\n\n/**\n * Type alias to make the language enum more flexible to also allow passing language strings\n * instead of just the enum.\n */\nexport type Languages = `${Language}`;\n\nexport interface TranslateParams extends Omit<DataLoaderLoadParams, 'text'> {\n /**\n * Values that are not translated, referenced as `$name` in the text. The text\n * is translated once as a template and cached, whatever the values are.\n */\n v?: Variables;\n}\n\nexport interface MagicTranslateOptions extends Omit<\n MagicTranslateConfig,\n 'language'\n> {\n /**\n * The language content is translated to. Defaults to the browser's language.\n */\n language?: Languages;\n /**\n * The element whose content is translated. Defaults to the whole document.\n */\n root?: Element;\n}\n\n/** Elements whose content is translated. */\nexport const CONTENT_SELECTOR = 't, mt-translate, [data-t]';\n/** Elements with attributes to translate, e.g. `data-t-attrs=\"placeholder title\"`. */\nexport const ATTRIBUTES_SELECTOR = '[data-t-attrs]';\n\ninterface Translation {\n source: string;\n // what was last written to the DOM, to tell our own changes from others\n rendered?: string;\n requestId: number;\n // the `data-v` attribute the source was last translated with\n variables: string | null;\n // to warn about unused variables once per text, not on every render\n template?: string;\n}\n\nfunction outermost(element: Element): Element {\n let current = element;\n for (\n let parent = current.parentElement?.closest(CONTENT_SELECTOR);\n parent;\n parent = current.parentElement?.closest(CONTENT_SELECTOR)\n ) {\n current = parent;\n }\n return current;\n}\n\nfunction optionsOf(element: Element): TranslateOptions {\n const options: TranslateOptions = {};\n const lang = element.getAttribute('data-lang');\n const formality = element.getAttribute('data-formality');\n if (lang) {\n options.lang = lang as Languages;\n }\n if (formality) {\n options.formality = formality as TranslateOptions['formality'];\n }\n return options;\n}\n\n/**\n * Reads the values in `data-v`, a JSON object such as `{\"name\": \"Bob\"}`.\n * Returns `null` if the attribute is not valid, so the text is left as it is\n * rather than translated with `$name` in it.\n */\nfunction variablesOf(element: Element): Variables | undefined | null {\n const json = element.getAttribute('data-v');\n if (json === null) {\n return undefined;\n }\n try {\n const variables: unknown = JSON.parse(json);\n if (\n variables &&\n typeof variables === 'object' &&\n !Array.isArray(variables)\n ) {\n return variables as Variables;\n }\n } catch {\n // reported below\n }\n console.warn(\n `[${libraryName}]: data-v must be a JSON object, such as data-v='{\"name\": \"Bob\"}', but is ${json}`\n );\n return null;\n}\n\nfunction fillHtml(html: string, values: readonly string[]): string {\n if (values.length === 0) {\n return html;\n }\n const container = document.createElement('template');\n container.innerHTML = html;\n fillElement(container.content, values);\n return container.innerHTML;\n}\n\nexport class MagicTranslate {\n private currentLanguage: Languages;\n private dataLoader: DataLoader<DataLoaderLoadParams, string, string>;\n private readonly root?: Element;\n private readonly contents = new Map<Element, Translation>();\n private readonly attributes = new Map<Element, Map<string, Translation>>();\n private readonly listeners = new Set<(language: Languages) => void>();\n private observer?: MutationObserver;\n\n constructor(private readonly options: MagicTranslateOptions) {\n if (!options.apiKey) {\n throw new Error(`Missing API key. Please get one at ${libraryWebsite}`);\n }\n\n this.currentLanguage =\n options.language ??\n utsLocaleToLanguage(\n typeof navigator === 'undefined' ? undefined : navigator.language\n );\n this.dataLoader = this.createDataLoader();\n\n if (typeof document !== 'undefined') {\n this.root = options.root ?? document.documentElement;\n this.start(this.root);\n }\n }\n\n /**\n * The language content is currently translated to.\n */\n get language(): Languages {\n return this.currentLanguage;\n }\n\n /**\n * Translates the page to another language.\n */\n setLanguage(language: Languages): void {\n if (language === this.currentLanguage) {\n return;\n }\n\n this.currentLanguage = language;\n this.dataLoader = this.createDataLoader();\n\n this.forget(\n (element) => !element.isConnected || !this.root?.contains(element)\n );\n this.contents.forEach((_, element) => this.translateContent(element, true));\n this.attributes.forEach((_, element) =>\n this.translateAttributes(element, true)\n );\n this.listeners.forEach((listener) => listener(language));\n }\n\n /**\n * Calls `listener` whenever the language changes. Returns a function that\n * removes the listener again.\n */\n onLanguageChange(listener: (language: Languages) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Translates a string to the current language.\n */\n translate(\n text: string,\n { v, ...params }: TranslateParams = {}\n ): Promise<string> {\n if (!v) {\n return this.dataLoader.load({ text, ...params });\n }\n const { template, values, unused } = toTemplate(text, v);\n warnAboutUnused(unused);\n return this.dataLoader\n .load({ text: template, ...params })\n .then((translation) => fillPlaceholders(translation, values));\n }\n\n /**\n * Stops translating the page. Content that is already translated stays\n * translated.\n */\n destroy(): void {\n this.observer?.disconnect();\n this.contents.clear();\n this.attributes.clear();\n this.listeners.clear();\n }\n\n private createDataLoader() {\n const { debug, apiKey, sourceLanguage, _apiUrl, formality } = this.options;\n const loaderFactory = this.options._loaderFactory ?? defaultLoaderFactory;\n\n return new DataLoader<DataLoaderLoadParams, string, string>(\n loaderFactory({\n debug: !!debug,\n apiKey,\n lang: this.currentLanguage,\n sourceLang: sourceLanguage,\n apiUrl: _apiUrl,\n formality,\n }),\n {\n cacheKeyFn: cacheKeyFn(apiKey, sourceLanguage, this.currentLanguage),\n }\n );\n }\n\n private start(root: Element) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n this.collect(root, contents, attributes);\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n\n // Picks up content that is added or changed later.\n this.observer = new MutationObserver((records) =>\n this.handleMutations(records)\n );\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n\n private collect(\n root: Element,\n contents: Set<Element>,\n attributes: Set<Element>\n ) {\n const all = [root, ...Array.from(root.querySelectorAll('*'))];\n for (const element of all) {\n if (element.matches(CONTENT_SELECTOR)) {\n contents.add(outermost(element));\n } else if (\n element.matches(ATTRIBUTES_SELECTOR) &&\n !element.closest(CONTENT_SELECTOR)\n ) {\n attributes.add(element);\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]) {\n const contents = new Set<Element>();\n const attributes = new Set<Element>();\n\n for (const record of records) {\n const target =\n record.target.nodeType === Node.ELEMENT_NODE\n ? (record.target as Element)\n : record.target.parentElement;\n if (!target) {\n continue;\n }\n\n const content = target.closest(CONTENT_SELECTOR);\n if (content) {\n contents.add(outermost(content));\n continue;\n }\n\n if (record.type === 'attributes' && target.matches(ATTRIBUTES_SELECTOR)) {\n attributes.add(target);\n }\n record.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.collect(node as Element, contents, attributes);\n }\n });\n }\n\n if (records.some((record) => record.removedNodes.length > 0)) {\n this.forget((element) => !element.isConnected);\n }\n contents.forEach((element) => this.translateContent(element));\n attributes.forEach((element) => this.translateAttributes(element));\n }\n\n private forget(predicate: (element: Element) => boolean) {\n for (const element of this.contents.keys()) {\n if (predicate(element)) {\n this.contents.delete(element);\n }\n }\n for (const element of this.attributes.keys()) {\n if (predicate(element)) {\n this.attributes.delete(element);\n }\n }\n }\n\n private translateContent(element: Element, force = false) {\n const html = element.innerHTML;\n const variablesJson = element.getAttribute('data-v');\n let translation = this.contents.get(element);\n if (!translation) {\n translation = { source: html, requestId: 0, variables: variablesJson };\n this.contents.set(element, translation);\n } else if (html !== translation.rendered && html !== translation.source) {\n // changed by someone else, e.g. the page's own scripts\n translation.source = html;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n // only the values changed, so the cached translation is reused\n translation.variables = variablesJson;\n } else if (!force) {\n return;\n }\n\n const [, leadingWhitespace, text, trailingWhitespace] =\n /^(\\s*)([\\s\\S]*?)(\\s*)$/.exec(\n translation.source.replace(/<!--[\\s\\S]*?-->/g, '')\n )!;\n if (!text) {\n return;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n return;\n }\n const { template, values, unused } = variables\n ? toHtmlTemplate(text, variables)\n : { template: text, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = `${leadingWhitespace}${fillHtml(translated, values)}${trailingWhitespace}`;\n if (element.innerHTML !== rendered) {\n element.innerHTML = rendered;\n }\n current.rendered = element.innerHTML;\n };\n // Until the first translation arrives, show the values instead of `$name`.\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n // make sure that we only ever use the response of the most recent request.\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n\n private translateAttributes(element: Element, force = false) {\n const names = (element.getAttribute('data-t-attrs') ?? '')\n .split(/[\\s,]+/)\n .filter(Boolean);\n const variablesJson = element.getAttribute('data-v');\n let translations = this.attributes.get(element);\n if (!translations) {\n translations = new Map();\n this.attributes.set(element, translations);\n }\n\n for (const name of names) {\n const value = element.getAttribute(name);\n if (!value?.trim()) {\n continue;\n }\n\n let translation = translations.get(name);\n if (!translation) {\n translation = { source: value, requestId: 0, variables: variablesJson };\n translations.set(name, translation);\n } else if (\n value !== translation.rendered &&\n value !== translation.source\n ) {\n translation.source = value;\n translation.rendered = undefined;\n translation.variables = variablesJson;\n } else if (variablesJson !== translation.variables) {\n translation.variables = variablesJson;\n } else if (!force) {\n continue;\n }\n\n const variables = variablesOf(element);\n if (variables === null) {\n continue;\n }\n const { template, values, unused } = variables\n ? toTemplate(translation.source, variables)\n : { template: translation.source, values: [], unused: [] };\n if (template !== translation.template) {\n translation.template = template;\n warnAboutUnused(unused);\n }\n\n const current = translation;\n const show = (translated: string) => {\n const rendered = fillPlaceholders(translated, values);\n current.rendered = rendered;\n if (element.getAttribute(name) !== rendered) {\n element.setAttribute(name, rendered);\n }\n };\n if (variables && current.rendered === undefined) {\n show(template);\n }\n\n const requestId = ++current.requestId;\n this.translate(template, optionsOf(element)).then((translated) => {\n if (requestId !== current.requestId || !element.isConnected) {\n return;\n }\n show(translated);\n });\n }\n }\n}\n\n/**\n * Starts translating the page: the content of every `<t>` element (or element\n * with a `data-t` attribute), and the attributes listed in `data-t-attrs`.\n * Content added later is translated as well.\n */\nexport function createMagicTranslate(\n options: MagicTranslateOptions\n): MagicTranslate {\n return new MagicTranslate(options);\n}\n","import {\n createMagicTranslate,\n Languages,\n MagicTranslate,\n TranslateParams,\n} from './magic-translate';\n\nexport * from './index';\n\ndeclare global {\n interface Window {\n magicTranslate?: MagicTranslate;\n }\n}\n\nconst STORAGE_KEY = 'magic-translate-language';\n\nfunction storedLanguage(): Languages | undefined {\n try {\n return (localStorage.getItem(STORAGE_KEY) as Languages | null) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n// `<script src=\".../magic-translate.min.js\" data-api-key=\"...\"></script>`\n// starts translating the page without any further code.\nconst script = document.currentScript as HTMLScriptElement | null;\nconst apiKey = script?.dataset.apiKey;\n\nif (script && apiKey) {\n const { language, sourceLanguage, formality } = script.dataset;\n\n const start = () => {\n const magicTranslate = createMagicTranslate({\n apiKey,\n // a language picked with the language switcher outlives page loads\n language: storedLanguage() ?? (language as Languages | undefined),\n sourceLanguage: sourceLanguage as Languages | undefined,\n formality: formality as TranslateParams['formality'],\n debug: script.hasAttribute('data-debug'),\n });\n magicTranslate.onLanguageChange((language) => {\n try {\n localStorage.setItem(STORAGE_KEY, language);\n } catch {\n // storage is unavailable, e.g. in some private windows\n }\n });\n window.magicTranslate = magicTranslate;\n document.dispatchEvent(\n new CustomEvent('magictranslate:ready', { detail: magicTranslate })\n );\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', start);\n } else {\n start();\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,15 +1,25 @@
1
1
  {
2
2
  "name": "@magic-translate/js",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "keywords": [
5
5
  "javascript",
6
+ "vanilla-js",
6
7
  "html",
7
- "magic-translate",
8
+ "script-tag",
9
+ "website-translation",
10
+ "wordpress",
8
11
  "i18n",
9
- "translations",
10
- "multilanguage"
12
+ "internationalization",
13
+ "localization",
14
+ "l10n",
15
+ "translation",
16
+ "translate",
17
+ "ai-translation",
18
+ "machine-translation",
19
+ "multilingual",
20
+ "magic-translate"
11
21
  ],
12
- "description": "Plain JavaScript client for Magic Translate (https://magictranslate.io): translate any website with a script tag",
22
+ "description": "Translate any website with one script tag. AI-powered i18n for plain JavaScript and HTML, with no translation keys or language files.",
13
23
  "main": "dist/index.cjs",
14
24
  "module": "dist/index.js",
15
25
  "types": "dist/index.d.ts",
@@ -32,25 +42,19 @@
32
42
  "url": "git+https://github.com/magic-translate/magic-translate.git",
33
43
  "directory": "packages/js"
34
44
  },
35
- "homepage": "https://magictranslate.io",
45
+ "homepage": "https://magictranslate.io/docs/javascript",
36
46
  "license": "MIT",
37
47
  "engines": {
38
48
  "node": ">=22.22.1"
39
49
  },
40
50
  "dependencies": {
41
- "@magic-translate/core": "^1.4.1"
51
+ "@magic-translate/core": "^1.5.0"
42
52
  },
43
53
  "devDependencies": {
44
- "@eslint/js": "^10.0.1",
45
54
  "@types/node": "^26.6.5",
46
55
  "@vitest/coverage-v8": "^5.0.5",
47
- "eslint": "^10.13.0",
48
- "eslint-config-prettier": "^10.1.8",
49
- "globals": "^17.13.0",
50
56
  "jsdom": "^30.1.3",
51
- "prettier": "^3.9.10",
52
57
  "typescript": "~6.0.3",
53
- "typescript-eslint": "^8.72.0",
54
58
  "vite": "^8.3.5",
55
59
  "vitest": "^5.0.5"
56
60
  },
@@ -61,7 +65,6 @@
61
65
  "build": "vite build && vite build --mode script && tsc -p tsconfig.build.json",
62
66
  "start": "vite build --watch",
63
67
  "test": "vitest run --coverage",
64
- "test:watch": "vitest",
65
- "lint": "eslint ."
68
+ "test:watch": "vitest"
66
69
  }
67
70
  }