generated from ElnuDev/go-project
Compare commits
No commits in common. "01204ffc81072e51f608a6b540b9ed8a0df52d8d" and "848175efde5dbaf4f3ab4d23dc70a5ce9eabede9" have entirely different histories.
01204ffc81
...
848175efde
18 changed files with 13 additions and 211 deletions
|
@ -4,8 +4,8 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.elnu.com/ElnuDev/jichanorg/shiritori/api"
|
. "git.elnu.com/ElnuDev/shiritori-go/shiritori"
|
||||||
. "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
|
"git.elnu.com/ElnuDev/shiritori-go/shiritori/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
1
dict/.gitignore
vendored
1
dict/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
JMdict.xml
|
|
|
@ -1,14 +0,0 @@
|
||||||
# jichanorg/dict
|
|
||||||
|
|
||||||
**jichanorg/dict** is a [hypermedia](https://hypermedia.systems/) and JSON API for parsing the JMDict Japanese dictionary project.
|
|
||||||
|
|
||||||
Its primary goals are:
|
|
||||||
|
|
||||||
- be a self-hostable dictionary solution to replace existing proprietary ([Jisho.org](https://jisho.org/)) and partially proprietary ([Jotoba](https://jotoba.de/)) dictionaries.
|
|
||||||
- Provide a hypermedia API for integration into other applications, such as [jichanorg/shiritori](../shiritori).
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
1. [Download the latest version of JMdict](https://www.edrdg.org/wiki/index.php/JMdict-EDICT_Dictionary_Project#CURRENT_VERSION_&_DOWNLOAD). For development, download the file with only English glosses, which will substantially decrease parsing time.
|
|
||||||
2. Extract the archive
|
|
||||||
3. Rename the file to JMDict.xml
|
|
|
@ -1,8 +0,0 @@
|
||||||
module git.elnu.com/ElnuDev/jichanorg/dict
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
require (
|
|
||||||
foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3 // indirect
|
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
|
||||||
)
|
|
|
@ -1,4 +0,0 @@
|
||||||
foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3 h1:zjHGpgUR2WP3pf6NVZM38OKYNse0GjovCW2v23V72PQ=
|
|
||||||
foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3/go.mod h1:ZrjLCcE7ZrND28ZOSGYMd78tL+Dffiv2g+NjOMKgnew=
|
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
112
dict/main.go
112
dict/main.go
|
@ -1,112 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"foosoft.net/projects/jmdict"
|
|
||||||
"git.elnu.com/ElnuDev/jichanorg/httputils"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dict jmdict.Jmdict
|
|
||||||
|
|
||||||
func LoadDict() error {
|
|
||||||
const jmdictFile = "JMdict.xml"
|
|
||||||
reader, err := os.Open(jmdictFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dict, _, err = jmdict.LoadJmdict(reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Entry struct {
|
|
||||||
Kanji string
|
|
||||||
Reading string
|
|
||||||
Definitions []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseEntry(entry jmdict.JmdictEntry) Entry {
|
|
||||||
kanji := ""
|
|
||||||
if len(entry.Kanji) > 0 {
|
|
||||||
kanji = entry.Kanji[0].Expression
|
|
||||||
}
|
|
||||||
reading := ""
|
|
||||||
if len(entry.Readings) > 0 {
|
|
||||||
reading = entry.Readings[0].Reading
|
|
||||||
}
|
|
||||||
var definitions []string
|
|
||||||
if len(entry.Sense) > 0 && len(entry.Sense[0].Glossary) > 0 {
|
|
||||||
definitions = make([]string, len(entry.Sense[0].Glossary))
|
|
||||||
for i, glossary := range entry.Sense[0].Glossary {
|
|
||||||
definitions[i] = glossary.Content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Entry{
|
|
||||||
Kanji: kanji,
|
|
||||||
Reading: reading,
|
|
||||||
Definitions: definitions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Search(query string) []Entry {
|
|
||||||
entries := make([]Entry, 0)
|
|
||||||
for _, jmdictEntry := range dict.Entries {
|
|
||||||
for _, kanji := range jmdictEntry.Kanji {
|
|
||||||
if kanji.Expression == query {
|
|
||||||
goto match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, reading := range jmdictEntry.Readings {
|
|
||||||
if reading.Reading == query {
|
|
||||||
goto match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
match:
|
|
||||||
entry := ParseEntry(jmdictEntry)
|
|
||||||
entries = append(entries, entry)
|
|
||||||
}
|
|
||||||
return entries
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := LoadDict()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("JMdict loaded!")
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/api/search", httputils.GenerateHandler(
|
|
||||||
"search.html",
|
|
||||||
func(w http.ResponseWriter, r *http.Request) bool {
|
|
||||||
if r.Header.Get("Accept") != "application/json" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
r.ParseMultipartForm(0)
|
|
||||||
query := r.FormValue("q")
|
|
||||||
entries := Search(query)
|
|
||||||
jsonBytes, _ := json.Marshal(entries)
|
|
||||||
fmt.Fprint(w, string(jsonBytes))
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
func(w http.ResponseWriter, r *http.Request) any {
|
|
||||||
r.ParseMultipartForm(0)
|
|
||||||
query := r.FormValue("q")
|
|
||||||
entry := Search(query)
|
|
||||||
return entry
|
|
||||||
},
|
|
||||||
[]string{http.MethodGet, http.MethodPost},
|
|
||||||
))
|
|
||||||
r.Handle("/", http.FileServer(http.Dir("static")))
|
|
||||||
log.Fatal(http.ListenAndServe(":3334", r))
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>jidict</title>
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/missing.css@1.0.9/dist/missing.min.css">
|
|
||||||
<script src="https://unpkg.com/htmx.org@1.9.3"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<a href="/">
|
|
||||||
<img src="https://jichan.org/logo.svg" style="height: 4em; display: block; margin: 1em auto 1em auto">
|
|
||||||
</a>
|
|
||||||
<form
|
|
||||||
hx-post="/api/search"
|
|
||||||
hx-target="#results"
|
|
||||||
hx-swap="innerHTML">
|
|
||||||
<input type="text" name="q" placeholder="辞書をサーチする" class="width:100%" autocomplete="false">
|
|
||||||
</form>
|
|
||||||
<div id="results"></div>
|
|
||||||
<br>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,21 +0,0 @@
|
||||||
<p><i>{{ $count := (len .) }}{{ if eq $count 0 }}No results{{ else }}{{ $count }} result{{ if ne $count 1}}s{{ end }}{{ end }}.</i></p>
|
|
||||||
{{- range . -}}
|
|
||||||
<div class="box">
|
|
||||||
<h3>
|
|
||||||
{{- if .Kanji -}}
|
|
||||||
<ruby>{{- .Kanji -}}<rp>(</rp><rt>{{- .Reading -}}</rt><rp>)</rp></ruby>
|
|
||||||
{{- else -}}
|
|
||||||
{{- .Reading -}}
|
|
||||||
{{- end -}}
|
|
||||||
</h3>
|
|
||||||
{{ if le (len .Definitions) 2 -}}
|
|
||||||
<p>{{- index .Definitions 0 -}}</p>
|
|
||||||
{{- else -}}
|
|
||||||
<ol>
|
|
||||||
{{- range .Definitions }}
|
|
||||||
<li>{{- . -}}</li>
|
|
||||||
{{- end }}
|
|
||||||
</ol>
|
|
||||||
{{- end }}
|
|
||||||
</div>
|
|
||||||
{{ end -}}
|
|
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
||||||
module git.elnu.com/ElnuDev/jichanorg
|
module git.elnu.com/ElnuDev/shiritori-go
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
5
go.work
5
go.work
|
@ -1,5 +0,0 @@
|
||||||
go 1.20
|
|
||||||
|
|
||||||
use ./httputils
|
|
||||||
use ./dict
|
|
||||||
use ./shiritori
|
|
|
@ -1,3 +0,0 @@
|
||||||
module git.elnu.com/ElnuDev/jichanorg/httputils
|
|
||||||
|
|
||||||
go 1.20
|
|
|
@ -10,7 +10,7 @@ type Handler = func(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
func GenerateHandler(
|
func GenerateHandler(
|
||||||
file string,
|
file string,
|
||||||
handler func(http.ResponseWriter, *http.Request) bool,
|
handler func(http.ResponseWriter, *http.Request),
|
||||||
data func(http.ResponseWriter, *http.Request) any,
|
data func(http.ResponseWriter, *http.Request) any,
|
||||||
methods []string,
|
methods []string,
|
||||||
) Handler {
|
) Handler {
|
||||||
|
@ -19,7 +19,6 @@ func GenerateHandler(
|
||||||
tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file)))
|
tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file)))
|
||||||
}
|
}
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file)))
|
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
if method == r.Method {
|
if method == r.Method {
|
||||||
goto ok
|
goto ok
|
||||||
|
@ -28,8 +27,8 @@ func GenerateHandler(
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
ok:
|
ok:
|
||||||
renderTemplate := handler(w, r)
|
handler(w, r)
|
||||||
if renderTemplate && tmpl != nil {
|
if tmpl != nil {
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
tmpl.Execute(w, data(w, r))
|
tmpl.Execute(w, data(w, r))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.elnu.com/ElnuDev/jichanorg/httputils"
|
"git.elnu.com/ElnuDev/shiritori-go/httputils"
|
||||||
. "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
|
. "git.elnu.com/ElnuDev/shiritori-go/shiritori"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateApiEvents(clients *ClientSet) httputils.Handler {
|
func GenerateApiEvents(clients *ClientSet) httputils.Handler {
|
||||||
|
|
|
@ -3,17 +3,16 @@ package api
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.elnu.com/ElnuDev/jichanorg/httputils"
|
"git.elnu.com/ElnuDev/shiritori-go/httputils"
|
||||||
. "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
|
. "git.elnu.com/ElnuDev/shiritori-go/shiritori"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateApiSubmit(clients *ClientSet) httputils.Handler {
|
func GenerateApiSubmit(clients *ClientSet) httputils.Handler {
|
||||||
return httputils.GenerateHandler(
|
return httputils.GenerateHandler(
|
||||||
"",
|
"",
|
||||||
func(w http.ResponseWriter, r *http.Request) bool {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseMultipartForm(0)
|
r.ParseMultipartForm(0)
|
||||||
clients.BroadcastWord(r.FormValue("word"))
|
clients.BroadcastWord(r.FormValue("word"))
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
[]string{http.MethodPost},
|
[]string{http.MethodPost},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package clients
|
package shiritori
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.elnu.com/ElnuDev/jichanorg/httputils"
|
"git.elnu.com/ElnuDev/shiritori-go/httputils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client = chan []httputils.SseEvent
|
type Client = chan []httputils.SseEvent
|
|
@ -1,3 +0,0 @@
|
||||||
module git.elnu.com/ElnuDev/jichanorg/shiritori
|
|
||||||
|
|
||||||
go 1.20
|
|
Reference in a new issue