...

Source file src/github.com/go-playground/universal-translator/_examples/full-no-files/main.go

Documentation: github.com/go-playground/universal-translator/_examples/full-no-files

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"html/template"
     6  	"log"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/go-playground/locales"
    11  	"github.com/go-playground/locales/currency"
    12  	"github.com/go-playground/locales/en"
    13  	"github.com/go-playground/locales/fr"
    14  	"github.com/go-playground/pure/v5"
    15  	"github.com/go-playground/pure/v5/_examples/middleware/logging-recovery"
    16  	"github.com/go-playground/universal-translator"
    17  )
    18  
    19  var (
    20  	tmpls    *template.Template
    21  	utrans   *ut.UniversalTranslator
    22  	transKey = struct {
    23  		name string
    24  	}{
    25  		name: "transKey",
    26  	}
    27  )
    28  
    29  // Translator wraps ut.Translator in order to handle errors transparently
    30  // it is totally optional but recommended as it can now be used directly in
    31  // templates and nobody can add translations where they're not supposed to.
    32  type Translator interface {
    33  	locales.Translator
    34  
    35  	// creates the translation for the locale given the 'key' and params passed in.
    36  	// wraps ut.Translator.T to handle errors
    37  	T(key interface{}, params ...string) string
    38  
    39  	// creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments
    40  	//  and param passed in.
    41  	// wraps ut.Translator.C to handle errors
    42  	C(key interface{}, num float64, digits uint64, param string) string
    43  
    44  	// creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments
    45  	// and param passed in.
    46  	// wraps ut.Translator.O to handle errors
    47  	O(key interface{}, num float64, digits uint64, param string) string
    48  
    49  	//  creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and
    50  	//  'digit2' arguments and 'param1' and 'param2' passed in
    51  	// wraps ut.Translator.R to handle errors
    52  	R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) string
    53  
    54  	// Currency returns the type used by the given locale.
    55  	Currency() currency.Type
    56  }
    57  
    58  // implements Translator interface definition above.
    59  type translator struct {
    60  	locales.Translator
    61  	trans ut.Translator
    62  }
    63  
    64  var _ Translator = (*translator)(nil)
    65  
    66  func (t *translator) T(key interface{}, params ...string) string {
    67  
    68  	s, err := t.trans.T(key, params...)
    69  	if err != nil {
    70  		log.Printf("issue translating key: '%v' error: '%s'", key, err)
    71  	}
    72  
    73  	return s
    74  }
    75  
    76  func (t *translator) C(key interface{}, num float64, digits uint64, param string) string {
    77  
    78  	s, err := t.trans.C(key, num, digits, param)
    79  	if err != nil {
    80  		log.Printf("issue translating cardinal key: '%v' error: '%s'", key, err)
    81  	}
    82  
    83  	return s
    84  }
    85  
    86  func (t *translator) O(key interface{}, num float64, digits uint64, param string) string {
    87  
    88  	s, err := t.trans.C(key, num, digits, param)
    89  	if err != nil {
    90  		log.Printf("issue translating ordinal key: '%v' error: '%s'", key, err)
    91  	}
    92  
    93  	return s
    94  }
    95  
    96  func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) string {
    97  
    98  	s, err := t.trans.R(key, num1, digits1, num2, digits2, param1, param2)
    99  	if err != nil {
   100  		log.Printf("issue translating range key: '%v' error: '%s'", key, err)
   101  	}
   102  
   103  	return s
   104  }
   105  
   106  func (t *translator) Currency() currency.Type {
   107  
   108  	// choose your own locale. The reason it isn't mapped for you is because many
   109  	// countries have multiple currencies; it's up to you and you're application how
   110  	// and which currencies to use. I recommend adding a function it to to your custon translator
   111  	// interface like defined above.
   112  	switch t.Locale() {
   113  	case "en":
   114  		return currency.USD
   115  	case "fr":
   116  		return currency.EUR
   117  	default:
   118  		return currency.USD
   119  	}
   120  }
   121  
   122  func main() {
   123  
   124  	en := en.New()
   125  	utrans = ut.New(en, en, fr.New())
   126  	setup()
   127  
   128  	tmpls, _ = template.ParseFiles("home.tmpl")
   129  
   130  	r := pure.New()
   131  	r.Use(middleware.LoggingAndRecovery(true), translatorMiddleware)
   132  	r.Get("/", home)
   133  
   134  	log.Println("Running on Port :8080")
   135  	log.Println("Try me with URL http://localhost:8080/?locale=en")
   136  	log.Println("and then http://localhost:8080/?locale=fr")
   137  	http.ListenAndServe(":8080", r.Serve())
   138  }
   139  
   140  func home(w http.ResponseWriter, r *http.Request) {
   141  
   142  	// get locale translator ( could be wrapped into a helper function )
   143  	t := r.Context().Value(transKey).(Translator)
   144  
   145  	s := struct {
   146  		Trans       Translator
   147  		Now         time.Time
   148  		PositiveNum float64
   149  		NegativeNum float64
   150  		Percent     float64
   151  	}{
   152  		Trans:       t,
   153  		Now:         time.Now(),
   154  		PositiveNum: 1234576.45,
   155  		NegativeNum: -35900394.34,
   156  		Percent:     96.76,
   157  	}
   158  
   159  	if err := tmpls.ExecuteTemplate(w, "home", s); err != nil {
   160  		log.Fatal(err)
   161  	}
   162  }
   163  
   164  func translatorMiddleware(next http.HandlerFunc) http.HandlerFunc {
   165  
   166  	return func(w http.ResponseWriter, r *http.Request) {
   167  
   168  		// there are many ways to check, this is just checking for query param &
   169  		// Accept-Language header but can be expanded to Cookie's etc....
   170  
   171  		params := r.URL.Query()
   172  
   173  		locale := params.Get("locale")
   174  		var t ut.Translator
   175  
   176  		if len(locale) > 0 {
   177  
   178  			var found bool
   179  
   180  			if t, found = utrans.GetTranslator(locale); found {
   181  				goto END
   182  			}
   183  		}
   184  
   185  		// get and parse the "Accept-Language" http header and return an array
   186  		t, _ = utrans.FindTranslator(pure.AcceptedLanguages(r)...)
   187  	END:
   188  		// I would normally wrap ut.Translator with one with my own functions in order
   189  		// to handle errors and be able to use all functions from translator within the templates.
   190  		r = r.WithContext(context.WithValue(r.Context(), transKey, &translator{trans: t, Translator: t.(locales.Translator)}))
   191  
   192  		next(w, r)
   193  	}
   194  }
   195  
   196  func setup() {
   197  
   198  	en, _ := utrans.FindTranslator("en")
   199  	en.AddCardinal("days-left", "There is {0} day left", locales.PluralRuleOne, false)
   200  	en.AddCardinal("days-left", "There are {0} days left", locales.PluralRuleOther, false)
   201  
   202  	fr, _ := utrans.FindTranslator("fr")
   203  	fr.AddCardinal("days-left", "Il reste {0} jour", locales.PluralRuleOne, false)
   204  	fr.AddCardinal("days-left", "Il reste {0} jours", locales.PluralRuleOther, false)
   205  
   206  	err := utrans.VerifyTranslations()
   207  	if err != nil {
   208  		log.Fatal(err)
   209  	}
   210  }
   211  

View as plain text