diff --git a/dict/.gitignore b/dict/.gitignore
new file mode 100644
index 0000000..30e4d8d
--- /dev/null
+++ b/dict/.gitignore
@@ -0,0 +1 @@
+JMdict.xml
\ No newline at end of file
diff --git a/dict/README.md b/dict/README.md
new file mode 100644
index 0000000..bb0c063
--- /dev/null
+++ b/dict/README.md
@@ -0,0 +1,14 @@
+# 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
\ No newline at end of file
diff --git a/dict/go.mod b/dict/go.mod
new file mode 100644
index 0000000..c0b1214
--- /dev/null
+++ b/dict/go.mod
@@ -0,0 +1,8 @@
+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
+)
diff --git a/dict/go.sum b/dict/go.sum
new file mode 100644
index 0000000..7ae614e
--- /dev/null
+++ b/dict/go.sum
@@ -0,0 +1,4 @@
+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=
diff --git a/dict/main.go b/dict/main.go
new file mode 100644
index 0000000..77755a9
--- /dev/null
+++ b/dict/main.go
@@ -0,0 +1,112 @@
+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))
+}
diff --git a/dict/static/index.html b/dict/static/index.html
new file mode 100644
index 0000000..cfebb64
--- /dev/null
+++ b/dict/static/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ jidict
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dict/templates/search.html b/dict/templates/search.html
new file mode 100644
index 0000000..b87fb1c
--- /dev/null
+++ b/dict/templates/search.html
@@ -0,0 +1,21 @@
+{{ $count := (len .) }}{{ if eq $count 0 }}No results{{ else }}{{ $count }} result{{ if ne $count 1}}s{{ end }}{{ end }}.
+{{- range . -}}
+
+
+ {{- if .Kanji -}}
+ {{- .Kanji -}}
+ {{- else -}}
+ {{- .Reading -}}
+ {{- end -}}
+
+ {{ if le (len .Definitions) 2 -}}
+
{{- index .Definitions 0 -}}
+ {{- else -}}
+
+ {{- range .Definitions }}
+ - {{- . -}}
+ {{- end }}
+
+ {{- end }}
+
+{{ end -}}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index c3320da..4010d61 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module git.elnu.com/ElnuDev/shiritori-go
+module git.elnu.com/ElnuDev/jichanorg
go 1.20
\ No newline at end of file
diff --git a/go.work b/go.work
new file mode 100644
index 0000000..c8b5819
--- /dev/null
+++ b/go.work
@@ -0,0 +1,5 @@
+go 1.20
+
+use ./httputils
+use ./dict
+use ./shiritori
\ No newline at end of file
diff --git a/httputils/go.mod b/httputils/go.mod
new file mode 100644
index 0000000..294c2f4
--- /dev/null
+++ b/httputils/go.mod
@@ -0,0 +1,3 @@
+module git.elnu.com/ElnuDev/jichanorg/httputils
+
+go 1.20
\ No newline at end of file
diff --git a/httputils/handler.go b/httputils/handler.go
index b9a2907..31bf89b 100644
--- a/httputils/handler.go
+++ b/httputils/handler.go
@@ -10,7 +10,7 @@ type Handler = func(http.ResponseWriter, *http.Request)
func GenerateHandler(
file string,
- handler func(http.ResponseWriter, *http.Request),
+ handler func(http.ResponseWriter, *http.Request) bool,
data func(http.ResponseWriter, *http.Request) any,
methods []string,
) Handler {
@@ -19,6 +19,7 @@ func GenerateHandler(
tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file)))
}
return func(w http.ResponseWriter, r *http.Request) {
+ tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file)))
for _, method := range methods {
if method == r.Method {
goto ok
@@ -27,8 +28,8 @@ func GenerateHandler(
w.WriteHeader(http.StatusMethodNotAllowed)
return
ok:
- handler(w, r)
- if tmpl != nil {
+ renderTemplate := handler(w, r)
+ if renderTemplate && tmpl != nil {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
tmpl.Execute(w, data(w, r))
}
diff --git a/shiritori/api/events.go b/shiritori/api/events.go
index 3cea08b..744c4cc 100644
--- a/shiritori/api/events.go
+++ b/shiritori/api/events.go
@@ -4,8 +4,8 @@ import (
"fmt"
"net/http"
- "git.elnu.com/ElnuDev/shiritori-go/httputils"
- . "git.elnu.com/ElnuDev/shiritori-go/shiritori"
+ "git.elnu.com/ElnuDev/jichanorg/httputils"
+ . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
)
func GenerateApiEvents(clients *ClientSet) httputils.Handler {
diff --git a/shiritori/api/submit.go b/shiritori/api/submit.go
index 0d46aee..e1c9c5f 100644
--- a/shiritori/api/submit.go
+++ b/shiritori/api/submit.go
@@ -3,16 +3,17 @@ package api
import (
"net/http"
- "git.elnu.com/ElnuDev/shiritori-go/httputils"
- . "git.elnu.com/ElnuDev/shiritori-go/shiritori"
+ "git.elnu.com/ElnuDev/jichanorg/httputils"
+ . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
)
func GenerateApiSubmit(clients *ClientSet) httputils.Handler {
return httputils.GenerateHandler(
"",
- func(w http.ResponseWriter, r *http.Request) {
+ func(w http.ResponseWriter, r *http.Request) bool {
r.ParseMultipartForm(0)
clients.BroadcastWord(r.FormValue("word"))
+ return true
},
nil,
[]string{http.MethodPost},
diff --git a/shiritori/clientset.go b/shiritori/clients/clients.go
similarity index 96%
rename from shiritori/clientset.go
rename to shiritori/clients/clients.go
index 27e7428..8b7d1da 100644
--- a/shiritori/clientset.go
+++ b/shiritori/clients/clients.go
@@ -1,9 +1,9 @@
-package shiritori
+package clients
import (
"fmt"
- "git.elnu.com/ElnuDev/shiritori-go/httputils"
+ "git.elnu.com/ElnuDev/jichanorg/httputils"
)
type Client = chan []httputils.SseEvent
diff --git a/shiritori/go.mod b/shiritori/go.mod
new file mode 100644
index 0000000..a7956a2
--- /dev/null
+++ b/shiritori/go.mod
@@ -0,0 +1,3 @@
+module git.elnu.com/ElnuDev/jichanorg/shiritori
+
+go 1.20
\ No newline at end of file
diff --git a/cmd/shiritori/main.go b/shiritori/main.go
similarity index 75%
rename from cmd/shiritori/main.go
rename to shiritori/main.go
index 7e71134..1a2266b 100644
--- a/cmd/shiritori/main.go
+++ b/shiritori/main.go
@@ -4,8 +4,8 @@ import (
"log"
"net/http"
- . "git.elnu.com/ElnuDev/shiritori-go/shiritori"
- "git.elnu.com/ElnuDev/shiritori-go/shiritori/api"
+ "git.elnu.com/ElnuDev/jichanorg/shiritori/api"
+ . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients"
)
func main() {
diff --git a/cmd/shiritori/static/index.html b/shiritori/static/index.html
similarity index 100%
rename from cmd/shiritori/static/index.html
rename to shiritori/static/index.html
diff --git a/cmd/shiritori/static/style.css b/shiritori/static/style.css
similarity index 100%
rename from cmd/shiritori/static/style.css
rename to shiritori/static/style.css