Skip to content

Commit c8c47a4

Browse files
translator api
1 parent d6f3f10 commit c8c47a4

File tree

5 files changed

+343
-2
lines changed

5 files changed

+343
-2
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<link rel="icon" type="image/png" href="/Demos/built-in-ai/static/images/icon.png" />
8+
<link rel="preconnect" href="https://fonts.googleapis.com">
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10+
<link
11+
href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&display=swap"
12+
rel="stylesheet">
13+
<title>Translator API playground</title>
14+
<link rel="stylesheet" href="/Demos/built-in-ai/static/button.css">
15+
<link rel="stylesheet" href="/Demos/built-in-ai/static/design.css">
16+
<link rel="stylesheet" href="/Demos/built-in-ai/static/spinner.css">
17+
<link rel="stylesheet" href="/Demos/built-in-ai/static/app.css">
18+
19+
<!-- Prompt API Chrome OT -->
20+
<meta http-equiv="origin-trial" content="Avlqu5I+6AJLZzJKJk9CMcGXzrQCJa5rRyXhdfYbAU3hFCnxDmzIuuncmLbbRa2IQ6RfCqMyR5oFpZlalj3SRQEAAABreyJvcmlnaW4iOiJodHRwczovL21pY3Jvc29mdGVkZ2UuZ2l0aHViLmlvOjQ0MyIsImZlYXR1cmUiOiJBSVByb21wdEFQSU11bHRpbW9kYWxJbnB1dCIsImV4cGlyeSI6MTc3NDMxMDQwMH0=">
21+
<!-- Summarizer API Chrome OT -->
22+
<meta http-equiv="origin-trial" content="AnmQ0jmbPIpGfZVA7KzZjGm+eqWhTVbw32p25GY5AUThwYV9mfnlRV9OFcB+rppXIcqjo9EVZo7Twi4azvQmVAIAAABjeyJvcmlnaW4iOiJodHRwczovL21pY3Jvc29mdGVkZ2UuZ2l0aHViLmlvOjQ0MyIsImZlYXR1cmUiOiJBSVN1bW1hcml6YXRpb25BUEkiLCJleHBpcnkiOjE3NjA0MDAwMDB9">
23+
<!-- Writer API Chrome OT -->
24+
<meta http-equiv="origin-trial" content="AkHYvzmVSZSDzntbjLuermqGg6g5qWqmpmOwUlPNQFqqEs0MvwMZUI0f20Lef7fE+tDJDvPq3vOV0dyFk6sDXAYAAABceyJvcmlnaW4iOiJodHRwczovL21pY3Jvc29mdGVkZ2UuZ2l0aHViLmlvOjQ0MyIsImZlYXR1cmUiOiJBSVdyaXRlckFQSSIsImV4cGlyeSI6MTc2OTQ3MjAwMH0=">
25+
<!-- Rewriter API Chrome OT -->
26+
<meta http-equiv="origin-trial" content="As+DG2O8HwYYjnB9v8LvckNE3a00/dyFgYlSGJN6KEi6cxRjvMs2Lq3VxKL/p09haR+lWprzxFcu2zv/QnHTbQQAAABeeyJvcmlnaW4iOiJodHRwczovL21pY3Jvc29mdGVkZ2UuZ2l0aHViLmlvOjQ0MyIsImZlYXR1cmUiOiJBSVJld3JpdGVyQVBJIiwiZXhwaXJ5IjoxNzY5NDcyMDAwfQ==">
27+
</head>
28+
29+
30+
31+
<body>
32+
<header>
33+
<button title="Toggle the menu" class="nav-anchor" popovertarget="menu-popover"><span class="sr-only">Open menu</span></button>
34+
<h2 class="app-title">Built-in AI Playground</h2>
35+
36+
<nav id="menu">
37+
<ul class="menu">
38+
<li>
39+
<a href="/Demos/built-in-ai/playgrounds/prompt-api" class="item">Prompt</a>
40+
</li>
41+
<li>
42+
<span class="item parent">
43+
<a href="/Demos/built-in-ai/playgrounds/summarizer-api">Writing assistance</a>
44+
</span>
45+
<ul class="menu">
46+
<li>
47+
<a href="/Demos/built-in-ai/playgrounds/summarizer-api" class="item">Summarizer</a>
48+
</li>
49+
<li>
50+
<a href="/Demos/built-in-ai/playgrounds/writer-api" class="item">Writer</a>
51+
</li>
52+
<li>
53+
<a href="/Demos/built-in-ai/playgrounds/rewriter-api" class="item">Rewriter</a>
54+
</li>
55+
</ul>
56+
</li>
57+
<!-- li>
58+
<span class="item parent">
59+
Samples
60+
</span>
61+
<ul class="menu">
62+
<li>
63+
<a href="/Demos/built-in-ai/samples/e-commerce" class="item">E-commerce</a>
64+
</li>
65+
<li>
66+
<a href="/Demos/built-in-ai/samples/news" class="item">News</a>
67+
</li>
68+
</ul>
69+
</li -->
70+
</ul>
71+
</nav>
72+
73+
</header>
74+
75+
76+
<nav id="menu-popover" popover>
77+
<ul class="menu">
78+
<li>
79+
<a href="/Demos/built-in-ai/playgrounds/prompt-api" class="item">Prompt</a>
80+
</li>
81+
<li>
82+
<span class="item parent">
83+
<a href="/Demos/built-in-ai/playgrounds/summarizer-api">Writing assistance</a>
84+
</span>
85+
<ul class="menu">
86+
<li>
87+
<a href="/Demos/built-in-ai/playgrounds/summarizer-api" class="item">Summarizer</a>
88+
</li>
89+
<li>
90+
<a href="/Demos/built-in-ai/playgrounds/writer-api" class="item">Writer</a>
91+
</li>
92+
<li>
93+
<a href="/Demos/built-in-ai/playgrounds/rewriter-api" class="item">Rewriter</a>
94+
</li>
95+
</ul>
96+
</li>
97+
<!-- li>
98+
<span class="item parent">
99+
Samples
100+
</span>
101+
<ul class="menu">
102+
<li>
103+
<a href="/Demos/built-in-ai/samples/e-commerce" class="item">E-commerce</a>
104+
</li>
105+
<li>
106+
<a href="/Demos/built-in-ai/samples/news" class="item">News</a>
107+
</li>
108+
</ul>
109+
</li -->
110+
</ul>
111+
</nav>
112+
113+
114+
115+
<link rel="stylesheet" href="/Demos/built-in-ai/static/playground.css">
116+
117+
<link rel="stylesheet" href="/Demos/built-in-ai/static/translator-api.css">
118+
119+
120+
<main>
121+
<h1>Translator API</h1>
122+
<p class="description">Web API for translating text between languages using the browser-provided language model. For more information, check out <a href="https://learn.microsoft.com/microsoft-edge/web-platform/writing-assistance-apis" target="_blank">the API documentation</a>.</p>
123+
124+
<div id="message-ui" class="message-bar"></div>
125+
126+
<div class="settings card-with-shadow">
127+
128+
<div class="settings-row no-separator">
129+
<label for="source-language">Source language</label>
130+
<select name="source-language" id="source-language">
131+
<option value="en" selected>English</option>
132+
<option value="fr">French</option>
133+
<option value="es">Spanish</option>
134+
<option value="de">German</option>
135+
<option value="jp">Japanese</option>
136+
</select>
137+
</div>
138+
139+
<div class="settings-row no-separator">
140+
<label for="input">Text to translate</label>
141+
<textarea spellcheck="false" id="input"></textarea>
142+
</div>
143+
144+
<div class="settings-row no-separator">
145+
<label for="target-language">Target language</label>
146+
<select name="target-language" id="target-language">
147+
<option value="en">English</option>
148+
<option value="fr" selected>French</option>
149+
<option value="es">Spanish</option>
150+
<option value="de">German</option>
151+
<option value="jp">Japanese</option>
152+
</select>
153+
</div>
154+
155+
<div class="settings-row submit">
156+
<button class="ai-button" id="translate">Translate</button>
157+
<button id="stop">Stop</button>
158+
</div>
159+
</div>
160+
161+
<pre id="output"></pre>
162+
163+
<div class="metrics card-with-shadow">
164+
<span class="metric">Initial latency: <span class="value" id="init-latency-metric"></span> ms</span>
165+
<span class="metric">First chunk latency: <span class="value" id="first-chunk-latency-metric"></span> ms</span>
166+
<span class="metric">Chunks: <span class="value" id="chunks-metric"></span></span>
167+
<span class="metric">Rate: <span class="value" id="chunk-rate-metric"></span> chunk/sec</span>
168+
</div>
169+
</main>
170+
171+
<script src="/Demos/built-in-ai/static/session.js"></script>
172+
<script src="/Demos/built-in-ai/static/spinner.js"></script>
173+
<script src="/Demos/built-in-ai/static/metrics.js"></script>
174+
<script src="/Demos/built-in-ai/static/slider.js"></script>
175+
176+
<script src="/Demos/built-in-ai/static/translator-api.js"></script>
177+
178+
179+
</body>
180+
</html>

