This commit is contained in:
Andre Natal 2016-12-02 01:21:36 -08:00
Родитель 4feaf4fea4
Коммит 2a310975e2
13 изменённых файлов: 691 добавлений и 198 удалений

Просмотреть файл

@ -89,6 +89,7 @@ function startServer() {
var https = require('spdy');
var express = require('express');
var bodyParser = require('body-parser');
var sqlite3 = require('sqlite3').verbose();
// Read the server configuration file. It must define
// letsEncryptHostname and letsEncryptEmailAddress for the
@ -102,6 +103,8 @@ function startServer() {
process.exit(1);
}
var db = new sqlite3.Database(config.db);
var lex = LEX.create({
configDir: __dirname + '/letsencrypt.conf',
approveRegistration: function (hostname, approve) {
@ -135,6 +138,7 @@ function startServer() {
// This is how we handle WAV file uploads
app.post('/upload/:dir', function(request, response) {
var uid = request.headers['uid'];
var dir = request.params.dir;
var filenumber = directoryToFileNumber[dir];
if (filenumber !== undefined) { // Only if it is a known directory
@ -147,7 +151,7 @@ function startServer() {
extension = '.webm'; // Chrome gives us opus in webm
}
var path = uploaddir + '/' + dir + '/' + filename + extension;
var path = uploaddir + '/' + dir + '/' + uid + extension;
fs.writeFile(path, request.body, {}, function(err) {
response.send('Thanks for your contribution!');
if (err) {
@ -163,6 +167,16 @@ function startServer() {
}
});
app.get('/data/', function(request,response) {
db.serialize(function() {
var id = Math.random() * Date.now() * (request.headers.gender + request.headers.age + request.headers.langs1 + request.headers.langs2);
var stmt = db.prepare("INSERT INTO usr VALUES (?,?,?,?,?)");
stmt.run(id, request.headers.gender, request.headers.age, request.headers.langs1, request.headers.langs2);
stmt.finalize();
response.send({ uid: id });
});
});
// In test mode, just run the app over http to localhost:8000
if (process.argv[2] === 'test') {
app.listen(8000, function() {
@ -176,7 +190,7 @@ function startServer() {
res.setHeader('Location', 'https://' + req.headers.host + req.url);
res.statusCode = 302;
res.end('<!-- Please use https:// links instead -->');
})).listen(config.httpPort || 80);
})).listen(config.httpPort || 8080);
// Handle HTTPs requests using LEX and the Express app defined above

Просмотреть файл

@ -10,12 +10,13 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "David Flanagan",
"author": "David Flanagan and Andre Natal",
"license": "MPL-2.0",
"dependencies": {
"body-parser": "^1.15.0",
"express": "^4.13.4",
"letsencrypt-express": "^1.1.5",
"spdy": "^3.3.2"
"spdy": "^3.3.2",
"sqlite3": "^3.1.8"
}
}

Двоичные данные
public/imgs/15xvbd5.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 174 B

Двоичные данные
public/imgs/CheckMark-off.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 420 B

Двоичные данные
public/imgs/CheckMark-on.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 488 B

Двоичные данные
public/imgs/Circle.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 485 B

Двоичные данные
public/imgs/Square.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 165 B

Двоичные данные
public/imgs/Triangle-09-off.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 300 B

Двоичные данные
public/imgs/Triangle-09-on.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 350 B

Двоичные данные
public/imgs/wordmark_project-voicebank-gray.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Просмотреть файл

@ -12,82 +12,61 @@ body {
font-family: 'Fira Sans';
}
.header.titlevb {
margin: 20px 0px 0px 40px;
font-weight: 400;
font-size: x-large;
color: rgb(105,105,105);
}
.header.titlemz {
margin: 0px 0px 20px 40px;
font-weight: 200;
color: rgb(147,149,152);
.soflow {
-webkit-appearance: button;
-webkit-border-radius: 2px;
-webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
-webkit-padding-end: 20px;
-webkit-padding-start: 2px;
-webkit-user-select: none;
-moz-appearance: none;
background-image: url(imgs/15xvbd5.png);
background-position: 97% center;
background-repeat: no-repeat;
border: 1px solid #AAA;
color: #000;
font-size: inherit;
padding: 5px 30px 5px 5px;
text-overflow: ellipsis;
white-space: nowrap;
width: 80%;
text-align: left;
}
hr {
background-color: rgb(147,149,152);
height: 1px;
border: 0;
background-color: rgb(147,149,152);
height: 1px;
border: 0;
}
#main_div {
margin: 20px auto 0 auto; /* center body within window */
max-width: 480px; /* no wider than 480px, narrower on phones */
margin: 20px auto 0 auto; /* center body within window */
max-width: 480px; /* no wider than 480px, narrower on phones */
}
.title1{
font-weight: 600;
color: rgb(87,88,90);
font-size: large;
margin: 30px auto 20px auto; /* center body within window */
font-weight: 600;
color: rgb(87,88,90);
font-size: large;
margin: 30px auto 20px auto; /* center body within window */
}
.title2{
font-weight: 600;
color: rgb(87,88,90);
font-size: large;
margin: 30px auto 20px auto; /* center body within window */
font-weight: 600;
color: rgb(87,88,90);
font-size: large;
margin: 30px auto 20px auto; /* center body within window */
}
p {
color: rgb(115,116,117);
font-weight: 200;
margin: 0px 0px 30px 0px; /* center body within window */
color: rgb(115,116,117);
font-weight: 200;
margin: 0px 0px 30px 0px; /* center body within window */
}
label{
color: rgb(115,116,117);
font-weight: 200;
}
#tbldata {
display: table;
width: 100%;
font-weight: 300;
}
#tbldata div span {
display: table-cell;
padding: 5px 5px 5px 0px;
width: 1%;
}
#tbldata2 {
display: table;
width: 100%;
font-weight: 300;
}
#tbldata2 div {
display: table-row;
}
#tbldata2 div span {
display: table-cell;
padding: 5px 5px 5px 0px;
width: 1%;
color: rgb(115,116,117);
font-weight: 200;
}
#consent-screen button {
@ -114,11 +93,101 @@ table td {
}
#sentence {
font-size: larger;
font-weight: 400;
margin: 30px 0 0 5%;
text-align: center;
width: 90%;
font-size: larger;
font-weight: 400;
margin: 30px 0 0 5%;
text-align: center;
width: 90%;
}
#sensitivity {
width: 80%;
display: none;
}
#player-box{
border: solid 1px;
border-color: rgb(200,201,204);
}
#player {
display: none;
}
#playback-screen button {
font-size: 30px;
margin: 10px;
padding: 10px;
}
#playback-screen button span.small {
font-size: 18px;
}
#levels {
width: 100px;
height: 100px;
}
#divlevels {
width: 100px;
height: 100px;
margin: 20px 60%;
}
#elapsedtime {
margin: 30px 0px 0px 20%;
float: left;
font-size: xx-large;
}
.screen {
margin: 0 10px 0 10px;
}
a div span {
display: table-cell;
padding: 5px 5px 5px 0px;
width: 1%;
}
#tbldata2 {
display: table;
width: 100%;
font-weight: 300;
}
#tbldata2 div {
display: table-row;
}
#tbldata2 div span {
display: table-cell;
padding: 5px 5px 5px 0px;
}
#consent-screen button {
font-size: 18px;
}
#error-message {
margin: 15px;
font-style: italic;
}
table {
border-collapse:collapse;
margin:10px auto 0 auto;
}
table th {
background-color: #ccc;
padding: 3px;
border: solid black 1px;
}
table td {
padding: 3px 5px 3px 5px;
border: solid black 1px;
}
#container {
@ -133,36 +202,53 @@ table td {
border: solid rgb(200,201,204) 1px;
text-align:center;
margin:2%;
padding:20px;
padding: 10px 20px 10px 20px;
cursor: pointer;
}
.btn {
display: inline-block;
vertical-align: top;
border: solid rgb(101,206,245) 1px;
text-align:center;
color: rgb(101,206,245);
padding:10px 30px 10px 30px;
margin: 0 35%;
cursor: pointer;
display: inline-block;
vertical-align: top;
border: solid rgb(101,206,245) 1px;
text-align:center;
color: rgb(101,206,245);
padding: 10px 30px 10px 30px;
margin: 0 35%;
cursor: pointer;
}
.btn.continuedata {
margin: 0 70%;
margin: 10px 20% auto;
padding: 10px 30px 10px 30px;
text-align: right;
float: right;
}
#recording-indicator {
.recording-indicator {
display:inline-block;
width:15px;
height:15px;
background:red;
border-radius:50%;
opacity:0;
float:left;
}
body.recording #recording-indicator {
.stopped-indicator {
display:inline-block;
width:15px;
height:15px;
background: rgb(190,29,44);
border-radius:50%;
float:left;
}
#errorfill{
color: red;
}
body.recording .recording-indicator {
animation-name:blink;
animation-duration:1s;
animation-iteration-count:infinite;
@ -180,69 +266,56 @@ body.recording #recording-indicator {
}
}
#sensitivity {
width: 80%;
display: none;
#lblplay {
margin: 0 0 0 10px;
float:left;
color: rgb(188,189,192);
}
#player-box{
border: solid 1px;
border-color: rgb(200,201,204);
#lblsubmit {
margin: 0 0 0 4px;
float:right;
color: rgb(188,189,192);
}
#player {
display: none;
#lblrecord {
margin: 0 0 0 10px;
float:left;
color: rgb(190,29,44);
}
.textbox{
width: 100%;
height: 30px;
}
#playback-screen button {
font-size: 30px;
.wordmark {
width: auto;
height: 50px;
margin: 10px;
padding: 10px;
}
#playback-screen button span.small {
font-size: 18px;
.footeritem{
float: right;
margin: 10px 10px 10px 10px;
font-weight: 400;
font-size: small;
}
.ctltblright{
width : 100%;
}
.ctltblleft{
width : 80%;
.footer{
position:fixed;
bottom:0;
height:auto;
width:100%;
}
#levels {
width: 100px;
height: 100px;
a{
text-decoration: none;
color: black;
}
#divlevels {
width: 100px;
height: 100px;
margin: 20px 60%;
a:visited {
color: black;
}
#elapsedtime {
margin: 30px 0px 0px 20%;
float: left;
font-size: xx-large;
}
.screen {
margin: 0 10px 0 10px;
}
</style>
</head>
<body>
<div class="header titlevb">Project VoiceBank</div>
<div class="header titlemz">powered by Mozilla</div>
<div ><img class="wordmark" src="imgs/wordmark_project-voicebank-gray.png"></div>
<hr/>
<div id="main_div">
<div id="consent-screen" class="screen" hidden>
@ -257,6 +330,8 @@ body.recording #recording-indicator {
This website is used by Mozilla engineering to collect speech samples to
test and train our speech recognition engine and develop or improve other new
products or services. We may also share these voice samples with third parties who are researching topics related to speech recognition or speech synthesis.
<p> By clicking the "I agree" button below, you are agreeing to donate audio recordings of your voice and to place them in the public domain. This means that you agree to waive all rights to the recordings worldwide under copyright and database law, including moral and publicity rights and all related and neighboring rights.
</p>
<div id="agree" class="btn">I Agree</div>
@ -268,62 +343,53 @@ body.recording #recording-indicator {
A Little About You.
</div>
<p>Please fill out the information below to help categorize your voice.</p>
<div id="tbldata">
<div>
<span>
User ID
</span>
</div>
<div>
<span>
<input type="text" id="tbxuserid" class="textbox">
</span>
</div>
</div>
<div id="tbldata2">
<div id="tbldata2">
<div>
<span>Gender</span>
<span>Date of Birth</span>
</div>
<div>
<span>
<input type="text" class="ctltblleft">
<select class="soflow" id="gender">
<option>Select one</option>
<option>Male</option>
<option>Female</option>
</select>
</span>
<span>
<input type="text" class="ctltblright">
<select class="soflow" id="age">
<option>Select one</option>
<option>14-18</option>
<option>19-25</option>
<option>26-30</option>
<option>31-40</option>
<option>41-50</option>
<option>51-60</option>
<option>+60</option>
</select>
</span>
</div>
<div>
<span>Primary Language</span>
<span>Secondary Language (Optional)</span>
<span>Secondary Language (optional)</span>
</div>
<div>
<span>
<input type="text" class="ctltblleft">
<select class="soflow" id="langs1"></select>
</span>
<span>
<input type="text" class="ctltblright">
<select class="soflow" id="langs2"></select>
</span>
</div>
<div>
<span>Accent</span>
<span></span>
</div>
<div>
<span>
<input type="text" id="tbxaccent" class="ctltblleft">
</span>
<span>
<span></span>
<span>
<div id="datasubmit" class="btn continuedata">Continue</div>
</span>
</div>
</div>
<div id="datasubmit" class="btn continuedata">Continue</div>
</div>
<span id="errorfill" hidden>Please, fill the form before continue.</span>
</div>
<div id="record-screen" class="screen" hidden>
<div id="instructions">
@ -334,9 +400,21 @@ body.recording #recording-indicator {
<div id="player-box">
<br>
<div id="container">
<div id="recordButton"><div id="recording-indicator"></div> Record</div>
<div id="playButton">Play</div>
<div id="uploadButton">Submit</div>
<div id="recordButton">
<div id="divanim" class="stopped-indicator"></div>
<div id="lblrecord"> Record </div></div>
<div id="playButton">
<div style="float:left">
<img id="playimg" src="imgs/Triangle-09-off.png">
</div>
<div id="lblplay">Play</div>
</div>
<div id="uploadButton">
<div style="float:left;">
<img id="submitimg" src="imgs/CheckMark-off.png">
</div>
<div id="lblsubmit">Submit</div>
</div>
</div>
<div id="sentence" ></div>
<div>
@ -344,13 +422,12 @@ body.recording #recording-indicator {
<div id="divlevels"> <canvas hidden id="levels" width=500 height=100></canvas></div>
</div>
</div>
</div><br>
<p class="submitted" id="submitted">Submitted Recordings this Session: 0</p>
<!-- hidden stuff -->
<input id="sensitivity" type="range" min="1" max="200"></input>
<audio id="player" controls></audio><br/>
</div>
<div id="error-screen" class="screen" hidden>
<h1>VoiceBank</h1>
<p>
@ -374,5 +451,14 @@ body.recording #recording-indicator {
</p>
</div>
</div>
<div class="footer">
<hr/>
<div class="footeritem"><a href="https://www.mozilla.org/en-US/about/legal/" target="_blank">Legal</a></div>
<div class="footeritem"><a href="https://www.mozilla.org/en-US/privacy/websites/" target="_blank">Privacy</a></div>
<div class="footeritem"><a href="https://www.mozilla.org/en-US/about/legal/terms/mozilla/" target="_blank">Terms</a></div>
<div class="footeritem"><a href="https://www.mozilla.org/en-US/privacy/websites/#cookies" target="_blank">Cookies</a></div>
</div>
</body>
</html>

