├── .github ├── dependabot.yml └── workflows │ └── auto-publish.yml ├── .gitignore ├── .pr-preview.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── index.bs ├── security-privacy-questionnaire.md └── w3c.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See the documentation at 2 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | version: 2 4 | updates: 5 | # Update actions used by .github/workflows in this repository. 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | groups: 11 | actions-org: # Groups all Github-authored actions into a single PR. 12 | patterns: ["actions/*"] 13 | -------------------------------------------------------------------------------- /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | jobs: 7 | main: 8 | name: Build, Validate and Deploy 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: w3c/spec-prod@v2 15 | with: 16 | GH_PAGES_BRANCH: gh-pages 17 | BUILD_FAIL_ON: warning 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | index.html 2 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # W3C Web Machine Learning Community Group 2 | 3 | This repository is being used for work in the W3C Web Machine Learning Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors 2 | under the 3 | [W3C Software and Document License](https://www.w3.org/copyright/software-license/). 4 | 5 | Contributions to Specifications are made under the 6 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 7 | 8 | Contributions to Test Suites are made under the 9 | [W3C 3-clause BSD License](https://www.w3.org/copyright/3-clause-bsd-license-2008/) 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Explainer for the Translator and Language Detector APIs 2 | 3 | _This proposal is an early design sketch by the Chrome built-in AI team to describe the problem below and solicit feedback on the proposed solution. It has not been approved to ship in Chrome._ 4 | 5 | Browsers are increasingly offering language translation to their users. Such translation capabilities can also be useful to web developers. This is especially the case when browser's built-in translation abilities cannot help, such as: 6 | 7 | * translating user input or other interactive features; 8 | * pages with complicated DOMs which trip up browser translation; 9 | * providing in-page UI to start the translation; or 10 | * translating content that is not in the DOM, e.g. spoken content. 11 | 12 | To perform translation in such cases, web sites currently have to either call out to cloud APIs, or bring their own translation models and run them using technologies like WebAssembly and WebGPU. This proposal introduces a new JavaScript API for exposing a browser's existing language translation abilities to web pages, so that if present, they can serve as a simpler and less resource-intensive alternative. 13 | 14 | An important supplement to translation is language detection. This can be combined with translation, e.g. taking user input in an unknown language and translating it to a specific target language. In a similar way, browsers today often already have language detection capabilities, and we want to offer them to web developers through a JavaScript API. 15 | 16 | ## Goals 17 | 18 | Our goals are to: 19 | 20 | * Help web developers perform real-time translations (e.g. of user input). 21 | * Help web developers perform real-time language detection. 22 | * Guide web developers to gracefully handle failure cases, e.g. translation not being available or possible. 23 | * Harmonize well with existing browser and OS translation technology ([Brave](https://support.brave.com/hc/articles/8963107404813-How-do-I-use-Brave-Translate), [Chrome](https://support.google.com/chrome/answer/173424&co=GENIE.Platform%3DDesktop#zippy=%2Ctranslate-selected-text), [Edge](https://support.microsoft.com/topic/use-microsoft-translator-in-microsoft-edge-browser-4ad1c6cb-01a4-4227-be9d-a81e127fcb0b), [Firefox](https://support.mozilla.org/kb/website-translation), [Safari](https://9to5mac.com/2020/12/04/how-to-translate-websites-with-safari-mac/)), e.g. by allowing on-the-fly downloading of different languages instead of assuming all are present from the start. 24 | * Allow a variety of implementation strategies, including on-device vs. cloud-based translation, while keeping these details abstracted from developers. 25 | * Allow implementations to expose different capabilities for translation vs. language detection. For example, an implementation might be able to detect 30+ languages, but only be able to translate between 6. 26 | 27 | The following are explicit non-goals: 28 | 29 | * We do not intend to force every browser to ship language packs for every language combination, or even to support translation at all. It would be conforming to implement this API by always saying translation and language detection are unavailable, or to implement this API entirely by using cloud services instead of on-device translation. 30 | * We do not intend to provide guarantees of translation and language detection quality, stability, or interoperability between browsers. These are left as quality-of-implementation issues, similar to the [shape detection API](https://wicg.github.io/shape-detection-api/). (See also a [discussion of interop](https://www.w3.org/reports/ai-web-impact/#interop) in the W3C "AI & the Web" document.) 31 | 32 | The following are potential goals we are not yet certain of: 33 | 34 | * Allow web developers to know whether translations are done on-device or using cloud services. This would allow them to guarantee that any user data they feed into this API does not leave the device, which can be important for privacy purposes. (Similarly, we might want to allow developers to request on-device-only translation, in case a browser offers both varieties.) 35 | * Allow web developers to know some identifier for the translation and language detection models in use, separate from the browser version. This would allow them to allowlist or blocklist specific models to maintain a desired level of quality. 36 | 37 | Both of these potential goals are potentially detrimental to interoperability, so we want to investigate more how important such functionality is to developers to find the right tradeoff. 38 | 39 | ## Examples 40 | 41 | Note that in this API, languages are represented as [BCP 47](https://www.rfc-editor.org/info/bcp47) language tags, as already used by the existing JavaScript `Intl` API or the HTML `lang=""` attribute. Examples: `"ja"`, `"en"`, `"de-AT"`, `"zh-Hans-CN"`. 42 | 43 | See [below](#language-tag-handling) for more on the details of how language tags are handled in this API, and the [appendix](#appendix-converting-between-language-tags-and-human-readable-strings) for some helper code that converts between language tags and human-readable strings. 44 | 45 | ### Translation 46 | 47 | Here is the basic usage of the translator API, with no error handling: 48 | 49 | ```js 50 | const translator = await Translator.create({ 51 | sourceLanguage: "en", 52 | targetLanguage: "ja" 53 | }); 54 | 55 | const text = await translator.translate("Hello, world!"); 56 | const readableStreamOfText = translator.translateStreaming(` 57 | Four score and seven years ago our fathers brought forth, upon this... 58 | `); 59 | ``` 60 | 61 | Note that the `create()` method call here might cause the download of a translation model or language pack. Later examples show how to get more insight into this process. 62 | 63 | ### Language detection 64 | 65 | A similar simplified example of the language detector API: 66 | 67 | ```js 68 | const detector = await LanguageDetector.create(); 69 | 70 | const results = await detector.detect(someUserText); 71 | for (const result of results) { 72 | console.log(result.detectedLanguage, result.confidence); 73 | } 74 | ``` 75 | 76 | Here `results` will be an array of `{ detectedLanguage, confidence }` objects, with the `detectedLanguage` field being a BCP 47 language tag and `confidence` beeing a number between 0 and 1. The array will be sorted by descending confidence. The final entry in the array will always be [`"und"`](https://www.rfc-editor.org/rfc/rfc5646.html#:~:text=*%20%20The%20'und'%20(Undetermined)%20primary,certain%20situations.), representing the probability that the text is not written in any language the model knows. 77 | 78 | The array will always contain at least 1 entry, although it could be for the undetermined (`"und"`) language. 79 | 80 | Very low-confidence results are excluded. See [the specification](https://webmachinelearning.github.io/translation-api/#note-language-detection-post-processing) for more details, as well as the discussions in [issue #39](https://github.com/webmachinelearning/translation-api/issues/39) and [issue #51](https://github.com/webmachinelearning/translation-api/issues/51). 81 | 82 | Because of how very low-confidence results are excluded, the sum of all confidence values could be less than 1. 83 | 84 | ### Language detection with expected input languages 85 | 86 | If there are certain languages you need to be able to detect for your use case, you can include them in the `expectedInputLanguages` option when creating a language detector: 87 | 88 | ```js 89 | const detector = await LanguageDetector.create({ expectedInputLanguages: ["en", "ja"] }); 90 | ``` 91 | 92 | This will allow the implementation to download additional resources like language detection models if necessary, and will ensure that the promise is rejected with a `"NotSupportedError"` `DOMException` if the browser is unable to detect the given input languages. 93 | 94 | ### Checking before creation, and a more realistic combined example 95 | 96 | Both APIs provide the ability to know, before calling `create()`, what is possible with the implementation. This is done via `availability()` methods, which takes the same options as `create()`. They return a promise, which fulfills with one of the following values: 97 | 98 | * `"unavailable"` means that the implementation does not support translation or language detection of the given language(s). 99 | * `"downloadable"` means that the implementation supports translation or language detection of the given language(s), but it will have to download something (e.g., a machine learning model) as part of creating the associated object. 100 | * `"downloading"` means that the implementation supports translation or language detection of the given language(s), but it will have to finish an ongoing download as part of creating the associated object. 101 | * `"available"` means that the implementation supports translation or language detection of the given language(s), without performing any downloads. 102 | 103 | Here is an example that adds capability checking to log more information and fall back to cloud services, as part of a language detection plus translation task: 104 | 105 | ```js 106 | async function translateUnknownCustomerInput(textToTranslate, targetLanguage) { 107 | const detectorAvailability = await LanguageDetector.availability(); 108 | 109 | // If there is no language detector, then assume the source language is the 110 | // same as the document language. 111 | let sourceLanguage = document.documentElement.lang; 112 | 113 | // Otherwise, let's detect the source language. 114 | if (detectorAvailability !== "unavailable") { 115 | if (detectorAvailability !== "available") { 116 | console.log("Language detection is available, but something will have to be downloaded. Hold tight!"); 117 | } 118 | 119 | const detector = await LanguageDetector.create(); 120 | const [bestResult] = await detector.detect(textToTranslate); 121 | 122 | if (bestResult.detectedLanguage ==== "und" || bestResult.confidence < 0.4) { 123 | // We'll just return the input text without translating. It's probably mostly punctuation 124 | // or something. 125 | return textToTranslate; 126 | } 127 | sourceLanguage = bestResult.detectedLanguage; 128 | } 129 | 130 | // Now we've figured out the source language. Let's translate it! 131 | const translatorAvailability = await Translator.availability({ sourceLanguage, targetLanguage }); 132 | if (translatorAvailability === "unavailable") { 133 | console.warn("Translation is not available. Falling back to cloud API."); 134 | return await useSomeCloudAPIToTranslate(textToTranslate, { sourceLanguage, targetLanguage }); 135 | } 136 | 137 | if (translatorAvailability !== "available") { 138 | console.log("Translation is available, but something will have to be downloaded. Hold tight!"); 139 | } 140 | 141 | const translator = await Translator.create({ sourceLanguage, targetLanguage }); 142 | return await translator.translate(textToTranslate); 143 | } 144 | ``` 145 | 146 | ### Download progress 147 | 148 | For cases where using the API is only possible after a download, you can monitor the download progress (e.g. in order to show your users a progress bar) using code such as the following: 149 | 150 | ```js 151 | const translator = await Translator.create({ 152 | sourceLanguage, 153 | targetLanguage, 154 | monitor(m) { 155 | m.addEventListener("downloadprogress", e => { 156 | console.log(`Downloaded ${e.loaded * 100}%`); 157 | }); 158 | } 159 | }); 160 | ``` 161 | 162 | If the download fails, then `downloadprogress` events will stop being emitted, and the promise returned by `create()` will be rejected with a "`NetworkError`" `DOMException`. 163 | 164 | Note that in the case that multiple entities are downloaded (e.g., an `en` ↔ `ja` language pack and a `en` ↔ `ko` language pack to support the `ja` ↔ `ko` use case) web developers do not get the ability to monitor the individual downloads. All of them are bundled into the overall `downloadprogress` events, and the `create()` promise is not fulfilled until all downloads and loads are successful. 165 | 166 | The event is a [`ProgressEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent) whose `loaded` property is between 0 and 1, and whose `total` property is always 1. (The exact number of total or downloaded bytes are not exposed; see the discussion in [webmachinelearning/writing-assistance-apis issue #15](https://github.com/webmachinelearning/writing-assistance-apis/issues/15).) 167 | 168 | At least two events, with `e.loaded === 0` and `e.loaded === 1`, will always be fired. This is true even if creating the translator or language detector doesn't require any downloading. 169 | 170 |
171 | What's up with this pattern? 172 | 173 | This pattern is a little involved. Several alternatives have been considered. However, asking around the web standards community it seemed like this one was best, as it allows using standard event handlers and `ProgressEvent`s, and also ensures that once the promise is settled, the translator or language detector object is completely ready to use. 174 | 175 | It is also nicely future-extensible by adding more events and properties to the `m` object. 176 | 177 | Finally, note that there is a sort of precedent in the (never-shipped) [`FetchObserver` design](https://github.com/whatwg/fetch/issues/447#issuecomment-281731850). 178 |
179 | 180 | ### Too-large inputs 181 | 182 | It's possible that the inputs given for translation or language detection might be too large for the underlying machine learning model to handle. Although there are often techniques that allow implementations to break up the inputs into smaller chunks, and combine the results, the APIs have some facilities to allow browsers to signal such too-large inputs. 183 | 184 | Whenever any API call fails due to too-large input, it is rejected with a `QuotaExceededError`. This is a proposed new type of exception, which subclasses `DOMException`, and replaces the web platform's existing `"QuotaExceededError"` `DOMException`. See [whatwg/webidl#1465](https://github.com/whatwg/webidl/pull/1465) for this proposal. For our purposes, the important part is that it has the following properties: 185 | 186 | * `requested`: how much "usage" the input consists of 187 | * `quota`: how much "usage" was available (which will be less than `requested`) 188 | 189 | The "usage" concept is specific to the implementation, and could be something like string length, or [language model tokens](https://arxiv.org/abs/2404.08335). 190 | 191 | This allows detecting failures due to overlarge inputs and giving clear feedback to the user, with code such as the following: 192 | 193 | ```js 194 | const detector = await LanguageDetector.create(); 195 | 196 | try { 197 | console.log(await detector.detect(potentiallyLargeInput)); 198 | } catch (e) { 199 | if (e.name === "QuotaExceededError") { 200 | console.error(`Input too large! You tried to detect the language of ${e.requested} tokens, but ${e.quota} is the max supported.`); 201 | 202 | // Or maybe: 203 | console.error(`Input too large! It's ${e.requested / e.quota}x as large as the maximum possible input size.`); 204 | } 205 | } 206 | ``` 207 | 208 | In some cases, instead of providing errors after the fact, the developer needs to be able to communicate to the user how close they are to the limit. For this, they can use the `inputQuota` property and the `measureInputUsage()` method on the translator or language detector objects: 209 | 210 | ```js 211 | const translator = await Translator.create({ 212 | sourceLanguage: "en", 213 | targetLanguage: "jp" 214 | }); 215 | meterEl.max = translator.inputQuota; 216 | 217 | textbox.addEventListener("input", () => { 218 | meterEl.value = await translator.measureInputUsage(textbox.value); 219 | submitButton.disabled = meterEl.value > meterEl.max; 220 | }); 221 | 222 | submitButton.addEventListener("click", () => { 223 | console.log(translator.translate(textbox.value)); 224 | }); 225 | ``` 226 | 227 | Note that if an implementation does not have any limits, e.g. because it uses techniques to split up the input and process it a bit at a time, then `inputQuota` will be `+Infinity` and `measureInputUsage()` will always return 0. 228 | 229 | Developers need to be cautious not to over-use this API, however, as it requires a round-trip to the underlying model. That is, the following code is bad, as it performs two round trips with the same input: 230 | 231 | ```js 232 | // DO NOT DO THIS 233 | 234 | const usage = await translator.measureInputUsage(input); 235 | if (usage < translator.inputQuota) { 236 | console.log(await translator.translate(input)); 237 | } else { 238 | console.error(`Input too large!`); 239 | } 240 | ``` 241 | 242 | If you're planning to call `translate()` anyway, then using a pattern like the one that opened this section, which catches `QuotaExceededError`s, is more efficient than using `measureInputUsage()` plus a conditional call to `translate()`. 243 | 244 | ### Destruction and aborting 245 | 246 | The API comes equipped with a couple of `signal` options that accept `AbortSignal`s, to allow aborting the creation of the translator/language detector, or the translation/language detection operations themselves: 247 | 248 | ```js 249 | const controller = new AbortController(); 250 | stopButton.onclick = () => controller.abort(); 251 | 252 | const languageDetector = await LanguageDetector.create({ signal: controller.signal }); 253 | await languageDetector.detect(document.body.textContent, { signal: controller.signal }); 254 | ``` 255 | 256 | Destroying a translator or language detector will: 257 | 258 | * Reject any ongoing calls to `detect()` or `translate()`. 259 | * Error any `ReadableStream`s returned by `translateStreaming()`. 260 | * And, most importantly, allow the user agent to unload the machine learning models from memory. (If no other APIs are using them.) 261 | 262 | Allowing such destruction provides a way to free up the memory used by the model without waiting for garbage collection, since machine learning models can be quite large. 263 | 264 | Aborting the creation process will reject the promise returned by `create()`, and will also stop signaling any ongoing download progress. (The browser may then abort the downloads, or may continue them. Either way, no further `downloadprogress` events will be fired.) 265 | 266 | In all cases, the exception used for rejecting promises or erroring `ReadableStream`s will be an `"AbortError"` `DOMException`, or the given abort reason. 267 | 268 | ## Detailed design 269 | 270 | ### Language tag handling 271 | 272 | If a browser supports translating from `ja` to `en`, does it also support translating from `ja` to `en-US`? What about `en-GB`? What about the (discouraged, but valid) `en-Latn`, i.e. English written in the usual Latin script? But translation to `en-Brai`, English written in the Braille script, is different entirely. 273 | 274 | We're proposing that the API use the same model as JavaScript's `Intl` APIs, which tries to do [best-fit matching](https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit) of the requested language tag to the available language tags. The specification contains [a more detailed example](https://webmachinelearning.github.io/translation-api/#example-language-arc-support). 275 | 276 | ### Multilingual text 277 | 278 | For language detection of multilingual text, we return detected language confidences in proportion to the languages detected. The specification gives [an example](https://webmachinelearning.github.io/translation-api#example-multilingual-input) of how this works. See also the discussion in [issue #13](https://github.com/webmachinelearning/translation-api/issues/13). 279 | 280 | A future option might be to instead have the API return back the splitting of the text into different-language segments. There is [some precedent](https://github.com/pemistahl/lingua-py?tab=readme-ov-file#116-detection-of-multiple-languages-in-mixed-language-texts) for this, but it does not seem to be common yet. This could be added without backward-compatibility problems by making it a non-default mode. 281 | 282 | ### Downloading 283 | 284 | The current design envisions that `availability()` methods will _not_ cause downloads of language packs or other material like a language detection model. Whereas, the `create()` methods _can_ cause downloads. In all cases, whether or not creation will initiate a download can be detected beforehand by the corresponding `availability()` method. 285 | 286 | After a developer has a `Translator` or `LanguageDetector` object, further calls are not expected to cause any downloads. (Although they might require internet access, if the implementation is not entirely on-device.) 287 | 288 | This design means that the implementation must have all information about the capabilities of its translation and language detection models available beforehand, i.e. "shipped with the browser". (Either as part of the browser binary, or through some out-of-band update mechanism that eagerly pushes updates.) 289 | 290 | ## Privacy and security considerations 291 | 292 | Please see [the Writing Assistance APIs specification](https://webmachinelearning.github.io/writing-assistance-apis/#privacy), where we have centralized the normative privacy and security considerations that apply to these APIs as well as the writing assistance APIs. 293 | 294 | ### Permissions policy, iframes, and workers 295 | 296 | By default, these APIs are only available to top-level `Window`s, and to their same-origin iframes. Access to the APIs can be delegated to cross-origin iframes using the [Permissions Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy) `allow=""` attribute: 297 | 298 | ```html 299 | 300 | ``` 301 | 302 | These APIs are currently not available in workers, due to the complexity of establishing a responsible document for each worker in order to check the permissions policy status. See [this discussion](https://github.com/webmachinelearning/translation-api/issues/18#issuecomment-2705630392) for more. It may be possible to loosen this restriction over time, if use cases arise. 303 | 304 | Note that although the APIs are not exposed to web platform workers, a browser could expose them to extension service workers, which are outside the scope of web platform specifications and have a different permissions model. 305 | 306 | ## Alternatives considered and under consideration 307 | 308 | ### Streaming input support 309 | 310 | Although the API contains support for streaming output of a translation, via the `translateStreaming()` API, it doesn't support streaming input. Should it? 311 | 312 | We believe it should not, for now. In general, translation works best with more context; feeding more input into the system over time can produce very different results. For example, translating "彼女の話を聞いて、驚いた" to English would give "I was surprised to hear her story". But if you then streamed in another chunk, so that the full sentence was "彼女の話を聞いて、驚いたねこが逃げた", the result changes completely to "Upon hearing her story, the surprised cat ran away." This doesn't fit well with how streaming APIs behave generally. 313 | 314 | In other words, even if web developers are receiving a stream of input (e.g. over the network or from the user), they need to take special care in how they present such updating-over-time translations to the user. We shouldn't treat this as a usual stream-to-string or stream-to-stream API, because that will rarely be useful. 315 | 316 | That said, we are aware of [research](https://arxiv.org/abs/2005.08595) on translation algorithms which are specialized for this kind of setting, and attempt to mitigate the above problem. It's possible we might want to support this sort of API in the future, if implementations are excited about implementing that research. This should be possible to fit into the existing API surface, possibly with some extra feature-detection API. 317 | 318 | ### Flattening the API and reducing async steps 319 | 320 | The current design requires multiple async steps to do useful things: 321 | 322 | ```js 323 | const translator = await Translator.create(options); 324 | const text = await translator.translate(sourceText); 325 | 326 | const detector = await LanguageDetector.create(); 327 | const results = await detector.detect(sourceText); 328 | ``` 329 | 330 | Should we simplify these down with convenience APIs that do both steps at once? 331 | 332 | We're open to this idea, but we think the existing complexity is necessary to support the design wherein translation and language detection models might not be already downloaded. By separating the two stages, we allow web developers to perform the initial creation-and-possibly-downloading steps early in their page's lifecycle, in preparation for later, hopefully-quick calls to APIs like `translate()`. 333 | 334 | Another possible simplification is to make the `availability()` APIs synchronous instead of asynchronous. This would be implementable by having the browser proactively load the capabilities information into the main thread's process, upon creation of the global object. We think this is not worthwhile, as it imposes a non-negligible cost on all global object creation, even when the APIs are not used. 335 | 336 | ### Allowing unknown source languages for translation 337 | 338 | An earlier revision of this API including support for combining the language detection and translation steps into a single translation call, which did a best-guess on the source language. The idea was that this would possibly be more efficient than requiring the web developer to do two separate calls, and it could possibly even be done using a single model. 339 | 340 | We abandoned this design when it became clear that existing browsers have very decoupled implementations of translation vs. language detection, using separate models for each. This includes supporting different languages for language detection vs. for translation. So even if the translation model supported an unknown-source-language mode, it might not support the same inputs as the language detection model, which would create a confusing developer experience and be hard to signal in the API. 341 | 342 | ## Stakeholder feedback 343 | 344 | * W3C TAG: 345 | * Browser engines: 346 | * Chromium: prototyping behind a flag 347 | * Gecko: 348 | * WebKit: 349 | * Web developers: 350 | * Some comments in [W3C TAG design review](https://github.com/w3ctag/design-reviews/issues/948) 351 | * Some comments in [WICG proposal](https://github.com/WICG/proposals/issues/147) 352 | 353 | ## Appendix: converting between language tags and human-readable strings 354 | 355 | This code already works today and is not new to this API proposal. It is likely useful in conjunction with this API, for example when building user interfaces. 356 | 357 | ```js 358 | function languageTagToHumanReadable(languageTag, targetLanguage) { 359 | const displayNames = new Intl.DisplayNames([targetLanguage], { type: "language" }); 360 | return displayNames.of(languageTag); 361 | } 362 | 363 | languageTagToHumanReadable("ja", "en"); // "Japanese" 364 | languageTagToHumanReadable("zh", "en"); // "Chinese" 365 | languageTagToHumanReadable("zh-Hant", "en"); // "Traditional Chinese" 366 | languageTagToHumanReadable("zh-TW", "en"); // "Chinese (Taiwan)" 367 | 368 | languageTagToHumanReadable("en", "ja"); // "英語" 369 | ``` 370 | -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 | 19 | 20 |
 21 | urlPrefix: https://tc39.es/ecma402/; spec: ECMA-402
 22 |   type: dfn
 23 |     text: Unicode canonicalized locale identifier; url: sec-language-tags
 24 |   type: abstract-op
 25 |     text: LookupMatchingLocaleByBestFit; url: sec-lookupmatchinglocalebybestfit
 26 | urlPrefix: https://tc39.es/ecma262/; spec: ECMA-262
 27 |   type: dfn
 28 |     text: current realm; url: current-realm
 29 | urlPrefix: https://whatpr.org/webidl/1465.html; spec: WEBIDL
 30 |   type: interface
 31 |     text: QuotaExceededError; url: quotaexceedederror
 32 |   type: dfn; for: QuotaExceededError
 33 |     text: requested; url: quotaexceedederror-requested
 34 |     text: quota; url: quotaexceedederror-quota
 35 | 
