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).