generated from ElnuDev/go-project
parent
f8ccf62570
commit
01204ffc81
@ -0,0 +1 @@
|
|||||||
|
JMdict.xml
|
@ -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
|
@ -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
|
||||||
|
)
|
@ -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=
|
@ -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))
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<!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>
|
@ -0,0 +1,21 @@
|
|||||||
|
<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 -}}
|
Reference in new issue