@magic-translate/js 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +159 -2
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-translate.d.ts +67 -0
- package/dist/magic-translate.min.js +8 -0
- package/dist/magic-translate.min.js.map +1 -0
- package/package.json +66 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 - PRESENT i42 GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,160 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @magic-translate/js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@magic-translate/js)
|
|
4
|
+
[](https://www.npmjs.com/package/@magic-translate/js)
|
|
5
|
+
|
|
6
|
+
Plain JavaScript client for [Magic Translate](https://magictranslate.io). Mark
|
|
7
|
+
the text on your page, and Magic Translate translates it on the fly. Works on
|
|
8
|
+
any website: plain HTML, server-rendered pages, WordPress, or apps built with
|
|
9
|
+
any framework.
|
|
10
|
+
|
|
11
|
+
## Setup with a script tag
|
|
12
|
+
|
|
13
|
+
Add one script tag to your page. You can get an API key for free at
|
|
14
|
+
[magictranslate.io](https://magictranslate.io).
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script
|
|
18
|
+
src="https://cdn.jsdelivr.net/npm/@magic-translate/js@1/dist/magic-translate.min.js"
|
|
19
|
+
data-api-key="YOUR_API_KEY"
|
|
20
|
+
data-language="de"
|
|
21
|
+
defer
|
|
22
|
+
></script>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then wrap the text to translate in `<t>`:
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<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>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Leave out `data-language` to translate to the visitor's browser language. The
|
|
33
|
+
script tag accepts:
|
|
34
|
+
|
|
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. |
|
|
39
|
+
| `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. |
|
|
42
|
+
|
|
43
|
+
The script makes the running instance available as `window.magicTranslate`.
|
|
44
|
+
|
|
45
|
+
## Setup with npm
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install --save @magic-translate/js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { createMagicTranslate } from '@magic-translate/js';
|
|
53
|
+
|
|
54
|
+
const magicTranslate = createMagicTranslate({
|
|
55
|
+
apiKey: 'YOUR_API_KEY',
|
|
56
|
+
language: 'de',
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`createMagicTranslate` starts translating the page right away.
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Translating content
|
|
65
|
+
|
|
66
|
+
The content of these elements is translated:
|
|
67
|
+
|
|
68
|
+
- `<t>` elements: `<t>Hello</t>`
|
|
69
|
+
- `<mt-translate>` elements, if you prefer a custom element name
|
|
70
|
+
- any element with a `data-t` attribute, for places where an extra element
|
|
71
|
+
does not fit: `<option data-t>German</option>`, `<title data-t>Shop</title>`
|
|
72
|
+
|
|
73
|
+
The content can be text and HTML. The original content is shown until its
|
|
74
|
+
translation has loaded.
|
|
75
|
+
|
|
76
|
+
All translations requested at the same time are sent to the API in a single
|
|
77
|
+
request, and translations are cached, so you can mark as many elements as you
|
|
78
|
+
like.
|
|
79
|
+
|
|
80
|
+
Content added to the page later, or changed by your scripts, is translated
|
|
81
|
+
too, so it works with content rendered by JavaScript.
|
|
82
|
+
|
|
83
|
+
### Translating attributes
|
|
84
|
+
|
|
85
|
+
List the attributes to translate in `data-t-attrs`:
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<input placeholder="Search products" data-t-attrs="placeholder" />
|
|
89
|
+
<img src="team.jpg" alt="Our team" title="Say hi" data-t-attrs="alt title" />
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Options per element
|
|
93
|
+
|
|
94
|
+
`data-lang` and `data-formality` override the target language and the
|
|
95
|
+
formality:
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<t data-lang="fr" data-formality="formal">How are you?</t>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Switching languages
|
|
102
|
+
|
|
103
|
+
Call `setLanguage` to translate the page to another language:
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<select onchange="magicTranslate.setLanguage(this.value)">
|
|
107
|
+
<option value="en">English</option>
|
|
108
|
+
<option value="de">Deutsch</option>
|
|
109
|
+
<option value="fr">Français</option>
|
|
110
|
+
</select>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
With the script tag, the language picked last is remembered for the next page
|
|
114
|
+
load.
|
|
115
|
+
|
|
116
|
+
`magicTranslate.language` is the current language, and `onLanguageChange`
|
|
117
|
+
calls you back when it changes:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
magicTranslate.onLanguageChange((language) => {
|
|
121
|
+
document.documentElement.lang = language;
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Translating in code
|
|
126
|
+
|
|
127
|
+
To translate a string in JavaScript, call `translate`:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
const message = await magicTranslate.translate('Your order has been placed');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
With the script tag, the instance is created once the page has loaded. To use
|
|
134
|
+
it from your own scripts as soon as it is ready, listen for
|
|
135
|
+
`magictranslate:ready`:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
document.addEventListener('magictranslate:ready', (event) => {
|
|
139
|
+
const magicTranslate = event.detail;
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
`createMagicTranslate` accepts:
|
|
146
|
+
|
|
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. |
|
|
151
|
+
| `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. |
|
|
155
|
+
|
|
156
|
+
Call `destroy()` to stop translating the page.
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT © [i42 GmbH](https://i42.ch)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +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]}})});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { DataLoader as e, cacheKeyFn as t, defaultLoaderFactory as n, libraryWebsite as r, utsLocaleToLanguage as i } from "@magic-translate/core";
|
|
2
|
+
export * from "@magic-translate/core";
|
|
3
|
+
// src/magic-translate.ts
|
|
4
|
+
var a = "t, mt-translate, [data-t]", o = "[data-t-attrs]";
|
|
5
|
+
function s(e) {
|
|
6
|
+
let t = e;
|
|
7
|
+
for (let e = t.parentElement?.closest(a); e; e = t.parentElement?.closest(a)) t = e;
|
|
8
|
+
return t;
|
|
9
|
+
}
|
|
10
|
+
function c(e) {
|
|
11
|
+
let t = {}, n = e.getAttribute("data-lang"), r = e.getAttribute("data-formality");
|
|
12
|
+
return n && (t.lang = n), r && (t.formality = r), t;
|
|
13
|
+
}
|
|
14
|
+
var l = class {
|
|
15
|
+
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));
|
|
18
|
+
}
|
|
19
|
+
get language() {
|
|
20
|
+
return this.currentLanguage;
|
|
21
|
+
}
|
|
22
|
+
setLanguage(e) {
|
|
23
|
+
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)));
|
|
24
|
+
}
|
|
25
|
+
onLanguageChange(e) {
|
|
26
|
+
return this.listeners.add(e), () => this.listeners.delete(e);
|
|
27
|
+
}
|
|
28
|
+
translate(e, t = {}) {
|
|
29
|
+
return this.dataLoader.load({
|
|
30
|
+
text: e,
|
|
31
|
+
...t
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
destroy() {
|
|
35
|
+
this.observer?.disconnect(), this.contents.clear(), this.attributes.clear(), this.listeners.clear();
|
|
36
|
+
}
|
|
37
|
+
createDataLoader() {
|
|
38
|
+
let { debug: r, apiKey: i, sourceLanguage: a, _apiUrl: o, formality: s } = this.options, c = this.options._loaderFactory ?? n;
|
|
39
|
+
return new e(c({
|
|
40
|
+
debug: !!r,
|
|
41
|
+
apiKey: i,
|
|
42
|
+
lang: this.currentLanguage,
|
|
43
|
+
sourceLang: a,
|
|
44
|
+
apiUrl: o,
|
|
45
|
+
formality: s
|
|
46
|
+
}), { cacheKeyFn: t(i, a, this.currentLanguage) });
|
|
47
|
+
}
|
|
48
|
+
start(e) {
|
|
49
|
+
let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
50
|
+
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, {
|
|
51
|
+
subtree: !0,
|
|
52
|
+
childList: !0,
|
|
53
|
+
characterData: !0,
|
|
54
|
+
attributes: !0
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
collect(e, t, n) {
|
|
58
|
+
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);
|
|
60
|
+
}
|
|
61
|
+
handleMutations(e) {
|
|
62
|
+
let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
63
|
+
for (let r of e) {
|
|
64
|
+
let e = r.target.nodeType === Node.ELEMENT_NODE ? r.target : r.target.parentElement;
|
|
65
|
+
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) => {
|
|
68
|
+
e.nodeType === Node.ELEMENT_NODE && this.collect(e, t, n);
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
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
|
+
}
|
|
73
|
+
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);
|
|
76
|
+
}
|
|
77
|
+
translateContent(e, t = !1) {
|
|
78
|
+
let n = e.innerHTML, r = this.contents.get(e);
|
|
79
|
+
if (!r) r = {
|
|
80
|
+
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;
|
|
84
|
+
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;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
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);
|
|
99
|
+
if (!n?.trim()) continue;
|
|
100
|
+
let a = r.get(i);
|
|
101
|
+
if (!a) a = {
|
|
102
|
+
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;
|
|
106
|
+
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));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function u(e) {
|
|
115
|
+
return new l(e);
|
|
116
|
+
}
|
|
117
|
+
export { o as ATTRIBUTES_SELECTOR, a as CONTENT_SELECTOR, l as MagicTranslate, u as createMagicTranslate };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DataLoaderLoadParams, Language, MagicTranslateConfig } from '@magic-translate/core';
|
|
2
|
+
/**
|
|
3
|
+
* Type alias to make the language enum more flexible to also allow passing language strings
|
|
4
|
+
* instead of just the enum.
|
|
5
|
+
*/
|
|
6
|
+
export type Languages = `${Language}`;
|
|
7
|
+
export type TranslateParams = Omit<DataLoaderLoadParams, 'text'>;
|
|
8
|
+
export interface MagicTranslateOptions extends Omit<MagicTranslateConfig, 'language'> {
|
|
9
|
+
/**
|
|
10
|
+
* The language content is translated to. Defaults to the browser's language.
|
|
11
|
+
*/
|
|
12
|
+
language?: Languages;
|
|
13
|
+
/**
|
|
14
|
+
* The element whose content is translated. Defaults to the whole document.
|
|
15
|
+
*/
|
|
16
|
+
root?: Element;
|
|
17
|
+
}
|
|
18
|
+
/** Elements whose content is translated. */
|
|
19
|
+
export declare const CONTENT_SELECTOR = "t, mt-translate, [data-t]";
|
|
20
|
+
/** Elements with attributes to translate, e.g. `data-t-attrs="placeholder title"`. */
|
|
21
|
+
export declare const ATTRIBUTES_SELECTOR = "[data-t-attrs]";
|
|
22
|
+
export declare class MagicTranslate {
|
|
23
|
+
private readonly options;
|
|
24
|
+
private currentLanguage;
|
|
25
|
+
private dataLoader;
|
|
26
|
+
private readonly root?;
|
|
27
|
+
private readonly contents;
|
|
28
|
+
private readonly attributes;
|
|
29
|
+
private readonly listeners;
|
|
30
|
+
private observer?;
|
|
31
|
+
constructor(options: MagicTranslateOptions);
|
|
32
|
+
/**
|
|
33
|
+
* The language content is currently translated to.
|
|
34
|
+
*/
|
|
35
|
+
get language(): Languages;
|
|
36
|
+
/**
|
|
37
|
+
* Translates the page to another language.
|
|
38
|
+
*/
|
|
39
|
+
setLanguage(language: Languages): void;
|
|
40
|
+
/**
|
|
41
|
+
* Calls `listener` whenever the language changes. Returns a function that
|
|
42
|
+
* removes the listener again.
|
|
43
|
+
*/
|
|
44
|
+
onLanguageChange(listener: (language: Languages) => void): () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Translates a string to the current language.
|
|
47
|
+
*/
|
|
48
|
+
translate(text: string, params?: TranslateParams): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Stops translating the page. Content that is already translated stays
|
|
51
|
+
* translated.
|
|
52
|
+
*/
|
|
53
|
+
destroy(): void;
|
|
54
|
+
private createDataLoader;
|
|
55
|
+
private start;
|
|
56
|
+
private collect;
|
|
57
|
+
private handleMutations;
|
|
58
|
+
private forget;
|
|
59
|
+
private translateContent;
|
|
60
|
+
private translateAttributes;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Starts translating the page: the content of every `<t>` element (or element
|
|
64
|
+
* with a `data-t` attribute), and the attributes listed in `data-t-attrs`.
|
|
65
|
+
* Content added later is translated as well.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createMagicTranslate(options: MagicTranslateOptions): MagicTranslate;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var MagicTranslate=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`Magic Translate`,n=`https://magictranslate.io`,r;function i(e){return Object.keys(e).filter(t=>e[t]!==void 0).reduce((t,n)=>({...t,[n]:e[n]}),{})}function a(e){let t=new URLSearchParams,n=(e,r=``)=>{Array.isArray(e)?e.forEach((e,t)=>{n(e,r?`${r}[${t}]`:`[${t}]`)}):typeof e==`object`&&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`})(r||={});var o={debug:!1,apiUrl:`https://api.magictranslate.io`,lang:r.DE},s;(function(e){e.QUOTA_EXCEEDED_ERROR=`QUOTA_EXCEEDED_ERROR`})(s||={});var c=(e,t,n)=>r=>Object.entries(i({apiKey:e,language:n,sourceLanguage:r.sourceLang??t??`auto`,...r})).sort(([e])=>e.localeCompare(e)).reduce((e,t)=>`${e};${t.join(`=`)}`,``),l=e=>async n=>{if(e.debug&&console.debug(`[Magic Translate]: batch function called with: `,n),e.lang===e.sourceLang)return n.map(e=>e.text);let{apiKey:r,lang:s,formality:c}=e,l=i({apiKey:r,lang:s,formality:c,items:n.map(({text:e,sourceLang:t,lang:n,...r})=>i({text:e,lang:n,sourceLang:t,...r}))});return fetch(`${e.apiUrl??o.apiUrl}/v1/translate`,{method:`POST`,body:e.useJson?JSON.stringify(l):a(l),headers:{"Content-Type":e.useJson?`application/json`:`application/x-www-form-urlencoded`}}).then(e=>e.json()).then(e=>`results`in e?e.results:(`code`in e?console.error(`[${t}]: ${e.message}`):console.error(`[${t}]: Unexpected payload from magic translate server: ${JSON.stringify(e)}`),n.map(e=>e.text))).catch(e=>(console.error(`[${t}]: Error while loading translations: `,e),n.map(e=>e.text)))};function u(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,`default`)?e.default:e}var d,f,p=u(function(){if(f)return d;f=1;var e,t=function(){function e(e,t){if(typeof e!=`function`)throw 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){if(e&&!1===e.batch)return 1;var t=e&&e.maxBatchSize;if(t===void 0)return 1/0;if(typeof t!=`number`||t<1)throw TypeError(`maxBatchSize must be a positive number: `+t);return t}(t),this._batchScheduleFn=function(e){var t=e&&e.batchScheduleFn;if(t===void 0)return n;if(typeof t!=`function`)throw TypeError(`batchScheduleFn must be a function: `+t);return t}(t),this._cacheKeyFn=function(e){var t=e&&e.cacheKeyFn;if(t===void 0)return function(e){return e};if(typeof t!=`function`)throw TypeError(`cacheKeyFn must be a function: `+t);return t}(t),this._cacheMap=function(e){if(e&&!1===e.cache)return null;var t=e&&e.cacheMap;if(t===void 0)return new Map;if(t!==null){var n=[`get`,`set`,`delete`,`clear`].filter(function(e){return t&&typeof t[e]!=`function`});if(n.length!==0)throw TypeError(`Custom cacheMap missing methods: `+n.join(`, `))}return t}(t),this._batch=null,this.name=function(e){return e&&e.name?e.name:null}(t)}var t=e.prototype;return t.load=function(e){if(e==null)throw 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(t!==null&&!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,t.keys.length===0)return void i(t);var n;try{n=e._batchLoadFn(t.keys)}catch(n){return r(e,t,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||typeof n.then!=`function`)return r(e,t,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(!a(e))throw 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 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.
|
|
2
|
+
|
|
3
|
+
Keys:
|
|
4
|
+
`+String(t.keys)+`
|
|
5
|
+
|
|
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})({});
|
|
8
|
+
//# sourceMappingURL=magic-translate.min.js.map
|
|
@@ -0,0 +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":["../node_modules/@magic-translate/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"],"ignoreList":[0],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magic-translate/js",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"javascript",
|
|
6
|
+
"html",
|
|
7
|
+
"magic-translate",
|
|
8
|
+
"i18n",
|
|
9
|
+
"translations",
|
|
10
|
+
"multilanguage"
|
|
11
|
+
],
|
|
12
|
+
"description": "Plain JavaScript client for Magic Translate (https://magictranslate.io): translate any website with a script tag",
|
|
13
|
+
"main": "dist/index.cjs",
|
|
14
|
+
"module": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"unpkg": "dist/magic-translate.min.js",
|
|
17
|
+
"jsdelivr": "dist/magic-translate.min.js",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"author": "i42 GmbH <info@i42.ch>",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/magic-translate/magic-translate-js.git"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://magictranslate.io",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "vite build && vite build --mode script && tsc -p tsconfig.build.json",
|
|
38
|
+
"start": "vite build --watch",
|
|
39
|
+
"test": "vitest run --coverage",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"prepare": "husky",
|
|
42
|
+
"lint": "eslint ."
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=22.22.1"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@magic-translate/core": "^1.4.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@commitlint/cli": "^21.2.4",
|
|
52
|
+
"@commitlint/config-conventional": "^21.2.4",
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
54
|
+
"@types/node": "^26.6.5",
|
|
55
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
56
|
+
"eslint": "^10.13.0",
|
|
57
|
+
"eslint-config-prettier": "^10.1.8",
|
|
58
|
+
"globals": "^17.13.0",
|
|
59
|
+
"husky": "^9.1.7",
|
|
60
|
+
"jsdom": "^30.1.3",
|
|
61
|
+
"lint-staged": "^17.6.0",
|
|
62
|
+
"prettier": "^3.9.10",
|
|
63
|
+
"typescript": "~6.0.3",
|
|
64
|
+
"typescript-eslint": "^8.72.0",
|
|
65
|
+
"vite": "^8.3.5",
|
|
66
|
+
"vitest": "^5.0.5"
|
|
67
|
+
}
|
|
68
|
+
}
|