...

Source file src/github.com/go-playground/universal-translator/import_export.go

Documentation: github.com/go-playground/universal-translator

     1  package ut
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"io"
    10  
    11  	"github.com/go-playground/locales"
    12  )
    13  
    14  type translation struct {
    15  	Locale           string      `json:"locale"`
    16  	Key              interface{} `json:"key"` // either string or integer
    17  	Translation      string      `json:"trans"`
    18  	PluralType       string      `json:"type,omitempty"`
    19  	PluralRule       string      `json:"rule,omitempty"`
    20  	OverrideExisting bool        `json:"override,omitempty"`
    21  }
    22  
    23  const (
    24  	cardinalType = "Cardinal"
    25  	ordinalType  = "Ordinal"
    26  	rangeType    = "Range"
    27  )
    28  
    29  // ImportExportFormat is the format of the file import or export
    30  type ImportExportFormat uint8
    31  
    32  // supported Export Formats
    33  const (
    34  	FormatJSON ImportExportFormat = iota
    35  )
    36  
    37  // Export writes the translations out to a file on disk.
    38  //
    39  // NOTE: this currently only works with string or int translations keys.
    40  func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
    41  
    42  	_, err := os.Stat(dirname)
    43  	if err != nil {
    44  
    45  		if !os.IsNotExist(err) {
    46  			return err
    47  		}
    48  
    49  		if err = os.MkdirAll(dirname, 0744); err != nil {
    50  			return err
    51  		}
    52  	}
    53  
    54  	// build up translations
    55  	var trans []translation
    56  	var b []byte
    57  	var ext string
    58  
    59  	for _, locale := range t.translators {
    60  
    61  		for k, v := range locale.(*translator).translations {
    62  			trans = append(trans, translation{
    63  				Locale:      locale.Locale(),
    64  				Key:         k,
    65  				Translation: v.text,
    66  			})
    67  		}
    68  
    69  		for k, pluralTrans := range locale.(*translator).cardinalTanslations {
    70  
    71  			for i, plural := range pluralTrans {
    72  
    73  				// leave enough for all plural rules
    74  				// but not all are set for all languages.
    75  				if plural == nil {
    76  					continue
    77  				}
    78  
    79  				trans = append(trans, translation{
    80  					Locale:      locale.Locale(),
    81  					Key:         k.(string),
    82  					Translation: plural.text,
    83  					PluralType:  cardinalType,
    84  					PluralRule:  locales.PluralRule(i).String(),
    85  				})
    86  			}
    87  		}
    88  
    89  		for k, pluralTrans := range locale.(*translator).ordinalTanslations {
    90  
    91  			for i, plural := range pluralTrans {
    92  
    93  				// leave enough for all plural rules
    94  				// but not all are set for all languages.
    95  				if plural == nil {
    96  					continue
    97  				}
    98  
    99  				trans = append(trans, translation{
   100  					Locale:      locale.Locale(),
   101  					Key:         k.(string),
   102  					Translation: plural.text,
   103  					PluralType:  ordinalType,
   104  					PluralRule:  locales.PluralRule(i).String(),
   105  				})
   106  			}
   107  		}
   108  
   109  		for k, pluralTrans := range locale.(*translator).rangeTanslations {
   110  
   111  			for i, plural := range pluralTrans {
   112  
   113  				// leave enough for all plural rules
   114  				// but not all are set for all languages.
   115  				if plural == nil {
   116  					continue
   117  				}
   118  
   119  				trans = append(trans, translation{
   120  					Locale:      locale.Locale(),
   121  					Key:         k.(string),
   122  					Translation: plural.text,
   123  					PluralType:  rangeType,
   124  					PluralRule:  locales.PluralRule(i).String(),
   125  				})
   126  			}
   127  		}
   128  
   129  		switch format {
   130  		case FormatJSON:
   131  			b, err = json.MarshalIndent(trans, "", "    ")
   132  			ext = ".json"
   133  		}
   134  
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		err = os.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
   140  		if err != nil {
   141  			return err
   142  		}
   143  
   144  		trans = trans[0:0]
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  // Import reads the translations out of a file or directory on disk.
   151  //
   152  // NOTE: this currently only works with string or int translations keys.
   153  func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
   154  
   155  	fi, err := os.Stat(dirnameOrFilename)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	processFn := func(filename string) error {
   161  
   162  		f, err := os.Open(filename)
   163  		if err != nil {
   164  			return err
   165  		}
   166  		defer f.Close()
   167  
   168  		return t.ImportByReader(format, f)
   169  	}
   170  
   171  	if !fi.IsDir() {
   172  		return processFn(dirnameOrFilename)
   173  	}
   174  
   175  	// recursively go through directory
   176  	walker := func(path string, info os.FileInfo, err error) error {
   177  
   178  		if info.IsDir() {
   179  			return nil
   180  		}
   181  
   182  		switch format {
   183  		case FormatJSON:
   184  			// skip non JSON files
   185  			if filepath.Ext(info.Name()) != ".json" {
   186  				return nil
   187  			}
   188  		}
   189  
   190  		return processFn(path)
   191  	}
   192  
   193  	return filepath.Walk(dirnameOrFilename, walker)
   194  }
   195  
   196  // ImportByReader imports the the translations found within the contents read from the supplied reader.
   197  //
   198  // NOTE: generally used when assets have been embedded into the binary and are already in memory.
   199  func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
   200  
   201  	b, err := io.ReadAll(reader)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	var trans []translation
   207  
   208  	switch format {
   209  	case FormatJSON:
   210  		err = json.Unmarshal(b, &trans)
   211  	}
   212  
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	for _, tl := range trans {
   218  
   219  		locale, found := t.FindTranslator(tl.Locale)
   220  		if !found {
   221  			return &ErrMissingLocale{locale: tl.Locale}
   222  		}
   223  
   224  		pr := stringToPR(tl.PluralRule)
   225  
   226  		if pr == locales.PluralRuleUnknown {
   227  
   228  			err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
   229  			if err != nil {
   230  				return err
   231  			}
   232  
   233  			continue
   234  		}
   235  
   236  		switch tl.PluralType {
   237  		case cardinalType:
   238  			err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
   239  		case ordinalType:
   240  			err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
   241  		case rangeType:
   242  			err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
   243  		default:
   244  			return &ErrBadPluralDefinition{tl: tl}
   245  		}
   246  
   247  		if err != nil {
   248  			return err
   249  		}
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func stringToPR(s string) locales.PluralRule {
   256  
   257  	switch s {
   258  	case "Zero":
   259  		return locales.PluralRuleZero
   260  	case "One":
   261  		return locales.PluralRuleOne
   262  	case "Two":
   263  		return locales.PluralRuleTwo
   264  	case "Few":
   265  		return locales.PluralRuleFew
   266  	case "Many":
   267  		return locales.PluralRuleMany
   268  	case "Other":
   269  		return locales.PluralRuleOther
   270  	default:
   271  		return locales.PluralRuleUnknown
   272  	}
   273  
   274  }
   275  

View as plain text