built-in-ai/static/session.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const ERR_WRITER_MODEL_NOT_AVAILABLE = `The Writer API is enabled, but the model
1414
const ERR_REWRITER_API_NOT_DETECTED = `The Rewriter API is not available. ${WA_DOCS_INSTRUCTIONS}`;
1515
const ERR_REWRITER_MODEL_NOT_AVAILABLE = `The Rewriter API is enabled, but the model download hasn't started yet, maybe awaiting device capability check. ${WA_DOCS_INSTRUCTIONS}`;
1616

17+
const ERR_TRANSLATOR_API_NOT_DETECTED = `The Translator API is not available. ${WA_DOCS_INSTRUCTIONS}`;
18+
const ERR_TRANSLATOR_MODEL_NOT_AVAILABLE = `The Translator API is enabled, but the model download hasn't started yet, maybe awaiting device capability check. ${WA_DOCS_INSTRUCTIONS}`;
19+
1720
const ERR_API_CAPABILITY_ERROR = "Cannot create the session now. API availability error: ";
1821
const ERR_FAILED_CREATING_MODEL = "Could not create the session. Error: ";
1922

@@ -90,6 +93,11 @@ const defaultRewriterSessionOptions = {
9093
format: "as-is", // as-is, plain-text, markdown.
9194
monitor: modelDownloadProgressMonitor
9295
};
96+
const defaultTranslatorSessionOptions = {
97+
sourceLanguage: "en", // The source language code.
98+
targetLanguage: "fr", // The target language code.
99+
monitor: modelDownloadProgressMonitor
100+
};
93101

