Skip to content

Instantly share code, notes, and snippets.

@iamtakingiteasy
Created January 18, 2021 22:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamtakingiteasy/c9ecdadd91e266bc16d106457748abee to your computer and use it in GitHub Desktop.
Save iamtakingiteasy/c9ecdadd91e266bc16d106457748abee to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/signal"
"path/filepath"
"regexp"
"sync"
"syscall"
)
type context struct {
gidx int
dict map[string]string
dictf io.Writer
apireg string
apikey string
}
var reg = regexp.MustCompile("[一-龯ぁ-ゔゞァ-・ヽヾ゛゜ー]+")
func traverseMap(ctx *context, m map[string]interface{}) map[string]interface{} {
for k, v := range m {
m[k] = traverse(ctx, v)
}
return m
}
func traverseArr(ctx *context, a []interface{}) []interface{} {
for idx, el := range a {
a[idx] = traverse(ctx, el)
}
return a
}
type transes struct {
Translations []*trans `json:"translations"`
}
type trans struct {
Text string `json:"text"`
To string `json:"to"`
}
func traverseStrTranslateBing(ctx *context, s string) (res string, ok bool) {
bs, err := json.Marshal([]interface{}{
map[string]interface{}{
"text": s,
},
})
if err != nil {
panic(err)
}
rdr := bytes.NewReader(bs)
req, err := http.NewRequest(http.MethodPost, "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en&from=ja-JP", rdr)
if err != nil {
panic(err)
}
req.Header.Set("Ocp-Apim-Subscription-Region", ctx.apireg)
req.Header.Set("Ocp-Apim-Subscription-Key", ctx.apikey)
req.Header.Set("content-type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
bs, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
err = resp.Body.Close()
if err != nil {
panic(err)
}
if resp.StatusCode / 100 != 2 {
panic(string(bs))
}
var ts []*transes
err = json.Unmarshal(bs, &ts)
if err != nil {
panic(err)
}
if len(ts) == 0 {
panic(fmt.Sprint("empty", ts))
}
if len(ts[0].Translations) == 0 {
panic(fmt.Sprint("empty nest", ts))
}
return ts[0].Translations[0].Text, true
}
func traverseStrTranslate(ctx *context, s string) string {
ctx.gidx++
fmt.Println(ctx.gidx, s)
tr, ok := ctx.dict[s]
if ok {
return tr
}
fmt.Println("making network request")
tr, ok = traverseStrTranslateBing(ctx, s)
if !ok {
panic("no translation")
}
err := writeEntry(ctx.dictf, s, tr)
if err != nil {
panic(err)
}
ctx.dict[s] = tr
return tr
}
func traverseStr(ctx *context, s string) string {
if reg.MatchString(s) {
return s + " (" + traverseStrTranslate(ctx, s) + ")"
}
return s
}
func traverse(ctx *context, v interface{}) interface{} {
m, ok := v.(map[string]interface{})
if ok {
return traverseMap(ctx, m)
}
a, ok := v.([]interface{})
if ok {
return traverseArr(ctx, a)
}
s, ok := v.(string)
if ok {
return traverseStr(ctx, s)
}
return v
}
func readDict(f io.Reader) (m map[string]string, err error) {
m = make(map[string]string)
bs := make([]byte, 8)
for {
_, err = io.ReadFull(f, bs)
if err != nil {
return
}
klen := binary.BigEndian.Uint32(bs[:4])
vlen := binary.BigEndian.Uint32(bs[4:8])
kvbs := make([]byte, klen+vlen)
_, err = io.ReadFull(f, kvbs)
if err != nil {
return
}
m[string(kvbs[:klen])] = string(kvbs[klen:])
}
return
}
func writeDict(f io.Writer, m map[string]string) (err error) {
for k, v := range m {
err = writeEntry(f, k, v)
if err != nil {
return
}
}
return nil
}
func writeEntry(f io.Writer, k, v string) (err error) {
bs := make([]byte, 8)
binary.BigEndian.PutUint32(bs[0:4], uint32(len(k)))
binary.BigEndian.PutUint32(bs[4:8], uint32(len(v)))
_, err = f.Write(bs)
if err != nil {
return
}
_, err = f.Write(([]byte)(k))
if err != nil {
return
}
_, err = f.Write(([]byte)(v))
if err != nil {
return
}
return nil
}
func main() {
region, ok := os.LookupEnv("API_REGION")
if !ok {
panic("no API_REGION env")
}
key, ok := os.LookupEnv("API_KEY")
if !ok {
panic("no API_KEY env")
}
if len(os.Args) < 2 {
panic("no file")
}
fname := os.Args[1]
dictfile := fname + "_dict"
wf, err := os.OpenFile(dictfile + "_temp", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0644)
if err != nil {
panic(err)
}
rf, err := os.OpenFile(dictfile, os.O_RDONLY, 0644)
if err != nil && !os.IsNotExist(err) {
panic(err)
}
dict := make(map[string]string)
if err == nil {
dict, err = readDict(rf)
if err != nil && err != io.EOF {
panic(err)
}
err = rf.Close()
if err != nil {
panic(err)
}
err = writeDict(wf, dict)
if err != nil {
panic(err)
}
}
fmt.Println(dict)
cfg := &context{
gidx: 0,
dict: dict,
dictf: wf,
apikey: key,
apireg: region,
}
fmt.Println("\nFILE ", fname)
bs, err := ioutil.ReadFile(fname)
if err != nil {
panic(err)
}
var m interface{}
err = json.Unmarshal(bs, &m)
if err != nil {
panic(err)
}
wg := &sync.WaitGroup{}
wg.Add(1)
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
err = wf.Close()
if err != nil {
panic(err)
}
err = os.Rename(dictfile + "_temp", dictfile)
if err != nil {
panic(err)
}
wg.Done()
}()
m = traverse(cfg, m)
bs, err = json.Marshal(m)
if err != nil {
panic(err)
}
err = ioutil.WriteFile("out/" + filepath.Base(fname), bs, 0644)
if err != nil {
panic(err)
}
close(c)
wg.Wait()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment