Merge branch 'master' into dev
This commit is contained in:
commit
d3593004fd
86
README.md
86
README.md
@ -19,77 +19,41 @@ contributions are welcome.
|
||||
|
||||
## Installation ##
|
||||
|
||||
Builds of Yomichan Import are currently available for Linux, Mac OS X, and Windows. The required version of
|
||||
Builds of Yomichan Import are currently available for Linux, Mac OS X, and Windows. The necessary version of
|
||||
[Zero-EPWING](https://foosoft.net/projects/zero-epwing) is included for processing EPWING dictionaries.
|
||||
|
||||
* [yomichan-import_linux.tar.gz](https://foosoft.net/projects/yomichan-import/dl/yomichan-import_linux.tar.gz): (GTK+ 3 required for GUI)
|
||||
* [yomichan-import_darwin.tar.gz](https://foosoft.net/projects/yomichan-import/dl/yomichan-import_darwin.tar.gz)
|
||||
* [yomichan-import_windows.zip](https://foosoft.net/projects/yomichan-import/dl/yomichan-import_windows.zip) (64 bit Vista or above, no console output)
|
||||
|
||||
## Using the Graphical Interface ##
|
||||
## Basic Usage ##
|
||||
|
||||
In most cases, it is sufficient to run the application without command line arguments and use the graphical interface.
|
||||
Follow the steps below to import your dictionary into Yomichan:
|
||||
Please follow the steps outlined below to import your custom dictionary into Yomichan:
|
||||
|
||||
1. Launch the `yomichan-import` executable.
|
||||
2. Specify the path to the dictionary you wish to convert (path to `CATALOGS` file for EPWING dictionaries).
|
||||
3. Specify a network port to use (the default port `9876` should be fine for most configurations).
|
||||
4. Specify the dictionary format from the provided options.
|
||||
5. Press the button labeled *Import dictionary...* and wait for processing to complete.
|
||||
6. Once you the message `starting dictionary server on port 9876...`, the dictionary data is ready to be imported.
|
||||
7. In Yomichan, open the options page and select the *Local dictionary* item in the dictionary importer drop-down menu.
|
||||
8. When `http://localhost:9876/index.json` is displayed in the address text-box, press the *Import* button to begin import.
|
||||
9. Wait for the import progress to complete (a progress bar is displayed during dictionary processing).
|
||||
9. Close Yomichan Import once the import process has finished.
|
||||
2. Specify the source path of the dictionary you wish to convert.
|
||||
3. Specify the target path of the dictionary ZIP archive that you wish to create.
|
||||
4. Press the button labeled *Import dictionary...* and wait for processing to complete.
|
||||
5. On the Yomichan options page, browse to the dictionary ZIP archive file you created.
|
||||
6. Wait for the import progress to complete before closing the options page.
|
||||
|
||||
[![Import window](https://foosoft.net/projects/yomichan-import/img/import-thumb.png)](https://foosoft.net/projects/yomichan-import/img/import.png)
|
||||
|
||||
## Using the Command Line ##
|
||||
|
||||
Yomichan Import can be used as a command line application. When executed with the `--help` argument, usage instructions
|
||||
will be displayed (except on Windows).
|
||||
|
||||
```
|
||||
Usage: yomichan-import [options] input-path [output-dir]
|
||||
https://foosoft.net/projects/yomichan-import/
|
||||
|
||||
Parameters:
|
||||
-format string
|
||||
dictionary format [edict|enamdict|kanjidic|epwing]
|
||||
-port int
|
||||
port to serve dictionary JSON on (default 9876)
|
||||
-pretty
|
||||
output prettified dictionary JSON
|
||||
-serve
|
||||
serve dictionary JSON for extension
|
||||
-stride int
|
||||
dictionary bank stride (default 10000)
|
||||
-title string
|
||||
dictionary title
|
||||
```
|
||||
|
||||
In most cases it is sufficient to simply provide the path to the dictionary resource you wish to process, without
|
||||
explicitly specifying a format. Yomichan Import will attempt to automatically determine the format of the dictionary
|
||||
based on the contents of the path:
|
||||
|
||||
| Format | Resource |
|
||||
| ------------ | ------------------------------------ |
|
||||
| **edict** | file named `JMDict_e.xml` |
|
||||
| **enamdict** | file named `JMNedict.xml` |
|
||||
| **kanjidic** | file named `kanjidic2.xml` |
|
||||
| **epwing** | directory with file named `CATALOGS` |
|
||||
|
||||
For example, if you wanted to process an EPWING dictionary titled Daijirin, you could do so with the following command
|
||||
(shown on Linux):
|
||||
|
||||
```
|
||||
$ ./yomichan-import dict/Kokugo/Daijirin/
|
||||
```
|
||||
|
||||
Yomichan Import will now begin the conversion process, which can take a couple of minutes to complete. Once you see the
|
||||
message `starting dictionary server on port 9876...` output to your console, you can use Yomichan to import the
|
||||
processed dictionary data using the same steps as described in the *Using the Graphical Interface* section.
|
||||
[![Importer](https://foosoft.net/projects/yomichan-import/img/import-thumb.png)](https://foosoft.net/projects/yomichan-import/img/import.png)
|
||||
|
||||
## License ##
|
||||
|
||||
MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
73
common.go
73
common.go
@ -23,10 +23,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@ -82,7 +84,6 @@ type dbKanji struct {
|
||||
Onyomi []string
|
||||
Kunyomi []string
|
||||
Tags []string
|
||||
Stats map[string]string
|
||||
Meanings []string
|
||||
}
|
||||
|
||||
@ -99,16 +100,11 @@ func (kanji *dbKanji) addTags(tags ...string) {
|
||||
func (kanji dbKanjiList) crush() [][]interface{} {
|
||||
var results [][]interface{}
|
||||
for _, k := range kanji {
|
||||
tags := k.Tags
|
||||
for name, value := range k.Stats {
|
||||
tags = append(tags, fmt.Sprintf("%s:%s", name, value))
|
||||
}
|
||||
|
||||
result := []interface{}{
|
||||
k.Character,
|
||||
strings.Join(k.Onyomi, " "),
|
||||
strings.Join(k.Kunyomi, " "),
|
||||
strings.Join(tags, " "),
|
||||
strings.Join(k.Tags, " "),
|
||||
}
|
||||
|
||||
for _, meaning := range k.Meanings {
|
||||
@ -121,9 +117,12 @@ func (kanji dbKanjiList) crush() [][]interface{} {
|
||||
return results
|
||||
}
|
||||
|
||||
func writeDb(outputDir, title string, revision string, termRecords [][]interface{}, kanjiRecords [][]interface{}, tagMeta map[string]dbTagMeta, stride int, pretty bool) error {
|
||||
func writeDb(outputPath, title, revision string, termRecords [][]interface{}, kanjiRecords [][]interface{}, tagMeta map[string]dbTagMeta, stride int, pretty bool) error {
|
||||
const DB_VERSION = 1
|
||||
|
||||
var zbuff bytes.Buffer
|
||||
zip := zip.NewWriter(&zbuff)
|
||||
|
||||
marshalJson := func(obj interface{}, pretty bool) ([]byte, error) {
|
||||
if pretty {
|
||||
return json.MarshalIndent(obj, "", " ")
|
||||
@ -148,13 +147,12 @@ func writeDb(outputDir, title string, revision string, termRecords [][]interface
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fp, err := os.Create(path.Join(outputDir, fmt.Sprintf("%s_bank_%d.json", prefix, i/stride+1)))
|
||||
zw, err := zip.Create(fmt.Sprintf("%s_bank_%d.json", prefix, i/stride+1))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
if _, err = fp.Write(bytes); err != nil {
|
||||
if _, err := zw.Write(bytes); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@ -164,10 +162,6 @@ func writeDb(outputDir, title string, revision string, termRecords [][]interface
|
||||
return bankCount, nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
var db struct {
|
||||
Title string `json:"title"`
|
||||
@ -196,17 +190,27 @@ func writeDb(outputDir, title string, revision string, termRecords [][]interface
|
||||
return err
|
||||
}
|
||||
|
||||
fp, err := os.Create(path.Join(outputDir, "index.json"))
|
||||
zw, err := zip.Create("index.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
if _, err := fp.Write(bytes); err != nil {
|
||||
if _, err := zw.Write(bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
zip.Close()
|
||||
|
||||
fp, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fp.Write(zbuff.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fp.Close()
|
||||
}
|
||||
|
||||
func appendStringUnique(target []string, source ...string) []string {
|
||||
@ -229,28 +233,29 @@ func hasString(needle string, haystack []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func detectFormat(path string) string {
|
||||
func detectFormat(path string) (string, error) {
|
||||
switch filepath.Base(path) {
|
||||
case "JMdict", "JMdict.xml", "JMdict_e", "JMdict_e.xml":
|
||||
return "edict", nil
|
||||
case "JMnedict", "JMnedict.xml":
|
||||
return "enamdict", nil
|
||||
case "kanjidic2", "kanjidic2.xml":
|
||||
return "kanjidic", nil
|
||||
case "CATALOGS":
|
||||
return "epwing", nil
|
||||
}
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
return "", err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
_, err := os.Stat(filepath.Join(path, "CATALOGS"))
|
||||
if err == nil {
|
||||
return "epwing"
|
||||
}
|
||||
} else {
|
||||
base := filepath.Base(path)
|
||||
switch base {
|
||||
case "JMdict_e.xml":
|
||||
return "edict"
|
||||
case "JMnedict.xml":
|
||||
return "enamdict"
|
||||
case "kanjidic2.xml":
|
||||
return "kanjidic"
|
||||
return "epwing", nil
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
return "", errors.New("unrecognized dictionary format")
|
||||
}
|
||||
|
50
edict.go
50
edict.go
@ -97,7 +97,7 @@ func jmdictBuildTagMeta(entities map[string]string) map[string]dbTagMeta {
|
||||
return tags
|
||||
}
|
||||
|
||||
func jmdictExtractTerms(edictEntry jmdict.JmdictEntry) []dbTerm {
|
||||
func jmdictExtractTerms(edictEntry jmdict.JmdictEntry, language string) []dbTerm {
|
||||
var terms []dbTerm
|
||||
|
||||
convert := func(reading jmdict.JmdictReading, kanji *jmdict.JmdictKanji) {
|
||||
@ -133,7 +133,21 @@ func jmdictExtractTerms(edictEntry jmdict.JmdictEntry) []dbTerm {
|
||||
continue
|
||||
}
|
||||
|
||||
term := dbTerm{Reading: termBase.Reading, Expression: termBase.Expression}
|
||||
term := dbTerm{
|
||||
Reading: termBase.Reading,
|
||||
Expression: termBase.Expression,
|
||||
}
|
||||
|
||||
for _, glossary := range sense.Glossary {
|
||||
if glossary.Language == nil && language == "" || glossary.Language != nil && language == *glossary.Language {
|
||||
term.Glossary = append(term.Glossary, glossary.Content)
|
||||
}
|
||||
}
|
||||
|
||||
if len(term.Glossary) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
term.addTags(termBase.Tags...)
|
||||
term.addTags(sense.PartsOfSpeech...)
|
||||
term.addTags(sense.Fields...)
|
||||
@ -146,10 +160,6 @@ func jmdictExtractTerms(edictEntry jmdict.JmdictEntry) []dbTerm {
|
||||
term.addTags(partsOfSpeech...)
|
||||
}
|
||||
|
||||
for _, glossary := range sense.Glossary {
|
||||
term.Glossary = append(term.Glossary, glossary.Content)
|
||||
}
|
||||
|
||||
jmdictBuildRules(&term)
|
||||
jmdictBuildScore(&term)
|
||||
|
||||
@ -172,7 +182,7 @@ func jmdictExtractTerms(edictEntry jmdict.JmdictEntry) []dbTerm {
|
||||
return terms
|
||||
}
|
||||
|
||||
func jmdictExportDb(inputPath, outputDir, title string, stride int, pretty bool) error {
|
||||
func jmdictExportDb(inputPath, outputPath, language, title string, stride int, pretty bool) error {
|
||||
reader, err := os.Open(inputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -184,9 +194,31 @@ func jmdictExportDb(inputPath, outputDir, title string, stride int, pretty bool)
|
||||
return err
|
||||
}
|
||||
|
||||
var langTag string
|
||||
switch language {
|
||||
case "dutch":
|
||||
langTag = "dut"
|
||||
case "french":
|
||||
langTag = "fre"
|
||||
case "german":
|
||||
langTag = "ger"
|
||||
case "hungarian":
|
||||
langTag = "hun"
|
||||
case "italian":
|
||||
langTag = "ita"
|
||||
case "russian":
|
||||
langTag = "rus"
|
||||
case "slovenian":
|
||||
langTag = "slv"
|
||||
case "spanish":
|
||||
langTag = "spa"
|
||||
case "swedish":
|
||||
langTag = "swe"
|
||||
}
|
||||
|
||||
var terms dbTermList
|
||||
for _, entry := range dict.Entries {
|
||||
terms = append(terms, jmdictExtractTerms(entry)...)
|
||||
terms = append(terms, jmdictExtractTerms(entry, langTag)...)
|
||||
}
|
||||
|
||||
if title == "" {
|
||||
@ -194,7 +226,7 @@ func jmdictExportDb(inputPath, outputDir, title string, stride int, pretty bool)
|
||||
}
|
||||
|
||||
return writeDb(
|
||||
outputDir,
|
||||
outputPath,
|
||||
title,
|
||||
JMDICT_REVISION,
|
||||
terms.crush(),
|
||||
|
@ -97,7 +97,7 @@ func jmnedictExtractTerms(enamdictEntry jmdict.JmnedictEntry) []dbTerm {
|
||||
return terms
|
||||
}
|
||||
|
||||
func jmnedictExportDb(inputPath, outputDir, title string, stride int, pretty bool) error {
|
||||
func jmnedictExportDb(inputPath, outputPath, language, title string, stride int, pretty bool) error {
|
||||
reader, err := os.Open(inputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -110,8 +110,8 @@ func jmnedictExportDb(inputPath, outputDir, title string, stride int, pretty boo
|
||||
}
|
||||
|
||||
var terms dbTermList
|
||||
for _, e := range dict.Entries {
|
||||
terms = append(terms, jmnedictExtractTerms(e)...)
|
||||
for _, entry := range dict.Entries {
|
||||
terms = append(terms, jmnedictExtractTerms(entry)...)
|
||||
}
|
||||
|
||||
if title == "" {
|
||||
@ -119,7 +119,7 @@ func jmnedictExportDb(inputPath, outputDir, title string, stride int, pretty boo
|
||||
}
|
||||
|
||||
return writeDb(
|
||||
outputDir,
|
||||
outputPath,
|
||||
title,
|
||||
JMNEDICT_REVISION,
|
||||
terms.crush(),
|
||||
|
14
epwing.go
14
epwing.go
@ -62,14 +62,22 @@ type epwingExtractor interface {
|
||||
getRevision() string
|
||||
}
|
||||
|
||||
func epwingExportDb(inputPath, outputDir, title string, stride int, pretty bool) error {
|
||||
func epwingExportDb(inputPath, outputPath, language, title string, stride int, pretty bool) error {
|
||||
stat, err := os.Stat(inputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data []byte
|
||||
var toolExec bool
|
||||
if stat.IsDir() {
|
||||
toolExec = true
|
||||
} else if filepath.Base(inputPath) == "CATALOGS" {
|
||||
inputPath = filepath.Dir(inputPath)
|
||||
toolExec = true
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if toolExec {
|
||||
toolPath := filepath.Join("bin", runtime.GOOS, "zero-epwing")
|
||||
if runtime.GOOS == "windows" {
|
||||
toolPath += ".exe"
|
||||
@ -193,7 +201,7 @@ func epwingExportDb(inputPath, outputDir, title string, stride int, pretty bool)
|
||||
}
|
||||
|
||||
return writeDb(
|
||||
outputDir,
|
||||
outputPath,
|
||||
title,
|
||||
strings.Join(revisions, ";"),
|
||||
terms.crush(),
|
||||
|
102
gui.go
102
gui.go
@ -44,36 +44,33 @@ func (l *logger) Write(p []byte) (n int, err error) {
|
||||
|
||||
func gui() error {
|
||||
return ui.Main(func() {
|
||||
pathEntry := ui.NewEntry()
|
||||
browseButton := ui.NewButton("Browse...")
|
||||
pathBox := ui.NewHorizontalBox()
|
||||
pathBox.Append(pathEntry, true)
|
||||
pathBox.Append(browseButton, false)
|
||||
pathSourceEntry := ui.NewEntry()
|
||||
pathSourceButton := ui.NewButton("Browse...")
|
||||
pathSourceBox := ui.NewHorizontalBox()
|
||||
pathSourceBox.Append(pathSourceEntry, true)
|
||||
pathSourceBox.Append(pathSourceButton, false)
|
||||
|
||||
portSpin := ui.NewSpinbox(0, 65535)
|
||||
portSpin.SetValue(DEFAULT_PORT)
|
||||
|
||||
formatCombo := ui.NewCombobox()
|
||||
formatCombo.Append("EPWING")
|
||||
formatCombo.Append("EDICT")
|
||||
formatCombo.Append("ENAMDICT")
|
||||
formatCombo.Append("KANJIDIC")
|
||||
formatCombo.SetSelected(0)
|
||||
pathTargetEntry := ui.NewEntry()
|
||||
pathTargetButton := ui.NewButton("Browse...")
|
||||
pathTargetBox := ui.NewHorizontalBox()
|
||||
pathTargetBox.Append(pathTargetEntry, true)
|
||||
pathTargetBox.Append(pathTargetButton, false)
|
||||
|
||||
titleEntry := ui.NewEntry()
|
||||
languageEntry := ui.NewEntry()
|
||||
outputEntry := ui.NewEntry()
|
||||
importButton := ui.NewButton("Import dictionary...")
|
||||
|
||||
mainBox := ui.NewVerticalBox()
|
||||
mainBox.Append(ui.NewLabel("Path to dictionary source (CATALOGS file for EPWING):"), false)
|
||||
mainBox.Append(pathBox, false)
|
||||
mainBox.Append(ui.NewLabel("Dictionary title (leave blank for default):"), false)
|
||||
mainBox.Append(ui.NewLabel("Path to dictionary source (CATALOGS file for EPWING)"), false)
|
||||
mainBox.Append(pathSourceBox, false)
|
||||
mainBox.Append(ui.NewLabel("Path to dictionary target ZIP file"), false)
|
||||
mainBox.Append(pathTargetBox, false)
|
||||
mainBox.Append(ui.NewLabel("Dictionary display title (blank for default)"), false)
|
||||
mainBox.Append(titleEntry, false)
|
||||
mainBox.Append(ui.NewLabel("Network port for extension server:"), false)
|
||||
mainBox.Append(portSpin, false)
|
||||
mainBox.Append(ui.NewLabel("Dictionary format:"), false)
|
||||
mainBox.Append(formatCombo, false)
|
||||
mainBox.Append(ui.NewLabel("Application output:"), false)
|
||||
mainBox.Append(ui.NewLabel("Dictionary glossary language (blank for English)"), false)
|
||||
mainBox.Append(languageEntry, false)
|
||||
mainBox.Append(ui.NewLabel("Application output"), false)
|
||||
mainBox.Append(outputEntry, false)
|
||||
mainBox.Append(ui.NewVerticalBox(), true)
|
||||
mainBox.Append(importButton, false)
|
||||
@ -82,9 +79,19 @@ func gui() error {
|
||||
window.SetMargined(true)
|
||||
window.SetChild(mainBox)
|
||||
|
||||
browseButton.OnClicked(func(*ui.Button) {
|
||||
pathSourceButton.OnClicked(func(*ui.Button) {
|
||||
if path := ui.OpenFile(window); len(path) > 0 {
|
||||
pathEntry.SetText(path)
|
||||
pathSourceEntry.SetText(path)
|
||||
}
|
||||
})
|
||||
|
||||
pathTargetButton.OnClicked(func(*ui.Button) {
|
||||
if path := ui.SaveFile(window); len(path) > 0 {
|
||||
if len(filepath.Ext(path)) == 0 {
|
||||
path += ".zip"
|
||||
}
|
||||
|
||||
pathTargetEntry.SetText(path)
|
||||
}
|
||||
})
|
||||
|
||||
@ -94,45 +101,42 @@ func gui() error {
|
||||
importButton.Disable()
|
||||
outputEntry.SetText("")
|
||||
|
||||
var (
|
||||
outputDir string
|
||||
err error
|
||||
)
|
||||
|
||||
if outputDir, err = makeTmpDir(); err != nil {
|
||||
ui.MsgBoxError(window, "Error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
inputPath := pathEntry.Text()
|
||||
inputPath := pathSourceEntry.Text()
|
||||
if len(inputPath) == 0 {
|
||||
ui.MsgBoxError(window, "Error", "You must specify a dictionary source path.")
|
||||
ui.MsgBoxError(window, "Error", "You must specify a dictionary source path")
|
||||
importButton.Enable()
|
||||
return
|
||||
}
|
||||
|
||||
format := []string{"epwing", "edict", "enamdict", "kanjidic"}[formatCombo.Selected()]
|
||||
if format == "epwing" {
|
||||
inputPath = filepath.Dir(inputPath)
|
||||
outputPath := pathTargetEntry.Text()
|
||||
if len(outputPath) == 0 {
|
||||
ui.MsgBoxError(window, "Error", "You must specify a dictionary target path")
|
||||
importButton.Enable()
|
||||
return
|
||||
}
|
||||
|
||||
format, err := detectFormat(inputPath)
|
||||
if err != nil {
|
||||
ui.MsgBoxError(window, "Error", "Unable to detect dictionary format")
|
||||
importButton.Enable()
|
||||
return
|
||||
}
|
||||
|
||||
title := titleEntry.Text()
|
||||
port := portSpin.Value()
|
||||
language := languageEntry.Text()
|
||||
|
||||
go func() {
|
||||
var success bool
|
||||
defer ui.QueueMain(func() {
|
||||
importButton.Enable()
|
||||
if success {
|
||||
ui.MsgBox(window, "Success", "Conversion process complete")
|
||||
} else {
|
||||
ui.MsgBox(window, "Error", "Conversion process failed")
|
||||
}
|
||||
})
|
||||
|
||||
if err := exportDb(inputPath, outputDir, format, title, DEFAULT_STRIDE, false); err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := serveDb(outputDir, port); err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
success = exportDb(inputPath, outputPath, format, language, title, DEFAULT_STRIDE, false) == nil
|
||||
}()
|
||||
})
|
||||
|
||||
|
105
kanjidic.go
105
kanjidic.go
@ -23,26 +23,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/FooSoft/jmdict"
|
||||
)
|
||||
|
||||
const KANJIDIC_REVISION = "kanjidic2"
|
||||
const KANJIDIC_REVISION = "kanjidic1"
|
||||
|
||||
func kanjidicExtractKanji(entry jmdict.KanjidicCharacter) dbKanji {
|
||||
kanji := dbKanji{
|
||||
Character: entry.Literal,
|
||||
Stats: make(map[string]string),
|
||||
func kanjidicExtractKanji(entry jmdict.KanjidicCharacter, language string) *dbKanji {
|
||||
if entry.ReadingMeaning == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
kanji := dbKanji{Character: entry.Literal}
|
||||
|
||||
for _, m := range entry.ReadingMeaning.Meanings {
|
||||
if m.Language == nil && language == "" || m.Language != nil && language == *m.Language {
|
||||
kanji.Meanings = append(kanji.Meanings, m.Meaning)
|
||||
}
|
||||
}
|
||||
|
||||
if len(kanji.Meanings) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if level := entry.Misc.JlptLevel; level != nil {
|
||||
kanji.Stats["jlpt level"] = *level
|
||||
kanji.addTags(fmt.Sprintf("jlpt:%s", *level))
|
||||
}
|
||||
|
||||
if grade := entry.Misc.Grade; grade != nil {
|
||||
kanji.Stats["school grade"] = *grade
|
||||
kanji.addTags(fmt.Sprintf("grade:%s", *grade))
|
||||
if gradeInt, err := strconv.Atoi(*grade); err == nil {
|
||||
if gradeInt >= 1 && gradeInt <= 8 {
|
||||
kanji.addTags("jouyou")
|
||||
@ -53,34 +65,28 @@ func kanjidicExtractKanji(entry jmdict.KanjidicCharacter) dbKanji {
|
||||
}
|
||||
|
||||
for _, number := range entry.DictionaryNumbers {
|
||||
kanji.Stats[number.Type] = number.Value
|
||||
if number.Type == "heisig" {
|
||||
kanji.addTags(fmt.Sprintf("heisig:%s", number.Value))
|
||||
}
|
||||
}
|
||||
|
||||
if counts := entry.Misc.StrokeCounts; len(counts) > 0 {
|
||||
kanji.Stats["stroke count"] = counts[0]
|
||||
kanji.addTags(fmt.Sprintf("strokes:%s", counts[0]))
|
||||
}
|
||||
|
||||
if entry.ReadingMeaning != nil {
|
||||
for _, m := range entry.ReadingMeaning.Meanings {
|
||||
if m.Language == nil || *m.Language == "en" {
|
||||
kanji.Meanings = append(kanji.Meanings, m.Meaning)
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range entry.ReadingMeaning.Readings {
|
||||
switch r.Type {
|
||||
case "ja_on":
|
||||
kanji.Onyomi = append(kanji.Onyomi, r.Value)
|
||||
case "ja_kun":
|
||||
kanji.Kunyomi = append(kanji.Kunyomi, r.Value)
|
||||
}
|
||||
for _, r := range entry.ReadingMeaning.Readings {
|
||||
switch r.Type {
|
||||
case "ja_on":
|
||||
kanji.Onyomi = append(kanji.Onyomi, r.Value)
|
||||
case "ja_kun":
|
||||
kanji.Kunyomi = append(kanji.Kunyomi, r.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return kanji
|
||||
return &kanji
|
||||
}
|
||||
|
||||
func kanjidicExportDb(inputPath, outputDir, title string, stride int, pretty bool) error {
|
||||
func kanjidicExportDb(inputPath, outputPath, language, title string, stride int, pretty bool) error {
|
||||
reader, err := os.Open(inputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -92,38 +98,31 @@ func kanjidicExportDb(inputPath, outputDir, title string, stride int, pretty boo
|
||||
return err
|
||||
}
|
||||
|
||||
var langTag string
|
||||
switch language {
|
||||
case "french":
|
||||
langTag = "fr"
|
||||
case "spanish":
|
||||
langTag = "es"
|
||||
case "portuguese":
|
||||
langTag = "pt"
|
||||
}
|
||||
|
||||
var kanji dbKanjiList
|
||||
for _, entry := range dict.Characters {
|
||||
kanji = append(kanji, kanjidicExtractKanji(entry))
|
||||
kanjiCurr := kanjidicExtractKanji(entry, langTag)
|
||||
if kanjiCurr != nil {
|
||||
kanji = append(kanji, *kanjiCurr)
|
||||
}
|
||||
}
|
||||
|
||||
tagMeta := map[string]dbTagMeta{
|
||||
"jouyou": {Notes: "included in list of regular-use characters", Category: "frequent", Order: -5},
|
||||
"jinmeiyou": {Notes: "included in list of characters for use in personal names", Category: "frequent", Order: -5},
|
||||
"nelson_c": {Notes: "Modern Reader's Japanese-English Character Dictionary"},
|
||||
"nelson_n": {Notes: "The New Nelson Japanese-English Character Dictionary"},
|
||||
"halpern_njecd": {Notes: "New Japanese-English Character Dictionary"},
|
||||
"halpern_kkd": {Notes: "Kodansha Kanji Dictionary"},
|
||||
"halpern_kkld": {Notes: "Kanji Learners Dictionary"},
|
||||
"halpern_kkld_2ed": {Notes: "Kanji Learners Dictionary"},
|
||||
"heisig": {Notes: "Remembering The Kanji"},
|
||||
"heisig6": {Notes: "Remembering The Kanji, Sixth Ed."},
|
||||
"gakken": {Notes: "A New Dictionary of Kanji Usage"},
|
||||
"oneill_names": {Notes: "Japanese Names"},
|
||||
"oneill_kk": {Notes: "Essential Kanji"},
|
||||
"moro": {Notes: "Daikanwajiten"},
|
||||
"henshall": {Notes: "A Guide To Remembering Japanese Characters"},
|
||||
"sh_kk": {Notes: "Kanji and Kana"},
|
||||
"sh_kk2": {Notes: "Kanji and Kana"},
|
||||
"sakade": {Notes: "A Guide To Reading and Writing Japanese"},
|
||||
"jf_cards": {Notes: "Japanese Kanji Flashcards"},
|
||||
"henshall3": {Notes: "A Guide To Reading and Writing Japanese"},
|
||||
"tutt_cards": {Notes: "Tuttle Kanji Cards"},
|
||||
"crowley": {Notes: "The Kanji Way to Japanese Language Power"},
|
||||
"kanji_in_context": {Notes: "Kanji in Context"},
|
||||
"busy_people": {Notes: "Japanese For Busy People"},
|
||||
"kodansha_compact": {Notes: "Kodansha Compact Kanji Guide"},
|
||||
"maniette": {Notes: "Les Kanjis dans la tete"},
|
||||
"jouyou": {Notes: "included in list of regular-use characters", Category: "frequent", Order: -5},
|
||||
"jinmeiyou": {Notes: "included in list of characters for use in personal names", Category: "frequent", Order: -5},
|
||||
"jlpt": {Notes: "corresponding Japanese Language Proficiency Test level"},
|
||||
"grade": {Notes: "school grade level at which the character is taught"},
|
||||
"strokes": {Notes: "number of strokes needed to write the character"},
|
||||
"heisig": {Notes: "frame number in Remembering the Kanji"},
|
||||
}
|
||||
|
||||
if title == "" {
|
||||
@ -131,7 +130,7 @@ func kanjidicExportDb(inputPath, outputDir, title string, stride int, pretty boo
|
||||
}
|
||||
|
||||
return writeDb(
|
||||
outputDir,
|
||||
outputPath,
|
||||
title,
|
||||
KANJIDIC_REVISION,
|
||||
nil,
|
||||
|
76
main.go
76
main.go
@ -28,25 +28,26 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_STRIDE = 10000
|
||||
DEFAULT_PORT = 9876
|
||||
DEFAULT_STRIDE = 10000
|
||||
DEFAULT_PORT = 9876
|
||||
DEFAULT_LANGUAGE = "english"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] input-path [output-dir]\n", path.Base(os.Args[0]))
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] input-path output-path\n", path.Base(os.Args[0]))
|
||||
fmt.Fprint(os.Stderr, "https://foosoft.net/projects/yomichan-import/\n\n")
|
||||
fmt.Fprint(os.Stderr, "Parameters:\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func exportDb(inputPath, outputDir, format, title string, stride int, pretty bool) error {
|
||||
handlers := map[string]func(string, string, string, int, bool) error{
|
||||
func exportDb(inputPath, outputPath, format, language, title string, stride int, pretty bool) error {
|
||||
handlers := map[string]func(string, string, string, string, int, bool) error{
|
||||
"edict": jmdictExportDb,
|
||||
"enamdict": jmnedictExportDb,
|
||||
"epwing": epwingExportDb,
|
||||
@ -54,18 +55,19 @@ func exportDb(inputPath, outputDir, format, title string, stride int, pretty boo
|
||||
"rikai": rikaiExportDb,
|
||||
}
|
||||
|
||||
handler, ok := handlers[format]
|
||||
handler, ok := handlers[strings.ToLower(format)]
|
||||
if !ok {
|
||||
return errors.New("unrecognized dictionray format")
|
||||
return errors.New("unrecognized dictionary format")
|
||||
}
|
||||
|
||||
log.Printf("converting '%s' to '%s' in '%s' format...", inputPath, outputDir, format)
|
||||
return handler(inputPath, outputDir, title, stride, pretty)
|
||||
}
|
||||
log.Printf("converting '%s' to '%s' in '%s' format...", inputPath, outputPath, format)
|
||||
if err := handler(inputPath, outputPath, strings.ToLower(language), title, stride, pretty); err != nil {
|
||||
log.Printf("conversion process failed: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
func serveDb(serveDir string, port int) error {
|
||||
log.Printf("starting dictionary server on port %d...\n", port)
|
||||
return http.ListenAndServe(fmt.Sprintf(":%d", port), http.FileServer(http.Dir(serveDir)))
|
||||
log.Print("conversion process complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeTmpDir() (string, error) {
|
||||
@ -74,62 +76,42 @@ func makeTmpDir() (string, error) {
|
||||
|
||||
func main() {
|
||||
var (
|
||||
format = flag.String("format", "", "dictionary format [edict|enamdict|epwing|kanjidic|rikai]")
|
||||
port = flag.Int("port", DEFAULT_PORT, "port to serve dictionary JSON on")
|
||||
pretty = flag.Bool("pretty", false, "output prettified dictionary JSON")
|
||||
serve = flag.Bool("serve", false, "serve dictionary JSON for extension")
|
||||
stride = flag.Int("stride", DEFAULT_STRIDE, "dictionary bank stride")
|
||||
title = flag.String("title", "", "dictionary title")
|
||||
format = flag.String("format", "", "dictionary format [edict|enamdict|epwing|kanjidic|rikai]")
|
||||
language = flag.String("language", DEFAULT_LANGUAGE, "dictionary language (if supported)")
|
||||
title = flag.String("title", "", "dictionary title")
|
||||
stride = flag.Int("stride", DEFAULT_STRIDE, "dictionary bank stride")
|
||||
pretty = flag.Bool("pretty", false, "output prettified dictionary JSON")
|
||||
)
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
var (
|
||||
inputPath string
|
||||
outputDir string
|
||||
)
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
if flag.NArg() != 2 {
|
||||
if err := gui(); err == nil {
|
||||
return
|
||||
} else {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
} else {
|
||||
inputPath = flag.Arg(0)
|
||||
if flag.NArg() > 1 {
|
||||
outputDir = flag.Arg(1)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
inputPath = flag.Arg(0)
|
||||
outputPath = flag.Arg(1)
|
||||
)
|
||||
|
||||
if _, err := os.Stat(inputPath); err != nil {
|
||||
log.Fatalf("dictionary path '%s' does not exist", inputPath)
|
||||
}
|
||||
|
||||
if *format == "" {
|
||||
if *format = detectFormat(inputPath); *format == "" {
|
||||
log.Fatal("failed to detect dictionary format")
|
||||
}
|
||||
}
|
||||
|
||||
if outputDir == "" {
|
||||
var err error
|
||||
if outputDir, err = makeTmpDir(); err != nil {
|
||||
if *format, err = detectFormat(inputPath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
*serve = true
|
||||
}
|
||||
|
||||
if err := exportDb(inputPath, outputDir, *format, *title, *stride, *pretty); err != nil {
|
||||
if err := exportDb(inputPath, outputPath, *format, *language, *title, *stride, *pretty); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if *serve {
|
||||
if err := serveDb(outputDir, *port); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user