-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtranslate.html
More file actions
134 lines (113 loc) · 4.13 KB
/
Copy pathtranslate.html
File metadata and controls
134 lines (113 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Translate</title>
<link rel="stylesheet" type="text/css" href="main.css" />
<style>
#loading progress {
width: 16em;
margin-top: 0.5em;
}
</style>
</head>
<body>
<header>
<h1><a href="index.html">Tools</a></h1>
</header>
<main>
<header class="bar">
<h2>Translate</h2>
<div class="group">
<select id="direction">
<option value="en-nl">English → Dutch</option>
<option value="nl-en">Dutch → English</option>
</select>
<button id="switch" type="button">Switch</button>
</div>
</header>
<div class="placeholder-text" id="loading">
<p id="loading-label">Downloading translation models...</p>
<progress id="loading-bar" value="0" max="100"></progress>
</div>
<div id="ui" hidden>
<textarea id="input" placeholder="Enter text..." rows="7"></textarea>
<textarea id="output" readonly placeholder="Translation..." rows="7" style="margin-top: 0.5em;"></textarea>
</div>
</main>
<script type="module">
import { pipeline, env } from '/vendor/transformers.js';
env.backends.onnx.wasm.wasmPaths = '/vendor/';
// Load only the vendored weights; never reach out to the HF hub.
env.allowLocalModels = true;
env.allowRemoteModels = false;
env.localModelPath = '/vendor/models/';
const MODELS = {
'en-nl': 'Xenova/opus-mt-en-nl',
'nl-en': 'Xenova/opus-mt-nl-en',
};
const loading = document.getElementById('loading');
const loadingLabel = document.getElementById('loading-label');
const loadingBar = document.getElementById('loading-bar');
const ui = document.getElementById('ui');
const direction = document.getElementById('direction');
const input = document.getElementById('input');
const output = document.getElementById('output');
let translator = null;
let debounceTimer = null;
let loadGen = 0;
async function loadModel() {
const gen = ++loadGen;
translator = null;
loading.hidden = false;
ui.hidden = true;
loadingBar.hidden = true;
loadingBar.value = 0;
loadingLabel.textContent = 'Loading model...';
const fileProgress = {};
const result = await pipeline('translation', MODELS[direction.value], {
dtype: 'q8', // keep in sync with the vendored variant in the Bakefile
progress_callback: ({ status, file, progress }) => {
if (gen !== loadGen) return;
if (status == 'progress' && file) {
loadingLabel.textContent = 'Downloading translation models...';
loadingBar.hidden = false;
fileProgress[file] = progress;
const values = Object.values(fileProgress);
loadingBar.value = values.reduce((a, b) => a + b, 0) / values.length;
}
},
});
if (gen !== loadGen) return;
translator = result;
loading.hidden = true;
ui.hidden = false;
input.focus();
}
async function translate() {
if (!translator || !input.value.trim()) {
output.value = '';
return;
}
const result = await translator(input.value);
output.value = result[0].translation_text;
}
direction.onchange = () => loadModel().then(translate);
input.oninput = () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(translate, 400);
};
document.getElementById('switch').onclick = () => {
direction.value = direction.value == 'en-nl' ? 'nl-en' : 'en-nl';
const tmp = input.value;
input.value = output.value;
output.value = tmp;
loadModel().then(translate);
};
loadModel();
</script>
<script src="/vendor/instant-page.js" type="module"></script>
</body>
</html>