2020-12-19 22:40:37 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2020-12-21 16:10:44 +00:00
< title > LibreTranslate - Free and Open Source Machine Translation API< / title >
2021-01-10 08:59:07 +00:00
< link rel = "shortcut icon" href = "{{ url_for('static', filename='favicon.ico') }}" >
2020-12-21 16:10:44 +00:00
< meta name = "description" content = "Free and Open Source Machine Translation API. 100% self-hosted, no limits, no ties to proprietary services. Run your own API server in just a few minutes." >
2020-12-21 00:11:18 +00:00
< meta name = "keywords" content = "translation,api" >
2020-12-20 19:03:53 +00:00
2020-12-21 16:10:44 +00:00
< meta property = "og:title" content = "LibreTranslate - Free and Open Source Machine Translation API" / >
2020-12-21 00:11:18 +00:00
< meta property = "og:type" content = "website" / >
< meta property = "og:url" content = "https://libretranslate.com" / >
< meta property = "og:image" content = "https://user-images.githubusercontent.com/1951843/102724116-32a6df00-42db-11eb-8cc0-129ab39cdfb5.png" / >
2020-12-21 16:10:44 +00:00
< meta property = "og:description" name = "description" class = "swiftype" content = "Free and Open Source Machine Translation API. 100% self-hosted, no limits, no ties to proprietary services. Run your own API server in just a few minutes." / >
2020-12-20 19:03:53 +00:00
2020-12-19 22:40:37 +00:00
< script src = "https://cdn.jsdelivr.net/npm/vue@2" > < / script >
<!-- Compiled and minified CSS -->
2020-12-20 16:55:56 +00:00
< link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" >
< link href = "https://fonts.googleapis.com/icon?family=Material+Icons" rel = "stylesheet" >
< link href = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.22.0/themes/prism.min.css" rel = "stylesheet" / >
< style type = "text/css" >
textarea.materialize-textarea{height: 120px;}
.code{
font-size: 90%;
border-radius: 4px;
padding: 4px;
border: 1px solid #9e9e9e;
background: #fbfbfb;
overflow: auto;
font-family: monospace;
min-height: 280px;
2020-12-20 18:45:34 +00:00
width: 100%;
overflow: auto;
2020-12-20 16:55:56 +00:00
}
.progress.translate{
position: absolute;
}
2020-12-20 18:45:34 +00:00
.card.horizontal .card-stacked{
overflow: auto;
}
2020-12-20 16:55:56 +00:00
< / style >
2020-12-20 19:03:53 +00:00
{% if gaId %}
<!-- Global site tag (gtag.js) - Google Analytics -->
< script async src = "https://www.googletagmanager.com/gtag/js?id={{ gaId }}" > < / script >
< script >
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ gaId }}');
< / script >
{% endif %}
2020-12-19 22:40:37 +00:00
< / head >
< body >
< nav class = "blue lighten-1" role = "navigation" >
2020-12-20 18:45:34 +00:00
< div class = "nav-wrapper container" > < a id = "logo-container" href = "/" class = "brand-logo" > < i class = "material-icons" > translate< / i > LibreTranslate< / a >
2020-12-20 16:55:56 +00:00
< ul class = "right hide-on-med-and-down" >
2020-12-20 18:45:34 +00:00
< li > < a href = "/docs" > API Docs< / a > < / li >
2020-12-20 16:55:56 +00:00
< li > < a href = "https://github.com/uav4geo/LibreTranslate" > GitHub< / a > < / li >
< / ul >
< ul id = "nav-mobile" class = "sidenav" >
2020-12-20 18:45:34 +00:00
< li > < a href = "/docs" > API Docs< / a > < / li >
2020-12-20 16:55:56 +00:00
< li > < a href = "https://github.com/uav4geo/LibreTranslate" > GitHub< / a > < / li >
< / ul >
< a href = "#" data-target = "nav-mobile" class = "sidenav-trigger" > < i class = "material-icons" > menu< / i > < / a >
< / div >
2020-12-19 22:40:37 +00:00
< / nav >
< div id = "app" >
2020-12-20 16:55:56 +00:00
< div class = "section no-pad-bot center" v-if = "loading" >
< div class = "container" >
< div class = "row" >
< div class = "preloader-wrapper active" >
< div class = "spinner-layer spinner-blue-only" >
< div class = "circle-clipper left" >
< div class = "circle" > < / div >
< / div > < div class = "gap-patch" >
< div class = "circle" > < / div >
< / div > < div class = "circle-clipper right" >
< div class = "circle" > < / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< div v-else-if = "error" >
2020-12-20 18:45:34 +00:00
< div class = "section no-pad-bot" >
< div class = "container" >
< div class = "row" >
< div class = "col s12 m7" >
< div class = "card horizontal" >
< div class = "card-stacked" >
< div class = "card-content" >
2020-12-20 19:03:53 +00:00
< i class = "material-icons" > warning< / i > < p > [[ error ]]< / p >
2020-12-20 18:45:34 +00:00
< / div >
< div class = "card-action" >
< a href = "#" @ click = "dismissError" > Dismiss< / a >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
2020-12-20 16:55:56 +00:00
< / div >
< div v-else >
2020-12-19 22:40:37 +00:00
< div class = "section no-pad-bot" >
2020-12-20 16:55:56 +00:00
< div class = "container" >
< div class = "row" >
< h3 class = "header center" > Translation API< / h3 >
< div class = "card horizontal" >
< div class = "card-stacked" >
< div class = "card-content" >
< form class = "col s12" >
< div class = "row" >
< div class = "input-field col s5" >
< select class = "browser-default" v-model = "sourceLang" ref = "sourceLangDropdown" @ change = "handleInput" >
< template v-for = "option in langs" >
2020-12-20 19:03:53 +00:00
< option :value = "option.code" > [[ option.name ]]< / option >
2020-12-20 16:55:56 +00:00
< / template >
< / select >
2020-12-19 22:40:37 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< div class = "col s2 center" >
2020-12-20 21:58:44 +00:00
< a href = "javascript:void(0)" @ click = "swapLangs" class = "waves-effect waves-teal btn-flat btn-large" style = "margin-top: 8px;" > < i class = "material-icons" > swap_horiz< / i > < / a >
2020-12-19 22:40:37 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< div class = "input-field col s5" >
< select class = "browser-default" v-model = "targetLang" ref = "targetLangDropdown" @ change = "handleInput" >
< template v-for = "option in langs" >
2020-12-20 19:03:53 +00:00
< option :value = "option.code" > [[ option.name ]]< / option >
2020-12-20 16:55:56 +00:00
< / template >
< / select >
< / div >
2021-01-18 15:06:43 +00:00
< / div >
< div class = "row" >
2020-12-20 16:55:56 +00:00
< div class = "input-field col s6" >
< textarea id = "textarea1" class = "materialize-textarea" v-model = "inputText" @ input = "handleInput" ref = "inputTextarea" > < / textarea >
< label for = "textarea1" > Input Text< / label >
2020-12-20 18:45:34 +00:00
< div v-if = "charactersLimit !== -1" >
2020-12-20 19:03:53 +00:00
< label > [[ inputText.length ]] / [[ charactersLimit ]]< / label >
2020-12-20 18:45:34 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< / div >
< div class = "input-field col s6" >
< div >
< textarea id = "textarea2" class = "materialize-textarea" v-model = "translatedText" ref = "translatedTextarea" > < / textarea >
< label for = "textarea2" > < div class = "progress translate" v-if = "loadingTranslation" >
< div class = "indeterminate" > < / div >
< / div > < / label >
< / div >
< / div >
2021-01-18 15:06:43 +00:00
< / div >
2021-01-17 16:58:22 +00:00
< div class = "row" >
2021-01-18 15:06:43 +00:00
< div class = "col s6" >
< button class = "btn btn-small" style = "display: inline-block;" title = "Delete text" @ click = "deleteText" > < i class = "material-icons" > delete< / i > < / button >
< / div >
< div class = "col s6" style = "text-align: right;" >
< button class = "btn btn-small" style = "display: inline-block;" @ click = "copyText" > [[ copyTextLabel ]]< / button >
< / div >
2021-01-17 16:58:22 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< / form >
2020-12-19 22:40:37 +00:00
< / div >
< / div >
< / div >
< / div >
2020-12-20 16:55:56 +00:00
< / div >
2020-12-19 22:40:37 +00:00
< / div >
< div class = "section no-pad-bot" id = "index-banner" >
2020-12-20 16:55:56 +00:00
< div class = "container" >
< div class = "row center" >
2020-12-19 22:40:37 +00:00
< div class = "col s12 m12" >
2020-12-20 16:55:56 +00:00
< div class = "card horizontal" >
< div class = "card-stacked" >
< div class = "card-content" >
< div class = "row center" >
2020-12-20 18:45:34 +00:00
< div class = "col s12 m12 l6 left-align" >
2020-12-20 16:55:56 +00:00
< p > Request< / p >
< p >
< pre class = "code" > < code class = "language-javascript" v-html = "$options.filters.highlight(requestCode)" >
< / code > < / pre > < / p >
< / div >
2020-12-20 18:45:34 +00:00
< div class = "col s12 m12 l6 left-align" >
2020-12-20 16:55:56 +00:00
< p > Response< / p >
< pre class = "code" > < code class = "language-javascript" v-html = "$options.filters.highlight(output)" >
< / code > < / pre >
< / div >
< / div >
< / div >
< / div >
< / div >
2020-12-19 22:40:37 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< / div >
< / div >
2020-12-19 22:40:37 +00:00
< / div >
2020-12-21 04:47:24 +00:00
< div class = "section no-pad-bot" id = "index-banner" >
< div class = "container" >
< div class = "row center" >
< div class = "col s12 m12" >
< h3 class = "header" > Open Source Machine Translation< / h3 >
< h4 class = "header" > 100% Self-Hosted. No Limits. No Ties to Proprietary Services.< / h4 >
< br / > < a class = "waves-effect waves-light btn btn-large" href = "https://github.com/uav4geo/LibreTranslate" > < i class = "material-icons left" > cloud_download< / i > Download< / a >
< br / > < br / > < br / >
< / div >
< / div >
< / div >
< / div >
2020-12-19 22:40:37 +00:00
< / div >
2020-12-20 16:55:56 +00:00
< / div >
2020-12-19 22:40:37 +00:00
< footer class = "page-footer blue darken-3" >
< div class = "container" >
2020-12-20 16:55:56 +00:00
< div class = "row" >
< div class = "col l6 s12" >
< h5 class = "white-text" > LibreTranslate< / h5 >
2020-12-21 16:10:44 +00:00
< p class = "grey-text text-lighten-4" > Free and Open Source Machine Translation API< / p >
2020-12-20 16:55:56 +00:00
< p class = "grey-text text-lighten-4" >
Made with ❤ by < a class = "grey-text text-lighten-3" href = "https://uav4geo.com" > UAV4GEO< / a > and powered by < a class = "grey-text text-lighten-3" href = "https://github.com/argosopentech/argos-translate/" > Argos Translate< / a >
2020-12-19 22:40:37 +00:00
< / p >
2020-12-20 16:55:56 +00:00
< p > < a class = "grey-text text-lighten-4" href = "https://www.gnu.org/licenses/agpl-3.0.en.html" > License: AGPLv3< / a > < / p >
< / div >
< div class = "col l4 offset-l2 s12" >
<!-- <h5 class="white - text">Links</h5>
< ul >
< li > < a class = "grey-text text-lighten-3" href = "#!" > Link 1< / a > < / li >
< li > < a class = "grey-text text-lighten-3" href = "#!" > Link 2< / a > < / li >
< li > < a class = "grey-text text-lighten-3" href = "#!" > Link 3< / a > < / li >
< li > < a class = "grey-text text-lighten-3" href = "#!" > Link 4< / a > < / li >
< / ul > -->
< div class = "container" >
< / div >
< / div >
< / div >
2020-12-19 22:40:37 +00:00
< / div >
< div class = "footer-copyright center" >
< / div >
< / footer >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js" > < / script >
2020-12-20 16:55:56 +00:00
< script >
window.Prism = window.Prism || {};
window.Prism.manual = true;
< / script >
2021-01-17 16:58:22 +00:00
< script src = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.22.0/prism.min.js" > < / script >
2020-12-19 22:40:37 +00:00
< script >
// API host/endpoint
2020-12-20 16:55:56 +00:00
var BaseUrl = window.location.protocol + "//" + window.location.host;
2020-12-19 22:40:37 +00:00
document.addEventListener('DOMContentLoaded', function(){
2020-12-20 16:55:56 +00:00
var elems = document.querySelectorAll('.sidenav');
var instances = M.Sidenav.init(elems);
2020-12-19 22:40:37 +00:00
var app = new Vue({
el: '#app',
2020-12-20 19:03:53 +00:00
delimiters: ['[[',']]'],
2020-12-19 22:40:37 +00:00
data: {
2020-12-20 16:55:56 +00:00
BaseUrl: BaseUrl,
loading: true,
error: "",
langs: [],
2021-01-10 08:07:56 +00:00
settings: {},
2020-12-20 16:55:56 +00:00
sourceLang: "",
targetLang: "",
loadingTranslation: false,
inputText: "",
translatedText: "",
2020-12-20 18:45:34 +00:00
output: "",
charactersLimit: -1,
2021-01-18 15:06:43 +00:00
copyTextLabel: "Copy Text"
2020-12-19 22:40:37 +00:00
},
mounted: function(){
2020-12-20 16:55:56 +00:00
var self = this;
2021-01-10 08:07:56 +00:00
var requestSettings = new XMLHttpRequest();
requestSettings.open('GET', BaseUrl + '/frontend/settings', true);
2020-12-19 22:40:37 +00:00
2021-01-10 08:07:56 +00:00
requestSettings.onload = function() {
if (this.status >= 200 & & this.status < 400 ) {
// Success!
self.settings = JSON.parse(this.response);
self.sourceLang = self.settings.language.source.code;
self.targetLang = self.settings.language.target.code;
2021-01-10 09:24:42 +00:00
self.charactersLimit = self.settings.charLimit;
2021-01-10 08:07:56 +00:00
}else {
self.error = "Cannot load /frontend/settings";
self.loading = false;
}
};
requestSettings.onerror = function() {
self.error = "Error while calling /frontend/settings";
self.loading = false;
};
requestSettings.send();
var requestLanguages = new XMLHttpRequest();
requestLanguages.open('GET', BaseUrl + '/languages', true);
requestLanguages.onload = function() {
2020-12-19 22:40:37 +00:00
if (this.status >= 200 & & this.status < 400 ) {
2020-12-20 16:55:56 +00:00
// Success!
self.langs = JSON.parse(this.response);
2021-01-15 17:01:16 +00:00
self.langs.push({ name: 'Auto Detect (Experimental)', code: 'auto' })
2020-12-20 16:55:56 +00:00
if (self.langs.length === 0){
self.loading = false;
self.error = "No languages available. Did you install the models correctly?"
return;
}
self.loading = false;
2020-12-19 22:40:37 +00:00
} else {
2020-12-20 16:55:56 +00:00
self.error = "Cannot load /languages";
self.loading = false;
2020-12-19 22:40:37 +00:00
}
};
2021-01-10 08:07:56 +00:00
requestLanguages.onerror = function() {
2020-12-19 22:40:37 +00:00
self.error = "Error while calling /languages";
self.loading = false;
};
2021-01-10 08:07:56 +00:00
requestLanguages.send();
2020-12-19 22:40:37 +00:00
},
updated: function(){
2020-12-20 16:55:56 +00:00
M.FormSelect.init(this.$refs.sourceLangDropdown);
M.FormSelect.init(this.$refs.targetLangDropdown);
if (this.inputText === ""){
this.$refs.inputTextarea.style.height = 150 + "px";
this.$refs.translatedTextarea.style.height = 150 + "px";
}else{
2020-12-20 20:52:02 +00:00
this.$refs.inputTextarea.style.height = this.$refs.translatedTextarea.style.height = "1px";
2020-12-20 16:55:56 +00:00
this.$refs.inputTextarea.style.height = Math.max(150, this.$refs.inputTextarea.scrollHeight) + "px";
this.$refs.translatedTextarea.style.height = Math.max(150, this.$refs.translatedTextarea.scrollHeight) + "px";
}
2020-12-20 18:45:34 +00:00
if (this.charactersLimit !== -1 & & this.inputText.length >= this.charactersLimit){
this.inputText = this.inputText.substring(0, this.charactersLimit);
}
2021-01-18 15:06:43 +00:00
2020-12-20 16:55:56 +00:00
},
computed: {
requestCode: function(){
return ['const res = await fetch("' + this.BaseUrl + '/translate", {',
' method: "POST",',
' body: JSON.stringify({',
' q: "' + this.$options.filters.escape(this.inputText) + '",',
' source: "' + this.$options.filters.escape(this.sourceLang) + '",',
' target: "' + this.$options.filters.escape(this.targetLang) + '"',
' }),',
2021-01-15 17:12:00 +00:00
' headers: { "Content-Type": "application/json" }',
2021-01-15 17:13:42 +00:00
'});',
2020-12-20 16:55:56 +00:00
'',
'console.log(await res.json());'].join("\n");
}
},
filters: {
escape: function(v){
return v.replace('"', '\\\"');
},
highlight: function(v){
return Prism.highlight(v, Prism.languages.javascript, 'javascript');
}
},
methods: {
abortPreviousTransRequest: function(){
if (this.transRequest){
this.transRequest.abort();
this.transRequest = null;
}
},
swapLangs: function(){
var t = this.sourceLang;
this.sourceLang = this.targetLang;
this.targetLang = t;
this.inputText = this.translatedText;
this.translatedText = "";
this.handleInput();
},
2020-12-20 18:45:34 +00:00
dismissError: function(){
this.error = '';
},
2020-12-20 16:55:56 +00:00
handleInput: function(e){
if (this.timeout) clearTimeout(this.timeout);
this.timeout = null;
if (this.inputText === ""){
this.translatedText = "";
this.output = "";
this.abortPreviousTransRequest();
this.loadingTranslation = false;
return;
}
var self = this;
self.loadingTranslation = true;
this.timeout = setTimeout(function(){
self.abortPreviousTransRequest();
var request = new XMLHttpRequest();
self.transRequest = request;
var data = new FormData();
data.append("q", self.inputText);
data.append("source", self.sourceLang);
data.append("target", self.targetLang);
request.open('POST', BaseUrl + '/translate', true);
request.onload = function() {
2020-12-20 20:05:22 +00:00
try{
var res = JSON.parse(this.response);
2020-12-20 16:55:56 +00:00
// Success!
if (res.translatedText !== undefined){
self.translatedText = res.translatedText;
self.loadingTranslation = false;
2020-12-20 20:05:22 +00:00
self.output = JSON.stringify(res, null, 4);
}else{
throw new Error(res.error || "Unknown error");
2020-12-20 16:55:56 +00:00
}
2020-12-20 20:05:22 +00:00
}catch(e){
self.error = e.message;
self.loadingTranslation = false;
2020-12-20 16:55:56 +00:00
}
};
request.onerror = function() {
self.error = "Error while calling /translate";
self.loadingTranslation = false;
};
request.send(data);
}, 300);
2021-01-18 15:06:43 +00:00
},
2020-12-19 22:40:37 +00:00
2021-01-18 15:06:43 +00:00
copyText: function(e){
e.preventDefault();
console.log(this.$refs);
this.$refs.translatedTextarea.select();
this.$refs.translatedTextarea.setSelectionRange(0, 9999999); /* For mobile devices */
document.execCommand("copy");
if (this.copyTextLabel === "Copy Text"){
this.copyTextLabel = "Copied!";
var self = this;
setTimeout(function(){
self.copyTextLabel = "Copy Text";
}, 1500);
}
},
2021-01-17 16:58:22 +00:00
2021-01-18 15:06:43 +00:00
deleteText: function(e){
e.preventDefault();
this.inputText = this.translatedText = "";
}
}
});
2021-01-17 16:58:22 +00:00
});
2020-12-19 22:40:37 +00:00
< / script >
< / body >
2021-01-17 16:58:22 +00:00
< / html >