├── .gitignore
├── .goreleaser.yml
├── LICENSE
├── README.md
├── dict
└── dict.go
├── go.mod
├── go.sum
├── main.go
└── utils
└── xdg.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # others
15 | dist
16 | tango
17 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | project_name: tango
2 | before:
3 | hooks:
4 | - go mod tidy
5 | builds:
6 | - env:
7 | - CGO_ENABLED=0
8 | goos:
9 | - linux
10 | - darwin
11 | ldflags:
12 | - -s -w -X main.version=v{{ .Version }} -X main.gitCommit={{ .ShortCommit }} -X main.buildDate={{ .Date }}
13 | archives:
14 | - replacements:
15 | darwin: Darwin
16 | linux: Linux
17 | windows: Windows
18 | 386: i386
19 | amd64: x86_64
20 | brews:
21 | - tap:
22 | owner: masakichi
23 | name: homebrew-tap
24 | folder: Formula
25 | license: "MIT"
26 | homepage: "https://github.com/masakichi/tango"
27 | description: "Tango(単語) is a local command-line Japanese dictionary tool using yomichan's dictionary files."
28 | checksum:
29 | name_template: 'checksums.txt'
30 | snapshot:
31 | name_template: "{{ incpatch .Version }}-dev"
32 | changelog:
33 | sort: asc
34 | filters:
35 | exclude:
36 | - '^docs:'
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Gimo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tango(単語)
2 |
3 | Tango(単語) is a local command-line Japanese dictionary tool using yomichan's dictionary files.
4 |
5 |
6 |
7 |
8 |
9 | ## Install
10 |
11 | If you use Homebrew you can:
12 |
13 | ```bash
14 | brew install masakichi/tap/tango
15 | ```
16 |
17 | For Go 1.16+
18 |
19 | ```bash
20 | go install github.com/masakichi/tango@latest
21 | ```
22 |
23 | Or use `go get`
24 |
25 | ```bash
26 | GO111MODULE=on go get github.com/masakichi/tango
27 | ```
28 |
29 | ## Configuration
30 |
31 | After install tango, you will need to import dictionaries as you wish.
32 |
33 | Dictionariy zip files can be found in yomichan's homepage: https://foosoft.net/projects/yomichan/index.html#dictionaries
34 |
35 | You can directly import the downloaded dictionary zip file by calling `tango -import path_to_dictionary.zip`.
36 |
37 | ## Usage
38 |
39 | ```
40 | $ tango -h
41 |
42 | tango: A CLI Japanese Dictionary Tool
43 | Usage: tango 単語
44 |
45 | Other Commands of tango:
46 | -import string
47 | import yomichan's dictionary zip file
48 | -list
49 | list all dictionaries
50 | -version
51 | print tango app version
52 | ```
53 |
--------------------------------------------------------------------------------
/dict/dict.go:
--------------------------------------------------------------------------------
1 | package dict
2 |
3 | import (
4 | "archive/zip"
5 | "database/sql"
6 | "encoding/json"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "strings"
12 | )
13 |
14 | // https://github.com/FooSoft/yomichan-import/blob/5fe039e5f66ccad397f97a44a9f406a5a68a9438/common.go
15 | //type dbTerm struct {
16 | // Expression string
17 | // Reading string
18 | // DefinitionTags []string
19 | // Rules []string
20 | // Score int
21 | // Glossary []string
22 | // Sequence int
23 | // TermTags []string
24 | //}
25 | type Term struct {
26 | Expression string
27 | Reading string
28 | Glossaries []string
29 | Dict string
30 | }
31 |
32 | type Dict struct {
33 | ID int
34 | Title string `json:"title"`
35 | Format int `json:"format"`
36 | Revision string `json:"revision"`
37 | }
38 |
39 | func (t *Term) UnmarshalJSON(data []byte) error {
40 | var v []interface{}
41 | if err := json.Unmarshal(data, &v); err != nil {
42 | return err
43 | }
44 | t.Expression = v[0].(string)
45 | t.Reading = v[1].(string)
46 | //t.Glossaries = v[5].([]string)
47 | t.Glossaries = func(gs []interface{}) []string {
48 | result := []string{}
49 | for _, g := range gs {
50 | result = append(result, g.(string))
51 | }
52 | return result
53 | }(v[5].([]interface{}))
54 | return nil
55 | }
56 |
57 | func InitDictDB(db *sql.DB) {
58 | db.Exec(`CREATE TABLE IF NOT EXISTS terms (id INTEGER PRIMARY KEY, expression TEXT, reading TEXT, glossaries TEXT, dict_id INTEGER)`)
59 | db.Exec(`CREATE INDEX IF NOT EXISTS idx_expr ON terms (expression)`)
60 | db.Exec(`CREATE INDEX IF NOT EXISTS idx_read ON terms (reading)`)
61 | db.Exec(`CREATE TABLE IF NOT EXISTS dicts (id INTEGER PRIMARY KEY, title TEXT, format INTEGER, revision TEXT)`)
62 | }
63 |
64 | func ImportDictDB(db *sql.DB, dictName string) {
65 | r, err := zip.OpenReader(dictName)
66 | if err != nil {
67 | log.Fatal(err)
68 | }
69 | defer r.Close()
70 | var dict Dict
71 | for _, f := range r.File {
72 | if f.Name == "index.json" {
73 | rc, _ := f.Open()
74 | content, _ := ioutil.ReadAll(rc)
75 | json.Unmarshal(content, &dict)
76 | rc.Close()
77 | }
78 | }
79 | if &dict == nil {
80 | log.Fatal("bad dict")
81 | }
82 |
83 | var dictTitle string
84 | row := db.QueryRow(`SELECT title FROM dicts WHERE title = ?`, dict.Title)
85 | row.Scan(&dictTitle)
86 | if dictTitle != "" {
87 | log.Fatal("dict already exist")
88 | }
89 |
90 | tx, _ := db.Begin()
91 |
92 | rv, err := tx.Exec(
93 | `INSERT INTO dicts (title, format, revision) VALUES (?,?,?)`,
94 | dict.Title, dict.Format, dict.Revision,
95 | )
96 | if err != nil {
97 | log.Fatal(err)
98 | }
99 | dictID, _ := rv.LastInsertId()
100 |
101 | termList := []Term{}
102 | for _, f := range r.File {
103 | if !strings.HasPrefix(f.Name, "term_bank_") {
104 | continue
105 | }
106 | rc, _ := f.Open()
107 | content, _ := ioutil.ReadAll(rc)
108 |
109 | var v []Term
110 | json.Unmarshal(content, &v)
111 | termList = append(termList, v...)
112 | rc.Close()
113 | }
114 | canCommit := true
115 | for _, t := range termList {
116 | glossaries, _ := json.Marshal(t.Glossaries)
117 | _, err := tx.Exec(
118 | `INSERT INTO terms (expression, reading, glossaries, dict_id) VALUES (?,?,?,?)`,
119 | t.Expression, t.Reading, string(glossaries), dictID,
120 | )
121 | if err != nil {
122 | fmt.Println(err)
123 | canCommit = false
124 | break
125 | }
126 | }
127 | if canCommit {
128 | tx.Commit()
129 | fmt.Println("import done")
130 | } else {
131 | tx.Rollback()
132 | }
133 | }
134 |
135 | func getAllDicts(db *sql.DB) ([]Dict, error) {
136 | rows, err := db.Query(
137 | `SELECT id, title, format, revision
138 | FROM dicts
139 | ORDER BY id ASC`,
140 | )
141 | if err != nil {
142 | return nil, err
143 | }
144 | var result []Dict
145 | for rows.Next() {
146 | var d Dict
147 | rows.Scan(&d.ID, &d.Title, &d.Format, &d.Revision)
148 | result = append(result, d)
149 | }
150 | return result, nil
151 | }
152 |
153 | func defineWord(db *sql.DB, w string) ([]Term, error) {
154 | rows, err := db.Query(
155 | `SELECT expression, reading, glossaries, d.title
156 | FROM terms t
157 | LEFT JOIN dicts d on t.dict_id = d.id
158 | WHERE expression = ? OR reading = ?`,
159 | w, w,
160 | )
161 | if err != nil {
162 | return nil, err
163 | }
164 | var result []Term
165 | for rows.Next() {
166 | var t Term
167 | var glossaries string
168 | rows.Scan(&t.Expression, &t.Reading, &glossaries, &t.Dict)
169 | json.Unmarshal([]byte(glossaries), &t.Glossaries)
170 | result = append(result, t)
171 | }
172 | return result, nil
173 | }
174 |
175 | func PrintTerms(db *sql.DB) {
176 | terms, err := defineWord(db, os.Args[1])
177 | if err != nil {
178 | log.Fatal(err)
179 | }
180 | for _, t := range terms {
181 | fmt.Printf("%s(%s)\n[%s]\n", t.Expression, t.Reading, t.Dict)
182 | fmt.Println(strings.TrimSuffix(strings.Join(t.Glossaries, "\n"), "\n") + "\n")
183 | }
184 | }
185 |
186 | func PrintDicts(db *sql.DB) {
187 | dicts, err := getAllDicts(db)
188 | if err != nil {
189 | log.Fatal(err)
190 | }
191 | for _, d := range dicts {
192 | fmt.Printf("[%d] %s Format: %d, Revision: %s\n", d.ID, d.Title, d.Format, d.Revision)
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/masakichi/tango
2 |
3 | go 1.17
4 |
5 | require modernc.org/sqlite v1.13.1
6 |
7 | require (
8 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
9 | github.com/mattn/go-isatty v0.0.12 // indirect
10 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
11 | golang.org/x/mod v0.3.0 // indirect
12 | golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect
13 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect
14 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
15 | lukechampine.com/uint128 v1.1.1 // indirect
16 | modernc.org/cc/v3 v3.34.0 // indirect
17 | modernc.org/ccgo/v3 v3.11.2 // indirect
18 | modernc.org/libc v1.11.3 // indirect
19 | modernc.org/mathutil v1.4.1 // indirect
20 | modernc.org/memory v1.0.5 // indirect
21 | modernc.org/opt v0.1.1 // indirect
22 | modernc.org/strutil v1.1.1 // indirect
23 | modernc.org/token v1.0.0 // indirect
24 | )
25 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
2 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
3 | github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
4 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
6 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
7 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
8 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
9 | github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
10 | github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
11 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
12 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
13 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
14 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
15 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
16 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
17 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
18 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
19 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
20 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
21 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
22 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
23 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
24 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
25 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
26 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
27 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
28 | golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
29 | golang.org/x/sys v0.0.0-20210902050250-f475640dd07b h1:S7hKs0Flbq0bbc9xgYt4stIEG1zNDFqyrPwAX2Wj/sE=
30 | golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
31 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
32 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
33 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
34 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
35 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
36 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
37 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
38 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
39 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
40 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
41 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
42 | lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
43 | lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
44 | modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
45 | modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
46 | modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
47 | modernc.org/cc/v3 v3.34.0 h1:dFhZc/HKR3qp92sYQxKRRaDMz+sr1bwcFD+m7LSCrAs=
48 | modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
49 | modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
50 | modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
51 | modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
52 | modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
53 | modernc.org/ccgo/v3 v3.11.2 h1:gqa8PQ2v7SjrhHCgxUO5dzoAJWSLAveJqZTNkPCN0kc=
54 | modernc.org/ccgo/v3 v3.11.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE=
55 | modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
56 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
57 | modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
58 | modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
59 | modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
60 | modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
61 | modernc.org/libc v1.11.3 h1:q//spBhqp23lC/if8/o8hlyET57P8mCZqrqftzT2WmY=
62 | modernc.org/libc v1.11.3/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
63 | modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
64 | modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
65 | modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
66 | modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
67 | modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
68 | modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
69 | modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
70 | modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
71 | modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
72 | modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
73 | modernc.org/sqlite v1.13.1 h1:s/qk6VTTVyQIyhVNWa50whBBcI3+2oREbx85t227iOo=
74 | modernc.org/sqlite v1.13.1/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg=
75 | modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
76 | modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
77 | modernc.org/tcl v1.5.9 h1:DZMfR+RDJRhcrmMEMTJgVIX+Wf5qhfVX0llI0rsc20w=
78 | modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE=
79 | modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
80 | modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
81 | modernc.org/z v1.1.2 h1:IjjzDsIFbl0wuF2KfwvdyUAJVwxD4iwZ6akLNiDoClM=
82 | modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
83 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "os"
9 | "path/filepath"
10 | "runtime"
11 | "strings"
12 |
13 | "github.com/masakichi/tango/dict"
14 | "github.com/masakichi/tango/utils"
15 | _ "modernc.org/sqlite"
16 | )
17 |
18 | var (
19 | appName = "tango"
20 | version = "1.2.0-dev"
21 | gitCommit string
22 | buildDate string
23 | )
24 |
25 | var (
26 | dataDir = utils.GetDataDir(appName)
27 | databasePath = filepath.Join(dataDir, "tango.db")
28 | )
29 |
30 | func initDatabase() *sql.DB {
31 | if _, err := os.Stat(dataDir); os.IsNotExist(err) {
32 | os.MkdirAll(dataDir, os.ModePerm)
33 | }
34 | db, err := sql.Open("sqlite", databasePath)
35 | if err != nil {
36 | log.Fatal(err)
37 | }
38 | if _, err := os.Stat(databasePath); os.IsNotExist(err) {
39 | dict.InitDictDB(db)
40 | }
41 | return db
42 | }
43 |
44 | func main() {
45 |
46 | listFlag := flag.Bool("list", false, "list all dictionaries")
47 | importFlag := flag.String("import", "", "import yomichan's dictionary zip file")
48 | versionFlag := flag.Bool("version", false, "print tango app version")
49 | flag.Usage = func() {
50 | fmt.Fprintf(os.Stderr, "%s: A CLI Japanese Dictionary Tool\n", appName)
51 | fmt.Fprintf(os.Stderr, "Usage: %s %s\n\n", os.Args[0], "単語")
52 | fmt.Fprintf(os.Stderr, "Other Commands of %s:\n", os.Args[0])
53 | flag.PrintDefaults()
54 | }
55 |
56 | flag.Parse()
57 |
58 | if len(os.Args) < 2 {
59 | flag.Usage()
60 | return
61 | }
62 |
63 | db := initDatabase()
64 | defer db.Close()
65 |
66 | if !strings.HasPrefix(os.Args[1], "-") {
67 | dict.PrintTerms(db)
68 | return
69 | }
70 |
71 | if *importFlag != "" {
72 | dict.ImportDictDB(db, *importFlag)
73 | return
74 | }
75 |
76 | if *listFlag {
77 | dict.PrintDicts(db)
78 | return
79 | }
80 |
81 | if *versionFlag {
82 | if gitCommit != "" {
83 | version += "-" + gitCommit
84 | }
85 | if buildDate == "" {
86 | buildDate = "unknown"
87 | }
88 | osArch := runtime.GOOS + "/" + runtime.GOARCH
89 | versionString := fmt.Sprintf("%s %s %s BuildDate=%s",
90 | appName, version, osArch, buildDate)
91 | fmt.Println(versionString)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/utils/xdg.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | )
7 |
8 | func GetDataDir(name string) string {
9 | var basedir string
10 | if env := os.Getenv("XDG_DATA_HOME"); env != "" {
11 | basedir = env
12 | } else {
13 | basedir = filepath.Join(os.Getenv("HOME"), ".local", "share")
14 | }
15 | return filepath.Join(basedir, name)
16 | }
17 |
--------------------------------------------------------------------------------