Просмотреть файл

@ -28,10 +28,14 @@ var ERR_NO_MIC = 'You did not allow this website to use the microphone. ' +
'The website needs the microphone to record your voice.';
var ERR_UPLOAD_FAILED = 'Uploading your recording to the server failed. ' +
'This may be a temporary problem. Please try again.';
var ERR_DATA_FAILED = 'Submitting your profile data failed. ' +
'This may be a temporary problem. Please try again.';
// This is the program startup sequence.
checkPlatformSupport()
.then(getConsent)
.then(getLangs)
.then(populatelangs)
.then(getUserInfo)
.then(getMicrophone)
.then(rememberMicrophone)
@ -98,10 +102,38 @@ function getUserInfo(){
// Otherwise, display the data screen and wait for a response
var dataScreen = document.querySelector('#data-screen');
dataScreen.hidden = false;
document.querySelector('#datasubmit').onclick = function() {
localStorage.getUserInfoGiven = true; // Remember profile was already sent
dataScreen.hidden = true;
resolve();
document.querySelector("#datasubmit").onclick = function() {
// validate that all controls are selected
var controls = ["#langs1", "#langs2", "#gender", "#age"];
for (var ctl in controls){
if (document.querySelector(controls[ctl]).selectedIndex <= 0) {
document.querySelector("#errorfill").hidden = false;
return;
}
}
var headers = new Headers();
headers.append("gender", document.querySelector('#gender').selectedIndex);
headers.append("age", document.querySelector('#age').selectedIndex);
headers.append("langs1", document.querySelector('#langs1').selectedIndex);
headers.append("langs2", document.querySelector('#langs2').selectedIndex);
fetch('/data', { method: 'GET', headers: headers})
.then(function(response) {
if (response.status !== 200) {
displayErrorMessage(ERR_DATA_FAILED + ' ' + response.status + ' ' +
response.statusText);
} else {
response.text().then(function(text) {
localStorage.getUserInfoGiven = JSON.parse(text).uid;
});
dataScreen.hidden = true;
resolve();
}
})
.catch(function() {
displayErrorMessage(ERR_UPLOAD_FAILED);
});
};
});
}
@ -139,8 +171,12 @@ function rememberMicrophone(stream) {
// Fetch the sentences.json file that tell us what sentences
// to ask the user to read
function getSentences() {
return fetch('sentences.json').then(function(r) { return r.json(); });
function getSentences() { return fetch('sentences.json').then(function(r) { return r.json(); }); }
function getLangs(){ return fetch('langs.txt').then( function(r) { return r.text(); }); }
function populatelangs(langs){
document.querySelector('#langs1').innerHTML = document.querySelector('#langs2').innerHTML = langs;
}
// Once we get the json file, break the keys and values into two
@ -223,7 +259,10 @@ function initializeAndRun() {
recording = new Blob([recording], {type:'audio/webm;codecs=opus'});
}
fetch('/upload/' + directory, { method: 'POST', body: recording })
var headers = new Headers();
headers.append("uid", localStorage.getUserInfoGiven);
fetch('/upload/' + directory, { method: 'POST', headers, body: recording })
.then(function(response) {
if (response.status !== 200) {
displayErrorMessage(ERR_UPLOAD_FAILED + ' ' + response.status + ' ' +
@ -232,11 +271,10 @@ function initializeAndRun() {
// sum one
totalsess++;
document.querySelector('#submitted').innerHTML = "Submitted Recordings this Session: " + totalsess;
recordingScreen.discards();
}
})
.catch(function() {
displayErrorMessage(ERR_UPLOAD_FAILED);
});
}
@ -266,7 +304,12 @@ function RecordingScreen(element, microphone) {
};
this.discards = function() {
this.element.hidden = true;
element.querySelector('#playimg').src = "imgs/Triangle-09-off.png";
element.querySelector('#submitimg').src = "imgs/CheckMark-off.png";
document.querySelector('#lblplay').style.color = "rgb(188,189,192)";
document.querySelector('#lblsubmit').style.color = "rgb(188,189,192)";
canuploadandplay = false;
this.recording = null;
if (this.player.src) {
URL.revokeObjectURL(this.player.src);
@ -313,19 +356,7 @@ function RecordingScreen(element, microphone) {
var recordButton = element.querySelector('#recordButton');
var playButton = element.querySelector('#playButton');
var uploadButton = element.querySelector('#uploadButton');
// The button responds to clicks to start and stop recording
recordButton.addEventListener('click', function() {
// Don't respond if we're disabled
if (recordButton.className === 'disabled')
return;
if (recording) {
stopRecording();
}
else {
startRecording();
}
});
var canuploadandplay = false;
// How much we amplify the signal from the microphone.
// If we've got a saved value, use that.
@ -366,14 +397,15 @@ function RecordingScreen(element, microphone) {
// We want to be able to record up to 60s of audio in a single blob.
// Without this argument to start(), Chrome will call dataavailable
// very frequently.
recorder.start(60000);
//recordButton.className = 'recording';
recorder.start(20000);
document.querySelector('#divanim').className = 'recording-indicator';
document.body.className = 'recording';
}
}
function stopRecording() {
if (recording) {
canuploadandplay = true;
document.querySelector('#levels').hidden = true;
clockstop();
recording = false;
@ -394,8 +426,13 @@ function RecordingScreen(element, microphone) {
recordButton.className = '';
});
};
recorder.stop();
document.querySelector('#lblplay').style.color = "rgb(0,174,239)";
document.querySelector('#lblsubmit').style.color = "rgb(0,174,239)";
document.querySelector('#divanim').className = 'stopped-indicator';
element.querySelector('#playimg').src = "imgs/Triangle-09-on.png";
element.querySelector('#submitimg').src = "imgs/CheckMark-on.png";
}
}
@ -458,13 +495,8 @@ function RecordingScreen(element, microphone) {
// here other side
var x_bar = i * barwidth;
/*
if (rightside) {
console.log('rightside');
} else {
console.log('not rightside');
}
*/
context.fillStyle = 'black';
context.fillRect(x_bar, total,
barwidth, height);
@ -480,8 +512,6 @@ function RecordingScreen(element, microphone) {
context.fillRect(x_bar+25, inverso,
barwidth, height+20);
//console.log(n, levels.height ,height,total, inverso);
}
/*
@ -517,15 +547,31 @@ function RecordingScreen(element, microphone) {
requestAnimationFrame(visualize);
}
// The button responds to clicks to start and stop recording
recordButton.addEventListener('click', function() {
// Don't respond if we're disabled
if (recordButton.className === 'disabled')
return;
if (recording) {
stopRecording();
}
else {
startRecording();
}
});
uploadButton.addEventListener('click', function() {
if (!canuploadandplay)
return;
element.dispatchEvent(new CustomEvent('upload', {detail: this.recording}));
}.bind(this));
playButton.addEventListener('click', function() {
if (!canuploadandplay)
return;
this.player.play();
}.bind(this));
// CLOCK!
var timeBegan = null
, timeStopped = null

346
public/langs.txt Normal file
Просмотреть файл

@ -0,0 +1,346 @@
<option> Select one </option>
<option> Abaza </option>
<option> Abenaki </option>
<option> Abkhaz </option>
<option> Adangme </option>
<option> Adangbe </option>
<option> Adyghe </option>
<option> Afar </option>
<option> Afrikaans </option>
<option> Ainu </option>
<option> Ajië </option>
<option> Akan </option>
<option> Albanian </option>
<option> Aleut </option>
<option> Amdang </option>
<option> Amharic </option>
<option> Ancient Egyptian Language </option>
<option> Angika </option>
<option> Apache </option>
<option> Arabic </option>
<option> Aragonese </option>
<option> Aramaic </option>
<option> Arapaho </option>
<option> Armenian </option>
<option> Armenian (Eastern) </option>
<option> Armenian (Western) </option>
<option> Arrernte </option>
<option> Assamese </option>
<option> Asturian </option>
<option> Avar </option>
<option> Avestan </option>
<option> Aymara </option>
<option> Äynu </option>
<option> Azerbaijani </option>
<option> Baga </option>
<option> Bai </option>
<option> Balinese </option>
<option> Balti </option>
<option> Bambara </option>
<option> Bantu </option>
<option> Basa </option>
<option> Bashkir </option>
<option> Basque </option>
<option> Bassa </option>
<option> Batak </option>
<option> Belarusian </option>
<option> Bemba </option>
<option> Bengali </option>
<option> Berber </option>
<option> Berta </option>
<option> Bhojpuri </option>
<option> Bislama </option>
<option> Blackfoot </option>
<option> Bodo </option>
<option> Bosnian </option>
<option> Brazilian Portuguese </option>
<option> Breton </option>
<option> Buginese </option>
<option> Buhid </option>
<option> Bulgarian </option>
<option> Burmese </option>
<option> Cahuilla </option>
<option> Cantonese </option>
<option> Carrier </option>
<option> Catalan </option>
<option> Cayuga </option>
<option> Cebuano </option>
<option> Chamorro </option>
<option> Chechen </option>
<option> Cherokee </option>
<option> Chewa </option>
<option> Cheyenne </option>
<option> Chhattisgarhi </option>
<option> Chickasaw </option>
<option> Chinese </option>
<option> Chinese (Classical) </option>
<option> Chipewyan </option>
<option> Choctaw </option>
<option> Chuvash </option>
<option> Cimbrian </option>
<option> Comanche </option>
<option> Coptic </option>
<option> Cornish </option>
<option> Corsican </option>
<option> Cree </option>
<option> Croatian </option>
<option> Cupeño </option>
<option> Czech </option>
<option> Dakhini </option>
<option> Dakota </option>
<option> Dalecarlian </option>
<option> Danish </option>
<option> Dargwa </option>
<option> Dari </option>
<option> Dhivehi </option>
<option> Drehu </option>
<option> Dungan </option>
<option> Dutch </option>
<option> Dzongkha </option>
<option> Egyptian Arabic </option>
<option> English </option>
<option> Erzya </option>
<option> Esperanto </option>
<option> Estonian </option>
<option> Evenki </option>
<option> Ewe </option>
<option> Eyak </option>
<option> Faroese </option>
<option> Fijian </option>
<option> Fiji Hindi </option>
<option> Filipino </option>
<option> Finnish </option>
<option> Fon </option>
<option> French </option>
<option> Frisian (North) </option>
<option> Frisian (Saterland) </option>
<option> Frisian (West) </option>
<option> Friulian </option>
<option> Fula </option>
<option> Fur </option>
<option> Ga </option>
<option> Galician </option>
<option> Gan </option>
<option> Ge'ez </option>
<option> Georgian </option>
<option> German </option>
<option> Gikuyu </option>
<option> Gilbertese </option>
<option> Godoberi </option>
<option> Greek </option>
<option> Greenlandic </option>
<option> Guaraní </option>
<option> Gujarati </option>
<option> Gumuz </option>
<option> Gurung </option>
<option> Gwich'in </option>
<option> Haida </option>
<option> Hainanese </option>
<option> Haitian Creole </option>
<option> Hakka </option>
<option> Harari </option>
<option> Hausa </option>
<option> Hawaiian </option>
<option> Hebrew </option>
<option> Herero </option>
<option> Himba </option>
<option> Hindi </option>
<option> Hindustani </option>
<option> Hiri Motu </option>
<option> Hmong </option>
<option> Hopi </option>
<option> Hungarian </option>
<option> Icelandic </option>
<option> Ido </option>
<option> Igbo </option>
<option> Ik </option>
<option> Ilocano </option>
<option> Indonesian </option>
<option> Ingush </option>
<option> Interlingua </option>
<option> Inuktitut </option>
<option> Inupiat </option>
<option> Irish </option>
<option> Isthmus Nahuatl </option>
<option> Italian </option>
<option> Japanese </option>
<option> Javanese </option>
<option> Judaeo </option>
<option> Jutish </option>
<option> Jèrriais </option>
<option> Kabardian </option>
<option> Kabyle </option>
<option> Kaingang </option>
<option> Kannada </option>
<option> Kanuri </option>
<option> Karakalpak </option>
<option> Karamojong </option>
<option> Karelian </option>
<option> Kashmiri </option>
<option> Kashubian </option>
<option> Kazakh </option>
<option> Kelantanese Malay </option>
<option> Kendeje </option>
<option> Khakas </option>
<option> Khmer </option>
<option> Khoekhoe </option>
<option> Khowar </option>
<option> Kinyarwanda </option>
<option> Kiribati </option>
<option> Konkani </option>
<option> Korean </option>
<option> Kuliak </option>
<option> Kumyk </option>
<option> Kurdish </option>
<option> Kutchi </option>
<option> Kwadi </option>
<option> Ladakhi </option>
<option> Ladin </option>
<option> Lakota </option>
<option> Lao </option>
<option> Latin language </option>
<option> Latvian </option>
<option> Laz </option>
<option> Leonese </option>
<option> Lepcha </option>
<option> Limbu </option>
<option> Limburgish </option>
<option> Lingala </option>
<option> Lisu </option>
<option> Lithuanian </option>
<option> Livonian </option>
<option> Low German </option>
<option> Low Prussian Dialect </option>
<option> Luba </option>
<option> Luganda </option>
<option> Luiseño </option>
<option> Luo </option>
<option> Luri </option>
<option> Luxembourgish </option>
<option> Maasai </option>
<option> Macedonian </option>
<option> Magahi </option>
<option> Magar </option>
<option> Maithili </option>
<option> Makassarese </option>
<option> Malay </option>
<option> Malayalam </option>
<option> Maltese </option>
<option> Manchu </option>
<option> Mandarin </option>
<option> Manx </option>
<option> Marathi </option>
<option> Marshallese </option>
<option> Masalit </option>
<option> Meitei </option>
<option> Miami </option>
<option> Michoacán Nahuatl </option>
<option> Min </option>
<option> Min Bei </option>
<option> Min Dong </option>
<option> Min Nan </option>
<option> Min Zhong </option>
<option> Mon </option>
<option> Mongolian </option>
<option> Muscogee Creek </option>
<option> Musgu </option>
<option> Muskum </option>
<option> Māori </option>
<option> Nagumi </option>
<option> Nahuatl </option>
<option> Navajo </option>
<option> Ndyuka </option>
<option> Nepal Bhasa </option>
<option> Nepali </option>
<option> Ngbee </option>
<option> Northern Thai </option>
<option> Norwegian </option>
<option> Nuosu </option>
<option> Nyangia </option>
<option> Occidental </option>
<option> Occitan </option>
<option> Ojibwe </option>
<option> Okinawan Japanese </option>
<option> Old Church Slavonic </option>
<option> Old English </option>
<option> Omotik </option>
<option> Odia </option>
<option> Pahari </option>
<option> Palula </option>
<option> Pattani Malay </option>
<option> Persian </option>
<option> Phoenician </option>
<option> Piman </option>
<option> Pipil </option>
<option> Pitjantjatjara </option>
<option> Plautdietsch </option>
<option> Polish </option>
<option> Portuguese </option>
<option> Punjabi language </option>
<option> Pu </option>
<option> Raga </option>
<option> Rajasthani </option>
<option> Romani </option>
<option> Romanian </option>
<option> Romansh </option>
<option> Russian </option>
<option> Rusyn </option>
<option> Ruthenian </option>
<option> Sami </option>
<option> Samoan Gagana Sāmoa </option>
<option> Sanskrit </option>
<option> Sardinian </option>
<option> Saurashtra </option>
<option> Scots </option>
<option> Scottish Gaelic </option>
<option> Serbian </option>
<option> Shan </option>
<option> Shanghaiese </option>
<option> Sherpa language </option>
<option> Shona </option>
<option> Sicilian </option>
<option> Sikkimese </option>
<option> Sindhi </option>
<option> Sinhala </option>
<option> Slovak </option>
<option> Slovene </option>
<option> Somali </option>
<option> Sotho </option>
<option> Spanish </option>
<option> Stellingwarfs </option>
<option> Sundanese </option>
<option> Swahili </option>
<option> Swedish </option>
<option> Swiss German </option>
<option> Sylheti </option>
<option> Tagalog </option>
<option> Tahitian </option>
<option> Turkish </option>
<option> Tai Dam </option>
<option> Tai Khün </option>
<option> Tai Lü </option>
<option> Tai Nüa </option>
<option> Tamang </option>
<option> Tamil </option>
<option> Tangut </option>
<option> Tatar </option>
<option> Telugu </option>
<option> Tepes </option>
<option> Teribe </option>
<option> Tetum </option>
<option> Thai </option>
<option> Tibetan </option>
<option> Tigrinya </option>
<option> Tok Pisin </option>
<option> Tongan </option>
<option> Tongva </option>
<option> Tsez </option>
<option> Tswana </option>
<option> Tuareg </option>
<option> Turkish </option>
<option> Tulu language </option>
<option> Uduk </option>
<option> Ukrainian </option>
<option> Urdu </option>
<option> Uyghur </option>
<option> Yoruba </option>