You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.9 KiB
171 lines
3.9 KiB
// Copyright 2013 The Go Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package cldr |
|
|
|
import ( |
|
"archive/zip" |
|
"bytes" |
|
"encoding/xml" |
|
"fmt" |
|
"io" |
|
"io/ioutil" |
|
"log" |
|
"os" |
|
"path/filepath" |
|
"regexp" |
|
) |
|
|
|
// A Decoder loads an archive of CLDR data. |
|
type Decoder struct { |
|
dirFilter []string |
|
sectionFilter []string |
|
loader Loader |
|
cldr *CLDR |
|
curLocale string |
|
} |
|
|
|
// SetSectionFilter takes a list top-level LDML element names to which |
|
// evaluation of LDML should be limited. It automatically calls SetDirFilter. |
|
func (d *Decoder) SetSectionFilter(filter ...string) { |
|
d.sectionFilter = filter |
|
// TODO: automatically set dir filter |
|
} |
|
|
|
// SetDirFilter limits the loading of LDML XML files of the specied directories. |
|
// Note that sections may be split across directories differently for different CLDR versions. |
|
// For more robust code, use SetSectionFilter. |
|
func (d *Decoder) SetDirFilter(dir ...string) { |
|
d.dirFilter = dir |
|
} |
|
|
|
// A Loader provides access to the files of a CLDR archive. |
|
type Loader interface { |
|
Len() int |
|
Path(i int) string |
|
Reader(i int) (io.ReadCloser, error) |
|
} |
|
|
|
var fileRe = regexp.MustCompile(".*/(.*)/(.*)\\.xml") |
|
|
|
// Decode loads and decodes the files represented by l. |
|
func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) { |
|
d.cldr = makeCLDR() |
|
for i := 0; i < l.Len(); i++ { |
|
fname := l.Path(i) |
|
if m := fileRe.FindStringSubmatch(fname); m != nil { |
|
if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) { |
|
continue |
|
} |
|
var r io.Reader |
|
if r, err = l.Reader(i); err == nil { |
|
err = d.decode(m[1], m[2], r) |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
} |
|
d.cldr.finalize(d.sectionFilter) |
|
return d.cldr, nil |
|
} |
|
|
|
func (d *Decoder) decode(dir, id string, r io.Reader) error { |
|
var v interface{} |
|
var l *LDML |
|
cldr := d.cldr |
|
switch { |
|
case dir == "supplemental": |
|
v = cldr.supp |
|
case dir == "transforms": |
|
return nil |
|
case dir == "bcp47": |
|
v = cldr.bcp47 |
|
case dir == "validity": |
|
return nil |
|
default: |
|
ok := false |
|
if v, ok = cldr.locale[id]; !ok { |
|
l = &LDML{} |
|
v, cldr.locale[id] = l, l |
|
} |
|
} |
|
x := xml.NewDecoder(r) |
|
if err := x.Decode(v); err != nil { |
|
log.Printf("%s/%s: %v", dir, id, err) |
|
return err |
|
} |
|
if l != nil { |
|
if l.Identity == nil { |
|
return fmt.Errorf("%s/%s: missing identity element", dir, id) |
|
} |
|
// TODO: verify when CLDR bug http://unicode.org/cldr/trac/ticket/8970 |
|
// is resolved. |
|
// path := strings.Split(id, "_") |
|
// if lang := l.Identity.Language.Type; lang != path[0] { |
|
// return fmt.Errorf("%s/%s: language was %s; want %s", dir, id, lang, path[0]) |
|
// } |
|
} |
|
return nil |
|
} |
|
|
|
type pathLoader []string |
|
|
|
func makePathLoader(path string) (pl pathLoader, err error) { |
|
err = filepath.Walk(path, func(path string, _ os.FileInfo, err error) error { |
|
pl = append(pl, path) |
|
return err |
|
}) |
|
return pl, err |
|
} |
|
|
|
func (pl pathLoader) Len() int { |
|
return len(pl) |
|
} |
|
|
|
func (pl pathLoader) Path(i int) string { |
|
return pl[i] |
|
} |
|
|
|
func (pl pathLoader) Reader(i int) (io.ReadCloser, error) { |
|
return os.Open(pl[i]) |
|
} |
|
|
|
// DecodePath loads CLDR data from the given path. |
|
func (d *Decoder) DecodePath(path string) (cldr *CLDR, err error) { |
|
loader, err := makePathLoader(path) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return d.Decode(loader) |
|
} |
|
|
|
type zipLoader struct { |
|
r *zip.Reader |
|
} |
|
|
|
func (zl zipLoader) Len() int { |
|
return len(zl.r.File) |
|
} |
|
|
|
func (zl zipLoader) Path(i int) string { |
|
return zl.r.File[i].Name |
|
} |
|
|
|
func (zl zipLoader) Reader(i int) (io.ReadCloser, error) { |
|
return zl.r.File[i].Open() |
|
} |
|
|
|
// DecodeZip loads CLDR data from the zip archive for which r is the source. |
|
func (d *Decoder) DecodeZip(r io.Reader) (cldr *CLDR, err error) { |
|
buffer, err := ioutil.ReadAll(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer))) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return d.Decode(zipLoader{archive}) |
|
}
|
|
|