36 | 37 |

Introduction

38 | 39 | The translator and language detector APIs expose the ability to translate text between human languages, and detect the language of such text. They are complementary to any built-in browser UI features for these purposes, giving web developers the ability to trigger these operations programmatically and integrate them into their applications. This can be especially useful for operating on user input, or text retrieved from the network. 40 | 41 | These APIs are designed to provide a high-level interface for translation and language detection, abstracting away the complexities of underlying machine learning models and their management. To deal with possible interoperability issues arising from different implementation strategies or language support, the API design guides developers toward checking the availability of languages their applications are dependent on, and including appropriate error-handling. 42 | 43 |

Dependencies

44 | 45 | This specification depends on the Infra Standard. [[!INFRA]] 46 | 47 | As with the rest of the web platform, human languages are identified in these APIs by BCP 47 language tags, such as "`ja`", "`en-US`", "`sr-Cyrl`", or "`de-CH-1901-x-phonebk-extended`". The specific algorithms used for validation, canonicalization, and language tag matching are those from the ECMAScript Internationalization API Specification, which in turn defers some of its processing to Unicode Locale Data Markup Language (LDML). [[BCP47]] [[!ECMA-402]] [[UTS35]]. 48 | 49 | These APIs are part of a family of APIs expected to be powered by machine learning models, which share common API surface idioms and specification patterns. Currently, the specification text for these shared parts lives in [[WRITING-ASSISTANCE-APIS#supporting]], and the common privacy and security considerations are discussed in [[WRITING-ASSISTANCE-APIS#privacy]] and [[WRITING-ASSISTANCE-APIS#security]]. Implementing these APIs requires implementing that shared infrastructure, and conforming to those privacy and security considerations. But it does not require implementing or exposing the actual writing assistance APIs. [[!WRITING-ASSISTANCE-APIS]] 50 | 51 |

The translator API

52 | 53 | 54 | [Exposed=Window, SecureContext] 55 | interface Translator { 56 | static Promise<Translator> create(TranslatorCreateOptions options); 57 | static Promise<Availability> availability(TranslatorCreateCoreOptions options); 58 | 59 | Promise<DOMString> translate( 60 | DOMString input, 61 | optional TranslatorTranslateOptions options = {} 62 | ); 63 | ReadableStream translateStreaming( 64 | DOMString input, 65 | optional TranslatorTranslateOptions options = {} 66 | ); 67 | 68 | readonly attribute DOMString sourceLanguage; 69 | readonly attribute DOMString targetLanguage; 70 | 71 | Promise<double> measureInputUsage( 72 | DOMString input, 73 | optional TranslatorTranslateOptions options = {} 74 | ); 75 | readonly attribute unrestricted double inputQuota; 76 | }; 77 | Translator includes DestroyableModel; 78 | 79 | dictionary TranslatorCreateCoreOptions { 80 | required DOMString sourceLanguage; 81 | required DOMString targetLanguage; 82 | }; 83 | 84 | dictionary TranslatorCreateOptions : TranslatorCreateCoreOptions { 85 | AbortSignal signal; 86 | CreateMonitorCallback monitor; 87 | }; 88 | 89 | dictionary TranslatorTranslateOptions { 90 | AbortSignal signal; 91 | }; 92 | 93 | 94 |

Creation

95 | 96 |
97 | The static create(|options|) method steps are: 98 | 99 | 1. Return the result of [=creating an AI model object=] given |options|, "{{translator}}", [=validate and canonicalize translator options=], [=compute translator options availability=], [=download the translation model=], [=initialize the translation model=], and [=create the translator object=]. 100 |
101 | 102 |
103 | To validate and canonicalize translator options given a {{TranslatorCreateCoreOptions}} |options|, perform the following steps. They mutate |options| in place to canonicalize language tags, and throw an exception if any are invalid. 104 | 105 | 1. [=Validate and canonicalize language tags=] given |options| and "{{TranslatorCreateCoreOptions/sourceLanguage}}". 106 | 107 | 1. [=Validate and canonicalize language tags=] given |options| and "{{TranslatorCreateCoreOptions/targetLanguage}}". 108 |
109 | 110 |
111 | To download the translation model, given a {{TranslatorCreateCoreOptions}} |options|: 112 | 113 | 1. [=Assert=]: these steps are running [=in parallel=]. 114 | 115 | 1. Initiate the download process for everything the user agent needs to translate text from |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] to |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"]. 116 | 117 | This could include both a base translation model and specific language arc material, or perhaps material for multiple language arcs if an intermediate language is used. 118 | 119 | 1. If the download process cannot be started for any reason, then return false. 120 | 121 | 1. Return true. 122 |
123 | 124 |
125 | To initialize the translation model, given a {{TranslatorCreateCoreOptions}} |options|: 126 | 127 | 1. [=Assert=]: these steps are running [=in parallel=]. 128 | 129 | 1. Perform any necessary initialization operations for the AI model backing the user agent's capabilities for translating from |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] to |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"]. 130 | 131 | This could include loading the model into memory, or loading any fine-tunings necessary to support the specific options in question. 132 | 133 | 1. If initialization failed for any reason, then return a [=DOMException error information=] whose [=DOMException error information/name=] is "{{OperationError}}" and whose [=DOMException error information/details=] contain appropriate detail. 134 | 135 | 1. Return null. 136 |
137 | 138 |
139 | To create the translator object, given a [=ECMAScript/realm=] |realm| and a {{TranslatorCreateCoreOptions}} |options|: 140 | 141 | 1. [=Assert=]: these steps are running on |realm|'s [=ECMAScript/surrounding agent=]'s [=agent/event loop=]. 142 | 143 | 1. Let |inputQuota| be the amount of input quota that is available to the user agent for future [=translate|translation=] operations. (This value is [=implementation-defined=], and may be +∞ if there are no specific limits beyond, e.g., the user's memory, or the limits of JavaScript strings.) 144 | 145 | 1. Return a new {{Translator}} object, created in |realm|, with 146 | 147 |
148 | : [=Translator/source language=] 149 | :: |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] 150 | 151 | : [=Translator/target language=] 152 | :: |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"] 153 | 154 | : [=Translator/input quota=] 155 | :: |inputQuota| 156 |
157 |
158 | 159 |

Availability

160 | 161 |
162 | The static availability(|options|) method steps are: 163 | 164 | 1. Return the result of [=computing AI model availability=] given |options|, "{{translator}}", [=validate and canonicalize translator options=], and [=compute translator options availability=]. 165 |
166 | 167 |
168 | To compute translator options availability given a {{TranslatorCreateCoreOptions}} |options|, perform the following steps. They return either an {{Availability}} value or null, and they mutate |options| in place to update language tags to their best-fit matches. 169 | 170 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 171 | 172 | 1. Let |availabilities| be the user agent's [=translator language arc availabilities=]. 173 | 174 | 1. If |availabilities| is null, then return null. 175 | 176 | 1. [=map/For each=] |languageArc| → |availability| in |availabilities|: 177 | 178 | 1. Let |sourceLanguageBestFit| be [$LookupMatchingLocaleByBestFit$](« |languageArc|'s [=language arc/source language=] », « |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] »). 179 | 180 | 1. Let |targetLanguageBestFit| be [$LookupMatchingLocaleByBestFit$](« |languageArc|'s [=language arc/target language=] », « |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"] »). 181 | 182 | 1. If |sourceLanguageBestFit| and |targetLanguageBestFit| are both not undefined, then: 183 | 184 | 1. Set |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] to |sourceLanguageBestFit|.\[[locale]]. 185 | 186 | 1. Set |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"] to |targetLanguageBestFit|.\[[locale]]. 187 | 188 | 1. Return |availability|. 189 | 190 | 1. If (|options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"], |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"]) [=language arc/can be fulfilled by the identity translation=], then return "{{Availability/available}}". 191 | 192 |

Such cases could also return "{{Availability/downloadable}}", "{{Availability/downloading}}", or "{{Availability/available}}" because of the above steps, if the user agent has specific entries in its [=translator language arc availabilities=] for the given language arc. However, the identity translation is always available, so this step ensures that we never return "{{Availability/unavailable}}" for such cases. 193 | 194 |

195 |

One [=language arc=] that [=language arc/can be fulfilled by the identity translation=] is (`"en-US"`, `"en-GB"`). It is conceivable that an implementation might support a specialized model for this translation, which would show up in the [=translator language arc availabilities=]. 196 | 197 |

On the other hand, it's pretty unlikely that an implementation has any specialized model for the [=language arc=] ("`en-x-asdf`", "`en-x-xyzw`"). In such a case, this step takes over, and later calls to the [=translate=] algorithm will use the identity translation. 198 | 199 |

Note that when this step takes over, |options|["{{TranslatorCreateCoreOptions/sourceLanguage}}"] and |options|["{{TranslatorCreateCoreOptions/targetLanguage}}"] are not modified, so if this algorithm is being called from {{Translator/create()}}, that means the resulting {{Translator}} object's {{Translator/sourceLanguage}} and {{Translator/targetLanguage}} properties will return the original inputs, and not some canonicalized form. 200 |

201 | 202 | 1. Return "{{Availability/unavailable}}". 203 |
204 | 205 | A language arc is a [=tuple=] of two strings, a source language and a target language. Each item is a [=Unicode canonicalized locale identifier=]. 206 | 207 |
208 | The translator language arc availabilities are given by the following steps. They return a [=map=] from [=language arcs=] to {{Availability}} values, or null. 209 | 210 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 211 | 212 | 1. If there is some error attempting to determine what language arcs the user agent [=model availability/can support=] translating text between, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null. 213 | 214 | 1. Return a [=map=] from [=language arcs=] to {{Availability}} values, where each key is a [=language arc=] that the user agent supports translating text between, filled according to the following constraints: 215 | 216 | * If the user agent [=model availability/currently supports=] translating text from the [=language arc/source language=] to the [=language arc/target language=] of the [=language arc=], then the map must contain an [=map/entry=] whose [=map/key=] is that [=language arc=] and whose [=map/value=] is "{{Availability/available}}". 217 | 218 | * If the user agent believes it will be able to [=model availability/support=] translating text from the [=language arc/source language=] to the [=language arc/target language=] of the [=language arc=], but only after finishing a download that is already ongoing, then the map must contain an [=map/entry=] whose [=map/key=] is that [=language arc=] and whose [=map/value=] is "{{Availability/downloading}}". 219 | 220 | * If the user agent believes it will be able to [=model availability/support=] translating text from the [=language arc/source language=] to the [=language arc/target language=] of the [=language arc=], but only after performing a not-currently ongoing download, then the map must contain an [=map/entry=] whose [=map/key=] is that [=language arc=] and whose [=map/value=] is "{{Availability/downloadable}}". 221 | 222 | * The [=map/keys=] must not include any [=language arcs=] that [=language arc/overlap=] with the other [=map/keys=]. 223 |
224 | 225 |
226 | Let's suppose that the user agent's [=translator language arc availabilities=] are as follows: 227 | 228 | * ("`en`", "`zh-Hans`") → "{{Availability/available}}" 229 | * ("`en`", "`zh-Hant`") → "{{Availability/downloadable}}" 230 | 231 | The use of [$LookupMatchingLocaleByBestFit$] means that {{Translator/availability()}} will probably give the following answers: 232 | 233 | 234 | function a(sourceLanguage, targetLanguage) { 235 | return ai.translator.availability({ sourceLanguage, targetLanguage }): 236 | } 237 | 238 | await a("en", "zh-Hans") === "available"; 239 | await a("en", "zh-Hant") === "downloadable"; 240 | 241 | await a("en", "zh") === "available"; // zh will best-fit to zh-Hans 242 | 243 | await a("en", "zh-TW") === "downloadable"; // zh-TW will best-fit to zh-Hant 244 | await a("en", "zh-HK") === "available"; // zh-HK will best-fit to zh-Hans 245 | await a("en", "zh-CN") === "available"; // zh-CN will best-fit to zh-Hans 246 | 247 | await a("en-US", "zh-Hant") === "downloadable"; // en-US will best-fit to en 248 | await a("en-GB", "zh-Hant") === "downloadable"; // en-GB will best-fit to en 249 | 250 | // Even very unexpected subtags will best-fit to en or zh-Hans 251 | await a("en-Braille-x-lolcat", "zh-Hant") === "downloadable"; 252 | await a("en", "zh-BR-Kana") === "available"; 253 | 254 |
255 | 256 |
257 | A [=language arc=] |arc| overlaps with a [=set=] of [=language arcs=] |otherArcs| if the following steps return true: 258 | 259 | 1. Let |sourceLanguages| be the [=set=] composed of the [=language arc/source languages=] of each [=set/item=] in |otherArcs|. 260 | 261 | 1. If [$LookupMatchingLocaleByBestFit$](|sourceLanguages|, « |arc|'s [=language arc/source language=] ») is not undefined, then return true. 262 | 263 | 1. Let |targetLanguages| be the [=set=] composed of the [=language arc/target languages=] of each [=set/item=] in |otherArcs|. 264 | 265 | 1. If [$LookupMatchingLocaleByBestFit$](|targetLanguages|, « |arc|'s [=language arc/target language=] ») is not undefined, then return true. 266 | 267 | 1. Return false. 268 |
269 | 270 |
271 | The [=language arc=] ("`en`", "`fr`") [=language arc/overlaps=] with « ("`en`", "`fr-CA`") », so the user agent's [=translator language arc availabilities=] cannot contain both of these [=language arcs=] at the same time. 272 | 273 | Instead, a typical user agent will either support only one English-to-French language arc (presumably ("`en`", "`fr`")), or it could support multiple non-overlapping English-to-French language arcs, such as ("`en`", "`fr-FR`"), ("`en`", "`fr-CA`"), and ("`en`", "`fr-CH`"). 274 | 275 | In the latter case, if the web developer requested to create a translator using ai.translator.create({ sourceLanguage: "en", targetLanguage: "fr" }), the [$LookupMatchingLocaleByBestFit$] algorithm would choose one of the three possible language arcs to use (presumably ("`en`", "`fr-FR`")). 276 |
277 | 278 |
279 | A [=language arc=] |arc| can be fulfilled by the identity translation if the following steps return true: 280 | 281 | 1. If [$LookupMatchingLocaleByBestFit$](« |arc|'s [=language arc/source language=] », « |arc|'s [=language arc/target language=] ») is not undefined, then return true. 282 | 283 | 1. If [$LookupMatchingLocaleByBestFit$](« |arc|'s [=language arc/target language=] », « |arc|'s [=language arc/source language=] ») is not undefined, then return true. 284 | 285 | 1. Return false. 286 |
287 | 288 |

The {{Translator}} class

289 | 290 | Every {{Translator}} has a source language, a [=string=], set during creation. 291 | 292 | Every {{Translator}} has a target language, a [=string=], set during creation. 293 | 294 | Every {{Translator}} has an input quota, a number, set during creation. 295 | 296 |
297 | 298 | The sourceLanguage getter steps are to return [=this=]'s [=Translator/source language=]. 299 | 300 | The targetLanguage getter steps are to return [=this=]'s [=Translator/target language=]. 301 | 302 | The inputQuota getter steps are to return [=this=]'s [=Translator/input quota=]. 303 | 304 |
305 | 306 |
307 | The translate(|input|, |options|) method steps are: 308 | 309 | 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=translates=] |input| given [=this=]'s [=Translator/source language=], [=this=]'s [=Translator/target language=], [=this=]'s [=Translator/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|. 310 | 311 | 1. Return the result of [=getting an aggregated AI model result=] given [=this=], |options|, and |operation|. 312 |
313 | 314 |
315 | The translateStreaming(|input|, |options|) method steps are: 316 | 317 | 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=translates=] |input| given [=this=]'s [=Translator/source language=], [=this=]'s [=Translator/target language=], [=this=]'s [=Translator/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|. 318 | 319 | 1. Return the result of [=getting a streaming AI model result=] given [=this=], |options|, and |operation|. 320 |
321 | 322 |
323 | The measureInputUsage(|input|, |options|) method steps are: 324 | 325 | 1. Let |measureUsage| be an algorithm step which takes argument |stopMeasuring|, and returns the result of [=measuring translator input usage=] given |input|, [=this=]'s [=Translator/source language=], [=this=]'s [=Translator/target language=], and |stopMeasuring|. 326 | 327 | 1. Return the result of [=measuring AI model input usage=] given [=this=], |options|, and |measureUsage|. 328 |
329 | 330 |

