diff --git a/web/go.mod b/web/go.mod index 5ef9dc7..6f5cf9a 100644 --- a/web/go.mod +++ b/web/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( codeberg.org/SimpleWeb/SimplyTranslate/engines v0.0.0 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 diff --git a/web/go.sum b/web/go.sum index b6a6e0e..ecfb273 100644 --- a/web/go.sum +++ b/web/go.sum @@ -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/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/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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= diff --git a/web/main.go b/web/main.go index 427e330..2b8dad2 100644 --- a/web/main.go +++ b/web/main.go @@ -3,10 +3,14 @@ package main import ( "codeberg.org/SimpleWeb/SimplyTranslate/engines" "github.com/gofiber/fiber/v2" + "github.com/gofiber/template/html/v2" ) 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 { from := "" @@ -26,7 +30,7 @@ func main() { } else { return c.SendStatus(400) } - if engine == "" { + if _, ok := engines.Engines[engine]; !ok || engine == "" { engine = "google" } 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") } diff --git a/web/static/LICENSE b/web/static/LICENSE new file mode 100644 index 0000000..690ed1a --- /dev/null +++ b/web/static/LICENSE @@ -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) diff --git a/web/static/favicon.ico b/web/static/favicon.ico new file mode 100644 index 0000000..74c58f4 Binary files /dev/null and b/web/static/favicon.ico differ diff --git a/web/static/favicon.svg b/web/static/favicon.svg new file mode 100644 index 0000000..ee20376 --- /dev/null +++ b/web/static/favicon.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/static/favicon128x128.png b/web/static/favicon128x128.png new file mode 100644 index 0000000..910eb63 Binary files /dev/null and b/web/static/favicon128x128.png differ diff --git a/web/static/script.js b/web/static/script.js new file mode 100644 index 0000000..a21339e --- /dev/null +++ b/web/static/script.js @@ -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"; +}); \ No newline at end of file diff --git a/web/static/style.css b/web/static/style.css new file mode 100644 index 0000000..6fc7750 --- /dev/null +++ b/web/static/style.css @@ -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; + } +} \ No newline at end of file diff --git a/web/views/index.html b/web/views/index.html new file mode 100644 index 0000000..7b313c3 --- /dev/null +++ b/web/views/index.html @@ -0,0 +1,69 @@ + + + + + SimplyTranslate + + + + + + + + + + +
+

SimplyTranslate

+
+ +
+ + + +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ + + + \ No newline at end of file