github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/i18n/i18nyaml/i18nyaml.go (about) 1 // Provides YAML parsing for use with i18n package. 2 // 3 // YAML files are expected to be a simple list of name: value pairs. 4 // The group and locale either provided during the appropriate Load call 5 // or infered from the file name for functions which support that. 6 // File name convention is group.locale.yaml, e.g. "default.en-gb.yaml" 7 // contains text for the "default" group and "en-gb" locale. Multiple 8 // files can be provided for the same group by using the form: 9 // "group-additional.local.yaml". Example: "default-set1.en-gb.yaml", 10 // "default-set2.en-gb.yaml" - with the "-set1" part being ignored 11 // and only used to ensure the files sort properly - later ones taking 12 // higher priority. 13 // 14 package i18nyaml 15 16 import ( 17 "io" 18 "io/ioutil" 19 "log" 20 "net/http" 21 "os" 22 "path" 23 "sort" 24 "strings" 25 26 "github.com/gocaveman/caveman/i18n" 27 "github.com/gocaveman/caveman/webutil" 28 yaml "gopkg.in/yaml.v2" 29 ) 30 31 // this should create a namedsequence with the paths as names 32 func LoadDir(fs http.FileSystem, dirpath string) (webutil.NamedSequence, error) { 33 34 dirf, err := fs.Open(dirpath) 35 if err != nil { 36 return nil, err 37 } 38 defer dirf.Close() 39 40 var ret webutil.NamedSequence 41 42 fis, err := dirf.Readdir(-1) 43 if err != nil { 44 return nil, err 45 } 46 47 // sort by name reverse - later files get higher priority (lower sequence number) 48 sort.Slice(fis, func(i, j int) bool { 49 return fis[i].Name() >= fis[j].Name() 50 }) 51 52 // explicit sequence number to preserve sequence from file system 53 seqn := float64(0) 54 55 for _, fi := range fis { 56 seqn += 0.00001 57 58 baseName := path.Base(fi.Name()) 59 60 g, l, ok := FileNameParse(baseName) 61 if !ok { 62 continue 63 } 64 65 f, err := fs.Open(path.Join(dirpath, baseName)) 66 if err != nil { 67 return ret, err 68 } 69 defer f.Close() 70 71 tr, err := Load(f, g, l) 72 if err != nil { 73 return ret, err 74 } 75 76 ret = append(ret, webutil.NamedSequenceItem{Sequence: 50 + seqn, Name: fi.Name(), Value: tr}) 77 } 78 79 return ret, nil 80 } 81 82 func FileNameParse(fn string) (group, locale string, ok bool) { 83 fn = path.Base(fn) 84 parts := strings.Split(fn, ".") 85 if len(parts) != 3 { 86 return "", "", false 87 } 88 89 log.Printf("FIXME: need to support group-additional") 90 group = parts[0] 91 locale = parts[1] 92 ok = parts[2] == "yaml" 93 return 94 } 95 96 // Load reads a file containing YAML and returns a i18n.MapTranslator with the result. 97 func LoadFile(fpath, g, l string) (*i18n.MapTranslator, error) { 98 f, err := os.Open(fpath) 99 if err != nil { 100 return nil, err 101 } 102 defer f.Close() 103 104 return Load(f, g, l) 105 } 106 107 // Load reads a Reader containing YAML and returns a i18n.MapTranslator with the result. 108 func Load(r io.Reader, g, l string) (*i18n.MapTranslator, error) { 109 110 b, err := ioutil.ReadAll(r) 111 if err != nil { 112 return nil, err 113 } 114 115 ms := make(map[string]string) 116 117 err = yaml.Unmarshal(b, &ms) 118 if err != nil { 119 return nil, err 120 } 121 122 ret := i18n.NewMapTranslator() 123 for k, v := range ms { 124 ret.SetEntry(g, k, l, v) 125 } 126 127 return ret, nil 128 } 129 130 // Hm, should we have tooling here to go back and forth between db? This would allow us to 131 // use sqlite for local instances for the editor, and other db for other cases but then 132 // just rip through using Gorm and write out to file(s).