Translation

331 | 332 |

The algorithm

333 | 334 |
335 | To translate given: 336 | 337 | * a [=string=] |input|, 338 | * a [=Unicode canonicalized locale identifier=] |sourceLanguage|, 339 | * a [=Unicode canonicalized locale identifier=] |targetLanguage|, 340 | * a number |inputQuota|, 341 | * an algorithm |chunkProduced| that takes a string and returns nothing, 342 | * an algorithm |done| that takes no arguments and returns nothing, 343 | * an algorithm |error| that takes [=error information=] and returns nothing, and 344 | * an algorithm |stopProducing| that takes no arguments and returns a boolean, 345 | 346 | perform the following steps: 347 | 348 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 349 | 350 | 1. Let |requested| be the result of [=measuring translator input usage=] given |input|, |sourceLanguage|, |targetLanguage|, and |stopProducing|. 351 | 352 | 1. If |requested| is null, then return. 353 | 354 | 1. If |requested| is an [=error information=], then: 355 | 356 | 1. Perform |error| given |requested|. 357 | 358 | 1. Return. 359 | 360 | 1. [=Assert=]: |requested| is a number. 361 | 362 | 1. If |requested| is greater than |inputQuota|, then: 363 | 364 | 1. Let |errorInfo| be a [=quota exceeded error information=] with a [=quota exceeded error information/requested=] of |requested| and a [=quota exceeded error information/quota=] of |inputQuota|. 365 | 366 | 1. Perform |error| given |errorInfo|. 367 | 368 | 1. Return. 369 | 370 |

