Added html template
git-svn-id: file:///srv/svn/repo/mai/trunk@16 e410bdd4-646f-c54f-a7ce-fffcc4f439ae
This commit is contained in:
@@ -5,6 +5,7 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
codeberg.org/SimpleWeb/SimplyTranslate/engines v0.0.0
|
codeberg.org/SimpleWeb/SimplyTranslate/engines v0.0.0
|
||||||
github.com/gofiber/fiber/v2 v2.49.0 // indirect
|
github.com/gofiber/fiber/v2 v2.49.0 // indirect
|
||||||
|
github.com/gofiber/template/html/v2 v2.0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
replace codeberg.org/SimpleWeb/SimplyTranslate/engines v0.0.0 => ../engines
|
replace codeberg.org/SimpleWeb/SimplyTranslate/engines v0.0.0 => ../engines
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0
|
|||||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||||
github.com/gofiber/fiber/v2 v2.49.0 h1:xBVG2c66GDcWfww56xHvMn52Q0XX7UrSvjj6MD8/5EE=
|
github.com/gofiber/fiber/v2 v2.49.0 h1:xBVG2c66GDcWfww56xHvMn52Q0XX7UrSvjj6MD8/5EE=
|
||||||
github.com/gofiber/fiber/v2 v2.49.0/go.mod h1:oxpt7wQaEYgdDmq7nMxCGhilYicBLFnZ+jQSJcQDlSE=
|
github.com/gofiber/fiber/v2 v2.49.0/go.mod h1:oxpt7wQaEYgdDmq7nMxCGhilYicBLFnZ+jQSJcQDlSE=
|
||||||
|
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
|
||||||
|
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||||
|
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20=
|
||||||
|
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM=
|
||||||
|
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||||
|
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
|||||||
74
web/main.go
74
web/main.go
@@ -3,10 +3,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"codeberg.org/SimpleWeb/SimplyTranslate/engines"
|
"codeberg.org/SimpleWeb/SimplyTranslate/engines"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/template/html/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := fiber.New()
|
engine := html.New("./views", ".html")
|
||||||
|
app := fiber.New(fiber.Config{
|
||||||
|
Views: engine,
|
||||||
|
})
|
||||||
|
|
||||||
app.All("/api/translate", func(c *fiber.Ctx) error {
|
app.All("/api/translate", func(c *fiber.Ctx) error {
|
||||||
from := ""
|
from := ""
|
||||||
@@ -26,7 +30,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
return c.SendStatus(400)
|
return c.SendStatus(400)
|
||||||
}
|
}
|
||||||
if engine == "" {
|
if _, ok := engines.Engines[engine]; !ok || engine == "" {
|
||||||
engine = "google"
|
engine = "google"
|
||||||
}
|
}
|
||||||
if to == "" {
|
if to == "" {
|
||||||
@@ -39,5 +43,71 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.Get("/api/source_languages", func(c *fiber.Ctx) error {
|
||||||
|
engine := c.Query("engine")
|
||||||
|
if _, ok := engines.Engines[engine]; !ok || engine == "" {
|
||||||
|
engine = "google"
|
||||||
|
}
|
||||||
|
if result, err := engines.Engines[engine].SourceLanguages(); err != nil {
|
||||||
|
return c.SendStatus(500)
|
||||||
|
} else {
|
||||||
|
return c.JSON(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/api/target_languages", func(c *fiber.Ctx) error {
|
||||||
|
engine := c.Query("engine")
|
||||||
|
if _, ok := engines.Engines[engine]; !ok || engine == "" {
|
||||||
|
engine = "google"
|
||||||
|
}
|
||||||
|
if result, err := engines.Engines[engine].TargetLanguages(); err != nil {
|
||||||
|
return c.SendStatus(500)
|
||||||
|
} else {
|
||||||
|
return c.JSON(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.All("/", func(c *fiber.Ctx) error {
|
||||||
|
engine := c.Query("engine")
|
||||||
|
if _, ok := engines.Engines[engine]; !ok || engine == "" {
|
||||||
|
engine = "google"
|
||||||
|
}
|
||||||
|
targetLanguages, err := engines.Engines[engine].TargetLanguages()
|
||||||
|
if err != nil {
|
||||||
|
return c.SendStatus(500)
|
||||||
|
}
|
||||||
|
sourceLanguages, err := engines.Engines[engine].SourceLanguages()
|
||||||
|
if err != nil {
|
||||||
|
return c.SendStatus(500)
|
||||||
|
}
|
||||||
|
originalText := ""
|
||||||
|
translatedText := ""
|
||||||
|
from := ""
|
||||||
|
to := ""
|
||||||
|
|
||||||
|
if c.Method() == "POST" {
|
||||||
|
from =
|
||||||
|
c.FormValue("from")
|
||||||
|
to = c.FormValue("to")
|
||||||
|
originalText = c.FormValue("text")
|
||||||
|
if result, err := engines.Engines[engine].Translate(originalText, from, to); err != nil {
|
||||||
|
return c.SendStatus(500)
|
||||||
|
} else {
|
||||||
|
translatedText = result.TranslatedText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Render("index", fiber.Map{
|
||||||
|
"Engine": engine,
|
||||||
|
"SourceLanguages": targetLanguages,
|
||||||
|
"TargetLanguages": sourceLanguages,
|
||||||
|
"OriginalText": originalText,
|
||||||
|
"TranslatedText": translatedText,
|
||||||
|
"From": from,
|
||||||
|
"To": to,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Static("/static", "./static")
|
||||||
|
|
||||||
app.Listen(":3000")
|
app.Listen(":3000")
|
||||||
}
|
}
|
||||||
|
|||||||
3
web/static/LICENSE
Normal file
3
web/static/LICENSE
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
favicon.ico, favicon.svg, favicon128x128.png
|
||||||
|
Created by "joelchrono12" (https://https://joelchrono12.ml/)
|
||||||
|
Creative Commons Attribution 4.0 International License (CC BY 4.0)
|
||||||
BIN
web/static/favicon.ico
Normal file
BIN
web/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
144
web/static/favicon.svg
Normal file
144
web/static/favicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
BIN
web/static/favicon128x128.png
Normal file
BIN
web/static/favicon128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
17
web/static/script.js
Normal file
17
web/static/script.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||||
|
// this code submits the translation form when pressing Ctrl/Meta+Enter while focussed on the input text field
|
||||||
|
document.getElementById("input").addEventListener("keydown", function (event) {
|
||||||
|
if (event.keyCode === 13 && (event.metaKey || event.ctrlKey)) {
|
||||||
|
document.getElementById("translation-form").submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto resize textarea to fit words inside it without need to scroll -- Thanks to: https://stackoverflow.com/a/25621277
|
||||||
|
var input = document.getElementById("input");
|
||||||
|
var output = document.getElementById("output");
|
||||||
|
input.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
|
||||||
|
output.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
|
||||||
|
input.addEventListener("input", function (e) {
|
||||||
|
this.style.height = 150 + "px";
|
||||||
|
this.style.height = this.scrollHeight + "px";
|
||||||
|
});
|
||||||
183
web/static/style.css
Normal file
183
web/static/style.css
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap.languages {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#could_not_switch_languages_text {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
width: 450px;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.language,
|
||||||
|
.switch_languages {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language {
|
||||||
|
margin: 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch_languages {
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#switchbutton {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 4px;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
height: 5rem;
|
||||||
|
font-family: sans-serif;
|
||||||
|
|
||||||
|
/* Stretch to form width */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus,
|
||||||
|
button:focus {
|
||||||
|
border-color: #478061;
|
||||||
|
outline: 1px solid #478061;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
justify-content: center;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#definitions_and_translations {
|
||||||
|
display: grid;
|
||||||
|
margin: auto;
|
||||||
|
width: 1100px;
|
||||||
|
gap: 10px;
|
||||||
|
grid-template-areas: "definitions translations";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.def_type {
|
||||||
|
color: #007979;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.syn {
|
||||||
|
color: #804700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.syn_type {
|
||||||
|
color: #007979;
|
||||||
|
}
|
||||||
|
|
||||||
|
.use_in_sentence {
|
||||||
|
color: #009902;
|
||||||
|
}
|
||||||
|
|
||||||
|
.definitions li:not(:last-child) {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
#definitions_and_translations {
|
||||||
|
display: grid;
|
||||||
|
width: 90vw;
|
||||||
|
grid-template-areas:
|
||||||
|
"definitions definitions"
|
||||||
|
"translations translations";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.definitions {
|
||||||
|
grid-area: definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.translations {
|
||||||
|
grid-area: translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #212529;
|
||||||
|
color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
#could_not_switch_languages_text {
|
||||||
|
color: #F13333;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #9759f6;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #599bf6;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
button,
|
||||||
|
textarea {
|
||||||
|
background-color: #131618;
|
||||||
|
border-color: #495057;
|
||||||
|
color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.def_type {
|
||||||
|
color: cyan;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.syn {
|
||||||
|
color: burlywood;
|
||||||
|
}
|
||||||
|
|
||||||
|
.syn_type {
|
||||||
|
color: cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
.use_in_sentence {
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
web/views/index.html
Normal file
69
web/views/index.html
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>SimplyTranslate</title>
|
||||||
|
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||||
|
<meta name="description" content="Experience simple and private Google translations">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
|
||||||
|
<meta name="referrer" content="no-referrer">
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="center">
|
||||||
|
<h1>SimplyTranslate</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<form method="POST" id="translation-form">
|
||||||
|
|
||||||
|
<input type="hidden" name="engine" value="{{.Engine}}">
|
||||||
|
|
||||||
|
<div class="wrap languages">
|
||||||
|
<div class="language">
|
||||||
|
<select name="from" aria-label="Source language">
|
||||||
|
{{range $code, $name := .SourceLanguages}}
|
||||||
|
<option value="{{ $code }}" {{if eq $code $.From}}selected{{end}}>{{ $name }}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="switch_languages">
|
||||||
|
<button id="switchbutton" aria-label="Switch languages"
|
||||||
|
formaction="/switchlanguages/?engine={{ .Engine }}" type="submit"><-></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="language">
|
||||||
|
<select name="to" aria-label="Target language">
|
||||||
|
{{range $code, $name := .TargetLanguages}}
|
||||||
|
<option value="{{ $code }}" {{if eq $code $.To}}selected{{end}}>{{ $name }}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="item-wrapper">
|
||||||
|
<textarea autofocus class="item" id="input" name="text" dir="auto"
|
||||||
|
placeholder="Enter Text Here">{{ .OriginalText }}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-wrapper">
|
||||||
|
<textarea id="output" class="translation item" dir="auto" placeholder="Translation"
|
||||||
|
readonly>{{.TranslatedText}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="center">
|
||||||
|
<button type="submit">Translate with {{ .Engine }}!</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<script src="/static/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user