Skip to content
This repository has been archived by the owner on Feb 24, 2024. It is now read-only.

Simplify i18n #763

Merged
merged 3 commits into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 48 additions & 43 deletions middleware/i18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ type Translator struct {
// default is "lang"
SessionName string
// HelperName - name of the view helper. default is "t"
HelperName string
// HelperNamePlural - name of the view plural helper. default is "tp"
HelperNamePlural string
LanguageFinder LanguageFinder
HelperName string
LanguageFinder LanguageFinder
}

// Load translations from the t.Box.
Expand All @@ -59,13 +57,12 @@ func (t *Translator) AddTranslation(lang *language.Language, translations ...tra
// also call t.Load() and load the translations from disk.
func New(box packr.Box, language string) (*Translator, error) {
t := &Translator{
Box: box,
DefaultLanguage: language,
CookieName: "lang",
SessionName: "lang",
HelperName: "t",
HelperNamePlural: "tp",
LanguageFinder: defaultLanguageFinder,
Box: box,
DefaultLanguage: language,
CookieName: "lang",
SessionName: "lang",
HelperName: "t",
LanguageFinder: defaultLanguageFinder,
}
return t, t.Load()
}
Expand All @@ -74,6 +71,7 @@ func New(box packr.Box, language string) (*Translator, error) {
// selected. By default languages are loaded in the following order:
//
// Cookie - "lang"
// Session - "lang"
// Header - "Accept-Language"
// Default - "en-US"
//
Expand All @@ -91,45 +89,52 @@ func (t *Translator) Middleware() buffalo.MiddlewareFunc {
}
}

// set languages in context, if not set yet
if langs := c.Value("languages"); langs == nil {
c.Set("languages", t.LanguageFinder(t, c))
}

// set translator
if T := c.Value("T"); T == nil {
langs := c.Value("languages").([]string)
T, err := i18n.Tfunc(langs[0], langs[1:]...)
if err != nil {
return err
}
c.Set("T", T)
}

// set up the helper function for the views:
c.Set(t.HelperName, func(s string) (string, error) {
return t.Translate(c, s)
})
c.Set(t.HelperNamePlural, func(s string, i interface{}) (string, error) {
return t.TranslatePlural(c, s, i)
c.Set(t.HelperName, func(s string, i ...interface{}) string {
return t.Translate(c, s, i...)
})
return next(c)
}
}
}

// Translate translates a string given a Context
// s is the translation ID
func (t *Translator) Translate(c buffalo.Context, s string) (string, error) {
if langs := c.Value("languages"); langs == nil {
c.Set("languages", t.LanguageFinder(t, c))
}
langs := c.Value("languages").([]string)
T, err := i18n.Tfunc(langs[0], langs[1:]...)
if err != nil {
return "", err
}
return T(s, c.Data()), nil
}

// TranslatePlural is the plural version of Translate
// s is the translation ID
// i must be an integer type (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45")
func (t *Translator) TranslatePlural(c buffalo.Context, s string, i interface{}) (string, error) {
if langs := c.Value("languages"); langs == nil {
c.Set("languages", t.LanguageFinder(t, c))
}
langs := c.Value("languages").([]string)
T, err := i18n.Tfunc(langs[0], langs[1:]...)
if err != nil {
return "", err
}
return T(s, i, c.Data()), nil
// Translate returns the translation of the string identified by translationID.
//
// See https://github.com/nicksnyder/go-i18n
//
// If there is no translation for translationID, then the translationID itself is returned.
// This makes it easy to identify missing translations in your app.
//
// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
// or struct that contains template data.
//
// If translationID is a plural form, the function accepts two parameter signatures
// 1. T(count int, data struct{})
// The first variadic argument must be an integer type
// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
// The second variadic argument may be a map[string]interface{} or struct{} that contains template data.
// 2. T(data struct{})
// data must be a struct{} or map[string]interface{} that contains a Count field and the template data,
// Count field must be an integer type (int, int8, int16, int32, int64)
// or a float formatted as a string (e.g. "123.45").
func (t *Translator) Translate(c buffalo.Context, translationID string, args ...interface{}) string {
T := c.Value("T").(i18n.TranslateFunc)
return T(translationID, args...)
}

func defaultLanguageFinder(t *Translator, c buffalo.Context) []string {
Expand Down
33 changes: 32 additions & 1 deletion middleware/i18n/i18n_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (
"github.com/stretchr/testify/require"
)

type User struct {
FirstName string
LastName string
}

func app() *buffalo.App {
app := buffalo.New(buffalo.Options{})

Expand All @@ -30,6 +35,13 @@ func app() *buffalo.App {
app.GET("/plural", func(c buffalo.Context) error {
return c.Render(200, r.HTML("plural.html"))
})
app.GET("/format", func(c buffalo.Context) error {
usersList := make([]User, 0)
usersList = append(usersList, User{"Mark", "Bates"})
usersList = append(usersList, User{"Chuck", "Berry"})
c.Set("Users", usersList)
return c.Render(200, r.HTML("format.html"))
})
return app
}

Expand Down Expand Up @@ -69,4 +81,23 @@ func Test_i18n_plural_fr(t *testing.T) {
req.Headers["Accept-Language"] = "fr-fr"
res := req.Get()
r.Equal("Bonjour, tout seul !\nBonjour, 5 personnes !\n", res.Body.String())
}
}

func Test_i18n_format(t *testing.T) {
r := require.New(t)

w := willie.New(app())
res := w.Request("/format").Get()
r.Equal("Hello Mark!\n\n\t* Mr. Mark Bates\n\n\t* Mr. Chuck Berry\n", res.Body.String())
}

func Test_i18n_format_fr(t *testing.T) {
r := require.New(t)

w := willie.New(app())
req := w.Request("/format")
// Set language as "french"
req.Headers["Accept-Language"] = "fr-fr"
res := req.Get()
r.Equal("Bonjour Mark !\n\n\t* M. Mark Bates\n\n\t* M. Chuck Berry\n", res.Body.String())
}
6 changes: 6 additions & 0 deletions middleware/i18n/locales/test.en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@
translation:
one: "Hello, alone!"
other: "Hello, {{.Count}} people!"

- id: test-format
translation: "Hello {{.Name}}!"

- id: test-format-loop
translation: "Mr. {{.FirstName}} {{.LastName}}"
6 changes: 6 additions & 0 deletions middleware/i18n/locales/test.fr-fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@
translation:
one: "Bonjour, tout seul !"
other: "Bonjour, {{.Count}} personnes !"

- id: test-format
translation: "Bonjour {{.Name}} !"

- id: test-format-loop
translation: "M. {{.FirstName}} {{.LastName}}"
4 changes: 4 additions & 0 deletions middleware/i18n/templates/format.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= t("test-format", {Name: "Mark"}) %>
<%= for (u) in Users { %>
* <%= t("test-format-loop", u) %>
<% } %>
4 changes: 2 additions & 2 deletions middleware/i18n/templates/plural.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<%= tp("greeting-plural", 1) %>
<%= tp("greeting-plural", 5) %>
<%= t("greeting-plural", 1) %>
<%= t("greeting-plural", 5) %>