In reality, we expect that implementations will check the input usage against the quota as part of the same call into the model as the translation itself. The steps are only separated in the specification for ease of understanding. 371 | 372 | 1. In an [=implementation-defined=] manner, subject to the following guidelines, begin the processs of translating |input| from |sourceLanguage| into |targetLanguage|. 373 | 374 | If |input| is the empty string, or otherwise consists of no translatable content (e.g., only contains whitespace, or control characters), then the resulting translation should be |input|. In such cases, |sourceLanguage| and |targetLanguage| should be ignored. 375 | 376 | If (|sourceLanguage|, |targetLanguage|) [=language arc/can be fulfilled by the identity translation=], then the resulting translation should be |input|. 377 | 378 | The translation process must conform to the guidance given in [[WRITING-ASSISTANCE-APIS#privacy]] and [[WRITING-ASSISTANCE-APIS#security]], notably including (but not limited to) [[WRITING-ASSISTANCE-APIS#privacy-user-input]] and [[WRITING-ASSISTANCE-APIS#security-runtime]]. 379 | 380 | 1. While true: 381 | 382 | 1. Wait for the next chunk of translated text to be produced, for the translation process to finish, or for the result of calling |stopProducing| to become true. 383 | 384 | 1. If such a chunk is successfully produced: 385 | 386 | 1. Let it be represented as a [=string=] |chunk|. 387 | 388 | 1. Perform |chunkProduced| given |chunk|. 389 | 390 | 1. Otherwise, if the translation process has finished: 391 | 392 | 1. Perform |done|. 393 | 394 | 1. [=iteration/Break=]. 395 | 396 | 1. Otherwise, if |stopProducing| returns true, then [=iteration/break=]. 397 | 398 | 1. Otherwise, if an error occurred during translation: 399 | 400 | 1. Let the error be represented as a [=DOMException error information=] |errorInfo| according to the guidance in [[#translator-errors]]. 401 | 402 | 1. Perform |error| given |errorInfo|. 403 | 404 | 1. [=iteration/Break=]. 405 |

406 | 407 |

Usage

408 | 409 |
410 | To measure translator input usage, given: 411 | 412 | * a [=string=] |input|, 413 | * a [=Unicode canonicalized locale identifier=] |sourceLanguage|, 414 | * a [=Unicode canonicalized locale identifier=] |targetLanguage|, and 415 | * an algorithm |stopMeasuring| that takes no arguments and returns a boolean, 416 | 417 | perform the following steps: 418 | 419 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 420 | 421 | 1. Let |inputToModel| be the [=implementation-defined=] string that would be sent to the underlying model in order to [=translate=] |input| from |sourceLanguage| to |targetLanguage|. 422 | 423 |

This might be just |input| itself, if |sourceLanguage| and |targetLanguage| were loaded into the model during initialization. Or it might consist of more, e.g. appropriate quota usage for encoding the languages in question, or some sort of wrapper prompt to a language model. 424 | 425 | If during this process |stopMeasuring| starts returning true, then return null. 426 | 427 | If an error occurs during this process, then return an appropriate [=DOMException error information=] according to the guidance in [[#translator-errors]]. 428 | 429 | 1. Return the amount of input usage needed to represent |inputToModel| when given to the underlying model. The exact calculation procedure is [=implementation-defined=], subject to the following constraints. 430 | 431 | The returned input usage must be nonnegative and finite. It must be 0, if there are no usage quotas for the translation process (i.e., if the [=Translator/input quota=] is +∞). Otherwise, it must be positive and should be roughly proportional to the [=string/length=] of |inputToModel|. 432 | 433 |

This might be the number of tokens needed to represent |input| in a language model tokenization scheme, or it might be |input|'s [=string/length=]. It could also be some variation of these which also counts the usage of any prefixes or suffixes necessary to give to the model. 434 | 435 | If during this process |stopMeasuring| starts returning true, then instead return null. 436 | 437 | If an error occurs during this process, then instead return an appropriate [=DOMException error information=] according to the guidance in [[#translator-errors]]. 438 |

439 | 440 |

Errors

441 | 442 | When translation fails, the following possible reasons may be surfaced to the web developer. This table lists the possible {{DOMException}} [=DOMException/names=] and the cases in which an implementation should use them: 443 | 444 | 445 | 446 | 447 | 450 | 451 | 455 | 459 |
{{DOMException}} [=DOMException/name=] 448 | Scenarios 449 |
"{{NotAllowedError}}" 452 | 453 |

Translation is disabled by user choice or user agent policy. 454 |

"{{NotReadableError}}" 456 | 457 |

The translation output was filtered by the user agent, e.g., because it was detected to be harmful, inaccurate, or nonsensical. 458 |

"{{UnknownError}}" 460 | 461 |

All other scenarios, including if the user agent believes it cannot translate and also meet the requirements given in [[WRITING-ASSISTANCE-APIS#privacy]] and [[WRITING-ASSISTANCE-APIS#security]]. Or, if the user agent would prefer not to disclose the failure reason. 462 |

463 | 464 |

This table does not give the complete list of exceptions that can be surfaced by the translator API. It only contains those which can come from certain [=implementation-defined=] steps. 465 | 466 |

Permissions policy integration

467 | 468 | Access to the translator API is gated behind the [=policy-controlled feature=] "translator", which has a [=policy-controlled feature/default allowlist=] of [=default allowlist/'self'=]. 469 | 470 |

The language detector API

471 | 472 | 473 | [Exposed=Window, SecureContext] 474 | interface LanguageDetector { 475 | static Promise<LanguageDetector> create( 476 | optional LanguageDetectorCreateOptions options = {} 477 | ); 478 | static Promise<Availability> availability( 479 | optional LanguageDetectorCreateCoreOptions options = {} 480 | ); 481 | 482 | Promise<sequence<LanguageDetectionResult>> detect( 483 | DOMString input, 484 | optional LanguageDetectorDetectOptions options = {} 485 | ); 486 | 487 | readonly attribute FrozenArray<DOMString>? expectedInputLanguages; 488 | 489 | Promise<double> measureInputUsage( 490 | DOMString input, 491 | optional LanguageDetectorDetectOptions options = {} 492 | ); 493 | readonly attribute unrestricted double inputQuota; 494 | }; 495 | LanguageDetector includes DestroyableModel; 496 | 497 | dictionary LanguageDetectorCreateCoreOptions { 498 | sequence<DOMString> expectedInputLanguages; 499 | }; 500 | 501 | dictionary LanguageDetectorCreateOptions : LanguageDetectorCreateCoreOptions { 502 | AbortSignal signal; 503 | CreateMonitorCallback monitor; 504 | }; 505 | 506 | dictionary LanguageDetectorDetectOptions { 507 | AbortSignal signal; 508 | }; 509 | 510 | dictionary LanguageDetectionResult { 511 | DOMString detectedLanguage; 512 | double confidence; 513 | }; 514 | 515 | 516 |

Creation

517 | 518 |
519 | The static create(|options|) method steps are: 520 | 521 | 1. Return the result of [=creating an AI model object=] given |options|, "{{language-detector}}", [=validate and canonicalize language detector options=], [=compute language detector options availability=], [=download the language detector model=], [=initialize the language detector model=], and [=create the language detector object=]. 522 |
523 | 524 |
525 | To validate and canonicalize language detector options given a {{LanguageDetectorCreateCoreOptions}} |options|, perform the following steps. They mutate |options| in place to canonicalize language tags, and throw an exception if any are invalid. 526 | 527 | 1. [=Validate and canonicalize language tags=] given |options| and "{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}". 528 |
529 | 530 |
531 | To download the language detector model, given a {{LanguageDetectorCreateCoreOptions}} |options|: 532 | 533 | 1. [=Assert=]: these steps are running [=in parallel=]. 534 | 535 | 1. Initiate the download process for everything the user agent needs to detect the languages of input text, including all the languages in |options|["{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}"]. 536 | 537 | This could include both a base language detection model, and specific fine-tunings or other material to help with the languages identified in |options|["{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}"]. 538 | 539 | 1. If the download process cannot be started for any reason, then return false. 540 | 541 | 1. Return true. 542 |
543 | 544 |
545 | To initialize the language detector model, given a {{LanguageDetectorCreateCoreOptions}} |options|: 546 | 547 | 1. [=Assert=]: these steps are running [=in parallel=]. 548 | 549 | 1. Perform any necessary initialization operations for the AI model backing the user agent's capabilities for detecting the languages of input text. 550 | 551 | This could include loading the model into memory, or loading any fine-tunings necessary to support the languages identified in |options|["{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}"]. 552 | 553 | 1. If initialization failed for any reason, then return a [=DOMException error information=] whose [=DOMException error information/name=] is "{{OperationError}}" and whose [=DOMException error information/details=] contain appropriate detail. 554 | 555 | 1. Return null. 556 |
557 | 558 |
559 | To create the language detector object, given a [=ECMAScript/realm=] |realm| and a {{LanguageDetectorCreateCoreOptions}} |options|: 560 | 561 | 1. [=Assert=]: these steps are running on |realm|'s [=ECMAScript/surrounding agent=]'s [=agent/event loop=]. 562 | 563 | 1. Let |inputQuota| be the amount of input quota that is available to the user agent for future [=detect languages|language detection=] operations. (This value is [=implementation-defined=], and may be +∞ if there are no specific limits beyond, e.g., the user's memory, or the limits of JavaScript strings.) 564 | 565 | 1. Return a new {{LanguageDetector}} object, created in |realm|, with 566 | 567 |
568 | : [=LanguageDetector/expected input languages=] 569 | :: the result of [=creating a frozen array=] given |options|["{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}"] if it [=set/is empty|is not empty=]; otherwise null 570 | 571 | : [=LanguageDetector/input quota=] 572 | :: |inputQuota| 573 |
574 |
575 | 576 |

Availability

577 | 578 |
579 | The static availability(|options|) method steps are: 580 | 581 | 1. Return the result of [=computing AI model availability=] given |options|, "{{language-detector}}", [=validate and canonicalize language detector options=], and [=compute language detector options availability=]. 582 |
583 | 584 |
585 | To compute language detector options availability given a {{LanguageDetectorCreateCoreOptions}} |options|, perform the following steps. They return either an {{Availability}} value or null, and they mutate |options| in place to update language tags to their best-fit matches. 586 | 587 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 588 | 589 | 1. If there is some error attempting to determine what language detection capabilities the user agent [=model availability/can support=], which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null. 590 | 591 | 1. Let |partition| be the result of [=getting the language availabilities partition=] given the purpose of detecting text written in that language. 592 | 593 | 1. Return the result of [=computing language availability=] given |options|["{{LanguageDetectorCreateCoreOptions/expectedInputLanguages}}"] and |partition|. 594 |
595 | 596 |

The {{LanguageDetector}} class

597 | 598 | Every {{LanguageDetector}} has an expected input languages, a {{FrozenArray}}<{{DOMString}}> or null, set during creation. 599 | 600 | Every {{LanguageDetector}} has an input quota, a number, set during creation. 601 | 602 |
603 | 604 | The expectedInputLanguages getter steps are to return [=this=]'s [=LanguageDetector/expected input languages=]. 605 | 606 | The inputQuota getter steps are to return [=this=]'s [=LanguageDetector/input quota=]. 607 | 608 |
609 | 610 |
611 | The detect(|input|, |options|) method steps are: 612 | 613 | 1. Let |global| be [=this=]'s [=relevant global object=]. 614 | 615 | 1. [=Assert=]: |global| is a {{Window}} object. 616 | 617 | 1. If |global|'s [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}. 618 | 619 | 1. Let |signals| be « [=this=]'s [=DestroyableModel/destruction abort controller=]'s [=AbortController/signal=] ». 620 | 621 | 1. If |options|["`signal`"] [=map/exists=], then [=set/append=] it to |signals|. 622 | 623 | 1. Let |compositeSignal| be the result of [=creating a dependent abort signal=] given |signals| using {{AbortSignal}} and [=this=]'s [=relevant realm=]. 624 | 625 | 1. If |compositeSignal| is [=AbortSignal/aborted=], then return [=a promise rejected with=] |compositeSignal|'s [=AbortSignal/abort reason=]. 626 | 627 | 1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=]. 628 | 629 | 1. Let |abortedDuringOperation| be false. 630 | 631 |

This variable will be written to from the [=event loop=], but read from [=in parallel=]. 632 | 633 | 1. [=AbortSignal/add|Add the following abort steps=] to |compositeSignal|: 634 | 635 | 1. Set |abortedDuringOperation| to true. 636 | 637 | 1. [=Reject=] |promise| with |compositeSignal|'s [=AbortSignal/abort reason=]. 638 | 639 | 1. Let |inputQuota| be [=this=]'s [=LanguageDetector/input quota=]. 640 | 641 | 1. [=In parallel=]: 642 | 643 | 1. Let |stopProducing| be the following steps: 644 | 645 | 1. Return |abortedDuringOperation|. 646 | 647 | 1. Let |result| be the result of [=detecting languages=] given |input|, |inputQuota|, and |stopProducing|. 648 | 649 | 1. [=Queue a global task=] on the [=AI task source=] given |global| to perform the following steps: 650 | 651 | 1. If |abortedDuringOperation| is true, then abort these steps. 652 | 653 | 1. Otherwise, if |result| is an [=error information=], then [=reject=] |promise| with the result of [=converting error information into an exception object=] given |result|. 654 | 655 | 1. Otherwise: 656 | 657 | 1. [=Assert=]: |result| is a [=list=] of {{LanguageDetectionResult}} dictionaries. (It is not null, since in that case |abortedDuringOperation| would have been true.) 658 | 659 | 1. [=Resolve=] |promise| with |result|. 660 |

661 | 662 |
663 | The measureInputUsage(|input|, |options|) method steps are: 664 | 665 | 1. Let |measureUsage| be an algorithm step which takes argument |stopMeasuring|, and returns the result of [=measuring language detector input usage=] given |input| and |stopMeasuring|. 666 | 667 | 1. Return the result of [=measuring AI model input usage=] given [=this=], |options|, and |measureUsage|. 668 |
669 | 670 |

Language detection

671 | 672 |

The algorithm

673 | 674 |
675 | To detect languages given a [=string=] |input|, a number |inputQuota|, and an algorithm |stopProducing| that takes no arguments and returns a boolean, perform the following steps. They will return either null, an [=error information=], or a [=list=] of {{LanguageDetectionResult}} dictionaries. 676 | 677 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 678 | 679 | 1. Let |requested| be the result of [=measuring language detector input usage=] given |input| and |stopProducing|. 680 | 681 | 1. If |requested| is null or an [=error information=], then return |requested|. 682 | 683 | 1. [=Assert=]: |requested| is a number. 684 | 685 | 1. If |requested| is greater than |inputQuota|, then return a [=quota exceeded error information=] with a [=quota exceeded error information/requested=] of |requested| and a [=quota exceeded error information/quota=] of |inputQuota|. 686 | 687 |

In reality, we expect that implementations will check the input usage against the quota as part of the same call into the model as the language detection itself. The steps are only separated in the specification for ease of understanding. 688 | 689 | 1. Let |partition| be the result of [=getting the language availabilities partition=] given the purpose of detecting text written in that language. 690 | 691 | 1. Let |currentlyAvailableLanguages| be |partition|["{{Availability/available}}"]. 692 | 693 | 1. In an [=implementation-defined=] manner, subject to the following guidelines, let |rawResult| and |unknown| be the result of detecting the languages of |input|. 694 | 695 | |rawResult| must be a [=map=] which has a [=map/key=] for each language in |currentlyAvailableLanguages|. The [=map/value=] for each such key must be a number between 0 and 1. This value must represent the implementation's confidence that |input| is written in that language. 696 | 697 | |unknown| must be a number between 0 and 1 that represents the implementation's confidence that |input| is not written in any of the languages in |currentlyAvailableLanguages|. 698 | 699 | The [=map/values=] of |rawResult|, plus |unknown|, must sum to 1. Each such value, or |unknown|, may be 0. 700 | 701 | If the implementation believes |input| to be written in multiple languages, then it should attempt to apportion the values of |rawResult| and |unknown| such that they are proportionate to the amount of |input| written in each detected language. The exact scheme for apportioning |input| is [=implementation-defined=]. 702 | 703 |

704 |

If |input| is "`tacosを食べる`", the implementation might split this into "`tacos`" and "`を食べる`", and then detect the languages of each separately. The first part might be detected as English with confidence 0.5 and Spanish with confidence 0.5, and the second part as Japanese with confidence 1. The resulting |rawResult| then might be «[ "`en`" → 0.25, "`es`" → 0.25, "`ja`" → 0.5 ]» (with |unknown| set to 0). 705 | 706 |

The decision to split this into two parts, instead of e.g. the three parts "`tacos`", "`を`", and "`食べる`", was an [=implementation-defined=] choice. Similarly, the decision to treat each part as contributing to "half" of the result, instead of e.g. weighting by number of [=code points=], was [=implementation-defined=]. 707 | 708 |

(Realistically, we expect that implementations will split on larger chunks than this, as generally more than 4-5 [=code points=] are necessary for most language detection models.) 709 |

710 | 711 | If |stopProducing| returns true at any point during this process, then return null. 712 | 713 | If an error occurred during language detection, then return an [=error information=] according to the guidance in [[#language-detector-errors]]. 714 | 715 | The detection process must conform to the guidance given in [[WRITING-ASSISTANCE-APIS#privacy]] and [[WRITING-ASSISTANCE-APIS#security]], notably including (but not limited to) [[WRITING-ASSISTANCE-APIS#privacy-user-input]] and [[WRITING-ASSISTANCE-APIS#security-runtime]]. 716 | 717 | 1. [=map/Sort in descending order=] |rawResult| with a less than algorithm which given [=map/entries=] |a| and |b|, returns true if |a|'s [=map/value=] is less than |b|'s [=map/value=]. 718 | 719 | 1. Let |results| be an empty [=list=]. 720 | 721 | 1. Let |cumulativeConfidence| be 0. 722 | 723 | 1. [=map/For each=] |key| → |value| of |rawResult|: 724 | 725 | 1. If |value| is 0, then [=iteration/break=]. 726 | 727 | 1. If |value| is less than |unknown|, then [=iteration/break=]. 728 | 729 | 1. [=list/Append=] «[ "{{LanguageDetectionResult/detectedLanguage}}" → |key|, "{{LanguageDetectionResult/confidence}}" → |value| ]» to |results|. 730 | 731 | 1. Set |cumulativeConfidence| to |cumulativeConfidence| + |value|. 732 | 733 | 1. If |cumulativeConfidence| is greater than or equal to 0.99, then [=iteration/break=]. 734 | 735 | 1. [=Assert=]: 1 − |cumulativeConfidence| is greater than or equal to |unknown|. 736 | 737 | 1. [=Assert=]: If |results|'s [=list/size=] is greater than 0, then |results|[|results|'s [=list/size=] - 1]["{{LanguageDetectionResult/confidence}}"] is greater than or equal to |unknown|. 738 | 739 | 1. [=list/Append=] «[ "{{LanguageDetectionResult/detectedLanguage}}" → "`und`", "{{LanguageDetectionResult/confidence}}" → |unknown| ]» to |results|. 740 | 741 | 1. Return |results|. 742 | 743 |

Languages which are less than 1% likely, or contribute to less than 1% of the text, are considered more likely to be noise and so not worth returning to the web developer. Similarly, if the implementation is less sure about a language than it is about the text not being in any of the languages it knows, that language is probably not worth returning to the web developer. 744 | 745 |

Because of such omitted low-probability results, the sum of all confidence values returned to the web developer could be less than 1. 746 |

747 | 748 |

Usage

749 | 750 |
751 | To measure language detector input usage, given a [=string=] |input| and an algorithm |stopMeasuring| that takes no arguments and returns a boolean, perform the following steps: 752 | 753 | 1. [=Assert=]: this algorithm is running [=in parallel=]. 754 | 755 | 1. Let |inputToModel| be the [=implementation-defined=] string that would be sent to the underlying model in order to [=detect languages=] given |input|. 756 | 757 |

This might be just |input| itself, or it might include some sort of wrapper prompt to a language model. 758 | 759 | If during this process |stopMeasuring| starts returning true, then return null. 760 | 761 | If an error occurs during this process, then return an appropriate [=DOMException error information=] according to the guidance in [[#language-detector-errors]]. 762 | 763 | 1. Return the amount of input usage needed to represent |inputToModel| when given to the underlying model. The exact calculation procedure is [=implementation-defined=], subject to the following constraints. 764 | 765 | The returned input usage must be nonnegative and finite. It must be 0, if there are no usage quotas for the translation process (i.e., if the [=LanguageDetector/input quota=] is +∞). Otherwise, it must be positive and should be roughly proportional to the [=string/length=] of |inputToModel|. 766 | 767 |

This might be the number of tokens needed to represent |input| in a language model tokenization scheme, or it might be |input|'s [=string/length=]. It could also be some variation of these which also counts the usage of any prefixes or suffixes necessary to give to the model. 768 | 769 | If during this process |stopMeasuring| starts returning true, then instead return null. 770 | 771 | If an error occurs during this process, then instead return an appropriate [=DOMException error information=] according to the guidance in [[#language-detector-errors]]. 772 |

773 | 774 |

Errors

775 | 776 | When language detection fails, the following possible reasons may be surfaced to the web developer. This table lists the possible {{DOMException}} [=DOMException/names=] and the cases in which an implementation should use them: 777 | 778 | 779 | 780 | 781 | 784 | 785 | 789 |
{{DOMException}} [=DOMException/name=] 782 | Scenarios 783 |
"{{NotAllowedError}}" 786 | 787 |

Language detection is disabled by user choice or user agent policy. 788 |

"{{UnknownError}}" 790 | 791 |

All other scenarios, including if the user agent believes it cannot detect and also meet the requirements given in [[WRITING-ASSISTANCE-APIS#privacy]] and [[WRITING-ASSISTANCE-APIS#security]]. Or, if the user agent would prefer not to disclose the failure reason. 792 |

793 | 794 |

This table does not give the complete list of exceptions that can be surfaced by the language detector API. It only contains those which can come from certain [=implementation-defined=] steps. 795 | 796 |

Permissions policy integration

797 | 798 | Access to the language detector API is gated behind the [=policy-controlled feature=] "language-detector", which has a [=policy-controlled feature/default allowlist=] of [=default allowlist/'self'=]. 799 | 800 |

Privacy considerations

801 | 802 | Please see [[WRITING-ASSISTANCE-APIS#privacy]] for a discussion of privacy considerations for the translator and language detector APIs. That text was written to apply to all APIs sharing the same infrastructure, as noted in [[#dependencies]]. 803 | 804 |

Security considerations

805 | 806 | Please see [[WRITING-ASSISTANCE-APIS#security]] for a discussion of security considerations for the translator and language detector APIs. That text was written to apply to all APIs sharing the same infrastructure, as noted in [[#dependencies]]. 807 | -------------------------------------------------------------------------------- /security-privacy-questionnaire.md: -------------------------------------------------------------------------------- 1 | # [Self-Review Questionnaire: Security and Privacy](https://w3ctag.github.io/security-questionnaire/) 2 | 3 | > 01. What information does this feature expose, 4 | > and for what purposes? 5 | 6 | This feature exposes two main pieces of information: 7 | 8 | - The availability information for each `(sourceLanguage, targetLanguage)` translation pair, or possible language detection result, so that web developers know what translations and detections are possible and whether they will require the user to download a potentially-large language pack. 9 | 10 | (This information has to be probed for each individual pair or possible language, and the browser can say that a language is unavailable even if it is, for privacy reasons.) 11 | 12 | - The actual results of translations and language detections, which can be dependent on the AI models in use. 13 | 14 | The privacy implications of both of these are discussed, in general terms, [in the _Writing Assistance APIs_ specification](https://webmachinelearning.github.io/writing-assistance-apis/#privacy), which was written to cover all APIs with similar concerns. 15 | 16 | > 02. Do features in your specification expose the minimum amount of information 17 | > necessary to implement the intended functionality? 18 | 19 | We believe so. It's possible that we could remove the exposure of the download status information. However, it would almost certainly be inferrable via timing side-channels. (I.e., if downloading a language pack is required, then the web developer can observe the first translation taking longer.) 20 | 21 | > 03. Do the features in your specification expose personal information, 22 | > personally-identifiable information (PII), or information derived from 23 | > either? 24 | 25 | No. Although it's imaginable that the translation or language detection models could be fine-tuned on PII to give more accurate-to-this-user translations, we intend to disallow this in the specification. 26 | 27 | > 04. How do the features in your specification deal with sensitive information? 28 | 29 | We do not deal with sensitive information. 30 | 31 | > 05. Do the features in your specification introduce state 32 | > that persists across browsing sessions? 33 | 34 | Yes. The downloading of language packs and translation or language detection models persists across browsing sessions. 35 | 36 | > 06. Do the features in your specification expose information about the 37 | > underlying platform to origins? 38 | 39 | Possibly. If a browser does not bundle its own models, but instead uses the operating system's functionality, it is possible for a web developer to infer information about such operating system functionality. 40 | 41 | > 07. Does this specification allow an origin to send data to the underlying 42 | > platform? 43 | 44 | Possibly. Again, in the scenario where translation is done via operating system functionality, such data would pass through OS libraries. 45 | 46 | > 08. Do features in this specification enable access to device sensors? 47 | 48 | No. 49 | 50 | > 09. Do features in this specification enable new script execution/loading 51 | > mechanisms? 52 | 53 | No. 54 | 55 | > 10. Do features in this specification allow an origin to access other devices? 56 | 57 | No. 58 | 59 | > 11. Do features in this specification allow an origin some measure of control over 60 | > a user agent's native UI? 61 | 62 | No. 63 | 64 | > 12. What temporary identifiers do the features in this specification create or 65 | > expose to the web? 66 | 67 | None. 68 | 69 | > 13. How does this specification distinguish between behavior in first-party and 70 | > third-party contexts? 71 | 72 | We use permissions policy to disallow the usage of these features by default in third-party (cross-origin) contexts. However, the top-level site can delegate to cross-origin iframes. 73 | 74 | Otherwise, some of the possible [anti-fingerprinting mitigations](https://webmachinelearning.github.io/writing-assistance-apis/#privacy-availability) involve partitioning information across sites, which is kind of like distinguishing between first- and third-party contexts. 75 | 76 | > 14. How do the features in this specification work in the context of a browser’s 77 | > Private Browsing or Incognito mode? 78 | 79 | We are not yet sure. It is likely that some behavior will be different to deal with the [anti-fingerprinting considerations](./README.md#privacy-considerations). For example, if storage partitioning infrastructure is used, then this would be automatic. 80 | 81 | Another possible area of discussion here is whether cloud-based translation APIs make sense in such modes, or whether they should be disabled. 82 | 83 | > 15. Does this specification have both "Security Considerations" and "Privacy 84 | > Considerations" sections? 85 | 86 | Yes: 87 | 88 | * [Privacy considerations](https://webmachinelearning.github.io/translation-api/#privacy) (delegates to [the corresponding section in _Writing Assistance APIs_](https://webmachinelearning.github.io/writing-assistance-apis/#privacy)) 89 | * [Security considerations](https://webmachinelearning.github.io/translation-api/#security) (delegates to [the corresponding section in _Writing Assistance APIs_](https://webmachinelearning.github.io/writing-assistance-apis/#security)) 90 | 91 | > 16. Do features in your specification enable origins to downgrade default 92 | > security protections? 93 | 94 | No. 95 | 96 | > 17. What happens when a document that uses your feature is kept alive in BFCache 97 | > (instead of getting destroyed) after navigation, and potentially gets reused 98 | > on future navigations back to the document? 99 | 100 | Ideally, nothing special should happen. In particular, `Translator` and `LanguageDetector` objects should still be usable without interruption after navigating back. We'll need to add web platform tests to confirm this, as it's easy to imagine implementation architectures in which keeping these objects alive while the `Document` is in the back/forward cache is difficult. 101 | 102 | (For such implementations, failing to bfcache `Document`s with active `Translator` or `LanguageDetector` objects would a simple way of being spec-compliant.) 103 | 104 | > 18. What happens when a document that uses your feature gets disconnected? 105 | 106 | The methods of the `Translator`/`LanguageDetector` objects will start rejecting with `"InvalidStateError"` `DOMException`s. 107 | 108 | > 19. What should this questionnaire have asked? 109 | 110 | Seems fine. 111 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [110166] 3 | , "contacts": ["cwilso"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------