github.com/liquid-dev/text@v0.3.3-liquid/unicode/cldr/decode.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cldr 6 7 import ( 8 "archive/zip" 9 "bytes" 10 "encoding/xml" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "log" 15 "os" 16 "path/filepath" 17 "regexp" 18 ) 19 20 // A Decoder loads an archive of CLDR data. 21 type Decoder struct { 22 dirFilter []string 23 sectionFilter []string 24 loader Loader 25 cldr *CLDR 26 curLocale string 27 } 28 29 // SetSectionFilter takes a list top-level LDML element names to which 30 // evaluation of LDML should be limited. It automatically calls SetDirFilter. 31 func (d *Decoder) SetSectionFilter(filter ...string) { 32 d.sectionFilter = filter 33 // TODO: automatically set dir filter 34 } 35 36 // SetDirFilter limits the loading of LDML XML files of the specied directories. 37 // Note that sections may be split across directories differently for different CLDR versions. 38 // For more robust code, use SetSectionFilter. 39 func (d *Decoder) SetDirFilter(dir ...string) { 40 d.dirFilter = dir 41 } 42 43 // A Loader provides access to the files of a CLDR archive. 44 type Loader interface { 45 Len() int 46 Path(i int) string 47 Reader(i int) (io.ReadCloser, error) 48 } 49 50 var fileRe = regexp.MustCompile(`.*[/\\](.*)[/\\](.*)\.xml`) 51 52 // Decode loads and decodes the files represented by l. 53 func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) { 54 d.cldr = makeCLDR() 55 for i := 0; i < l.Len(); i++ { 56 fname := l.Path(i) 57 if m := fileRe.FindStringSubmatch(fname); m != nil { 58 if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) { 59 continue 60 } 61 var r io.ReadCloser 62 if r, err = l.Reader(i); err == nil { 63 err = d.decode(m[1], m[2], r) 64 r.Close() 65 } 66 if err != nil { 67 return nil, err 68 } 69 } 70 } 71 d.cldr.finalize(d.sectionFilter) 72 return d.cldr, nil 73 } 74 75 func (d *Decoder) decode(dir, id string, r io.Reader) error { 76 var v interface{} 77 var l *LDML 78 cldr := d.cldr 79 switch { 80 case dir == "supplemental": 81 v = cldr.supp 82 case dir == "transforms": 83 return nil 84 case dir == "bcp47": 85 v = cldr.bcp47 86 case dir == "validity": 87 return nil 88 default: 89 ok := false 90 if v, ok = cldr.locale[id]; !ok { 91 l = &LDML{} 92 v, cldr.locale[id] = l, l 93 } 94 } 95 x := xml.NewDecoder(r) 96 if err := x.Decode(v); err != nil { 97 log.Printf("%s/%s: %v", dir, id, err) 98 return err 99 } 100 if l != nil { 101 if l.Identity == nil { 102 return fmt.Errorf("%s/%s: missing identity element", dir, id) 103 } 104 // TODO: verify when CLDR bug https://unicode.org/cldr/trac/ticket/8970 105 // is resolved. 106 // path := strings.Split(id, "_") 107 // if lang := l.Identity.Language.Type; lang != path[0] { 108 // return fmt.Errorf("%s/%s: language was %s; want %s", dir, id, lang, path[0]) 109 // } 110 } 111 return nil 112 } 113 114 type pathLoader []string 115 116 func makePathLoader(path string) (pl pathLoader, err error) { 117 err = filepath.Walk(path, func(path string, _ os.FileInfo, err error) error { 118 pl = append(pl, path) 119 return err 120 }) 121 return pl, err 122 } 123 124 func (pl pathLoader) Len() int { 125 return len(pl) 126 } 127 128 func (pl pathLoader) Path(i int) string { 129 return pl[i] 130 } 131 132 func (pl pathLoader) Reader(i int) (io.ReadCloser, error) { 133 return os.Open(pl[i]) 134 } 135 136 // DecodePath loads CLDR data from the given path. 137 func (d *Decoder) DecodePath(path string) (cldr *CLDR, err error) { 138 loader, err := makePathLoader(path) 139 if err != nil { 140 return nil, err 141 } 142 return d.Decode(loader) 143 } 144 145 type zipLoader struct { 146 r *zip.Reader 147 } 148 149 func (zl zipLoader) Len() int { 150 return len(zl.r.File) 151 } 152 153 func (zl zipLoader) Path(i int) string { 154 return zl.r.File[i].Name 155 } 156 157 func (zl zipLoader) Reader(i int) (io.ReadCloser, error) { 158 return zl.r.File[i].Open() 159 } 160 161 // DecodeZip loads CLDR data from the zip archive for which r is the source. 162 func (d *Decoder) DecodeZip(r io.Reader) (cldr *CLDR, err error) { 163 buffer, err := ioutil.ReadAll(r) 164 if err != nil { 165 return nil, err 166 } 167 archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer))) 168 if err != nil { 169 return nil, err 170 } 171 return d.Decode(zipLoader{archive}) 172 }