94102
// These APIs used to be under window.ai, but have then moved to window.
95103
// The following functions return the API object, depending on where it is found.
@@ -121,12 +129,19 @@ function getRewriterAPI() {
121129
throw ERR_REWRITER_API_NOT_DETECTED;
122130
}
123131

132+
function getTranslatorAPI() {
133+
if (window.Translator) return window.Translator;
134+
if (window.ai && window.ai.translator) return window.ai.translator;
135+
displaySessionMessage(ERR_TRANSLATOR_API_NOT_DETECTED, true);
136+
throw ERR_TRANSLATOR_API_NOT_DETECTED;
137+
}
138+
124139
// The following functions check if the APIs and models are available, and display messages to the user.
125140
// You can call these functions when the page loads if you want to display the status
126141
// to the user early, so they know what to expect (e.g. if their browser supports the APIs).
127142
// These functions don't trigger the model download and do not create sessions.
128-
async function checkAPIAvailability(api, modelError) {
129-
const availability = await api.availability();
143+
async function checkAPIAvailability(api, modelError, availabilityOptions) {
144+
const availability = await api.availability(availabilityOptions);
130145

131146
// The API is available, but the model is not.
132147
if (availability === "unavailable") {
@@ -171,6 +186,11 @@ async function checkRewriterAPIAvailability() {
171186
return availability;
172187
}
173188

189+
async function checkTranslatorAPIAvailability(availabilityOptions) {
190+
const availability = await checkAPIAvailability(getTranslatorAPI(), ERR_TRANSLATOR_MODEL_NOT_AVAILABLE, availabilityOptions);
191+
return availability;
192+
}
193+
174194
// The following functions create sessions for the APIs, possibly downloading the models first.
175195
async function getSession(api, defaultOptions, userOptions) {
176196
let session = null;
@@ -209,3 +229,11 @@ async function getRewriterSession(options) {
209229
await checkRewriterAPIAvailability();
210230
return await getSession(getRewriterAPI(), defaultRewriterSessionOptions, options);
211231
}
232+
233+
async function getTranslatorSession(options) {
234+
await checkTranslatorAPIAvailability({
235+
sourceLanguage: options.sourceLanguage,
236+
targetLanguage: options.targetLanguage
237+
});
238+
return await getSession(getTranslatorAPI(), defaultTranslatorSessionOptions, options);
239+
}

built-in-ai/static/translator-api.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#input {
2+
height: 14rem;
3+
max-height: 50vh;
4+
}
5+
6+
#output:empty::before {
7+
content: "The translation will appear here";
8+
}

built-in-ai/static/translator-api.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const inputEl = document.querySelector("#input");
2+
const sourceLanguageEl = document.querySelector("#source-language");
3+
const targetLanguageEl = document.querySelector("#target-language");
4+
const outputEl = document.querySelector("#output");
5+
const translateBtn = document.querySelector("#translate");
6+
const stopBtn = document.querySelector("#stop");
7+
const spinnerEl = createSpinner();
8+
9+
addEventListener("load", async () => {
10+
await checkTranslatorAPIAvailability({
11+
sourceLanguage: "en",
12+
targetLanguage: "fr"
13+
});
14+
let session = null;
15+
16+
let abortController
17+
18+
stopBtn.addEventListener("click", () => {
19+
if (abortController) {
20+
abortController.abort("User stopped the translation.");
21+
}
22+
abortController = null;
23+
session?.destroy();
24+
spinnerEl.remove();
25+
});
26+
27+
translateBtn.addEventListener("click", async () => {
28+
if (inputEl.value === "") {
29+
return;
30+
}
31+
32+
outputEl.textContent = "Translating ...";
33+
outputEl.appendChild(spinnerEl);
34+
35+
// Destroy the previous session, if any.
36+
session?.destroy();
37+
38+
const sourceLanguage = sourceLanguageEl.value;
39+
const targetLanguage = targetLanguageEl.value;
40+
console.log(`Translating from ${sourceLanguage} to ${targetLanguage}.`);
41+
42+
const metrics = new PlaygroundMetrics();
43+
metrics.signalOnBeforeCreateSession();
44+
45+
// Create a new session.
46+
try {
47+
session = await getTranslatorSession({
48+
sourceLanguage,
49+
targetLanguage
50+
});
51+
} catch (e) {
52+
displaySessionMessage(`Could not create the Translator session: ${e}`, true);
53+
console.error(e);
54+
spinnerEl.remove();
55+
return;
56+
}
57+
58+
metrics.signalOnAfterSessionCreated();
59+
60+
try {
61+
abortController = new AbortController();
62+
const stream = session.translateStreaming(inputEl.value, {
63+
signal: abortController.signal
64+
});
65+
66+
metrics.signalOnBeforeStream();
67+
68+
let isFirstChunk = true;
69+
for await (const chunk of stream) {
70+
if (isFirstChunk) {
71+
spinnerEl.remove();
72+
isFirstChunk = false;
73+
outputEl.textContent = "";
74+
}
75+
76+
metrics.signalOnStreamChunk();
77+
78+
outputEl.textContent += chunk;
79+
}
80+
} catch (e) {
81+
displaySessionMessage(`Could not translate the text: ${e}`, true);
82+
console.error(e);
83+
spinnerEl.remove();
84+
}
85+
});
86+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
layout: playground.njk
3+
title: Translator API playground
4+
shorttitle: Translator API
5+
description: Web API for translating text between languages using the browser-provided language model. For more information, check out <a href="https://learn.microsoft.com/microsoft-edge/web-platform/writing-assistance-apis" target="_blank">the API documentation</a>.
6+
stylesheet: translator-api.css
7+
script: translator-api.js
8+
permalink: translator-api/
9+
---
10+
11+
<div class="settings-row no-separator">
12+
<label for="source-language">Source language</label>
13+
<select name="source-language" id="source-language">
14+
<option value="en" selected>English</option>
15+
<option value="fr">French</option>
16+
<option value="es">Spanish</option>
17+
<option value="de">German</option>
18+
</select>
19+
</div>
20+
21+
<div class="settings-row no-separator">
22+
<label for="input">Text to translate</label>
23+
<textarea spellcheck="false" id="input"></textarea>
24+
</div>
25+
26+
<div class="settings-row no-separator">
27+
<label for="target-language">Target language</label>
28+
<select name="target-language" id="target-language">
29+
<option value="en">English</option>
30+
<option value="fr" selected>French</option>
31+
<option value="es">Spanish</option>
32+
<option value="de">German</option>
33+
</select>
34+
</div>
35+
36+
<div class="settings-row submit">
37+
<button class="ai-button" id="translate">Translate</button>
38+
<button id="stop">Stop</button>
39+
</div>

0 commit comments

Comments
 (0)