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

Commit

Permalink
I18n (internationalisation) support closes #249
Browse files Browse the repository at this point in the history
  • Loading branch information
markbates committed Apr 4, 2017
1 parent 4bd0d8c commit d3df3e0
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 1 deletion.
9 changes: 9 additions & 0 deletions generators/newapp/templates/actions/app.go.tmpl
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package actions

import (
"log"
"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/buffalo/middleware"
"github.com/gobuffalo/buffalo/middleware/i18n"
{{ if .withPop }}
"{{.modelsPath}}"
{{ end }}
Expand Down Expand Up @@ -35,6 +37,13 @@ func App() *buffalo.App {
app.Use(middleware.PopTransaction(models.DB))
{{ end }}

// Setup and use translations:
t, err := i18n.New(packr.NewBox("../locales"), "en-US")
if err != nil {
log.Fatal(err)
}
app.Use(t.Middleware())

app.GET("/", HomeHandler)

app.ServeFiles("/assets", packr.NewBox("../public/assets"))
Expand Down
2 changes: 2 additions & 0 deletions generators/newapp/templates/locales/all.en-us.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- id: welcome_greeting
translation: "Welcome to Buffalo (EN)"
2 changes: 1 addition & 1 deletion generators/newapp/templates/templates/index.html.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img src="/assets/images/logo.svg" alt="" />
</div>
<div class="col-md-10">
<h1>Welcome to Buffalo! [v{{.version}}]</h1>
<h1><%= t("welcome_greeting") %> [v{{.version}}]</h1>
<h2>
<a href="https://github.com/gobuffalo/buffalo"><i class="fa fa-github" aria-hidden="true"></i> https://github.com/gobuffalo/buffalo</a>
</h2>
Expand Down
126 changes: 126 additions & 0 deletions middleware/i18n/i18n.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package i18n

import (
"log"

"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/packr"
"github.com/gobuffalo/plush"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/nicksnyder/go-i18n/i18n/language"
"github.com/nicksnyder/go-i18n/i18n/translation"
"github.com/pkg/errors"
)

// LanguageFinder can be implemented for custom finding of search
// languages. This can be useful if you want to load a user's langugage
// from something like a database. See Middleware() for more information
// on how the default implementation searches for languages.
type LanguageFinder func(*Translator, buffalo.Context) []string

// Translator for handling all your i18n needs.
type Translator struct {
// Box - where are the files?
Box packr.Box
// DefaultLanguage - default is "en-US"
DefaultLanguage string
// CookieName - name of the cookie to find the desired language.
// default is "lang"
CookieName string
// HelperName - name of the view helper. default is "t"
HelperName string
LanguageFinder LanguageFinder
}

// Load translations from the t.Box.
func (t *Translator) Load() error {
return t.Box.Walk(func(path string, f packr.File) error {
b, err := t.Box.MustBytes(path)
if err != nil {
log.Fatal(err)
return errors.WithStack(err)
}
return i18n.ParseTranslationFileBytes(path, b)
})
}

// AddTranslation directly, without using a file. This is useful if you wish to load translations
// from a database, instead of disk.
func (t *Translator) AddTranslation(lang *language.Language, translations ...translation.Translation) {
i18n.AddTranslation(lang, translations...)
}

// New Translator. Requires a packr.Box that points to the location
// of the translation files, as well as a default language. This will
// 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",
HelperName: "t",
LanguageFinder: defaultLanguageFinder,
}
return t, t.Load()
}

// Middleware for loading the translations for the language(s)
// selected. By default languages are loaded in the following order:
//
// Cookie - "lang"
// Header - "Accept-Language"
// Default - "en-US"
//
// These values can be changed on the Translator itself. In development
// model the translation files will be reloaded on each request.
func (t *Translator) Middleware() buffalo.MiddlewareFunc {
return func(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {

// in development reload the translations
if c.Value("env").(string) == "development" {
err := t.Load()
if err != nil {
return err
}
}

langs := t.LanguageFinder(t, c)

c.Set("languages", langs)

// set up the helper function for the views:
c.Set(t.HelperName, func(s string, help plush.HelperContext) (string, error) {
T, err := i18n.Tfunc(langs[0], langs[1:]...)
if err != nil {
return "", err
}
return T(s), nil
})
return next(c)
}
}
}

func defaultLanguageFinder(t *Translator, c buffalo.Context) []string {
langs := []string{}

r := c.Request()

// try to get the language from a cookie:
if cookie, err := r.Cookie(t.CookieName); err == nil {
if cookie.Value != "" {
langs = append(langs, cookie.Value)
}
}

// try to get the language from a header:
acceptLang := r.Header.Get("Accept-Language")
if acceptLang != "" {
langs = append(langs, acceptLang)
}

// try to get the language from the session:
langs = append(langs, t.DefaultLanguage)
return langs
}
39 changes: 39 additions & 0 deletions middleware/i18n/i18n_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package i18n

import (
"log"
"testing"

"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/buffalo/render"
"github.com/gobuffalo/packr"
"github.com/markbates/willie"
"github.com/stretchr/testify/require"
)

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

r := render.New(render.Options{
TemplatesBox: packr.NewBox("./templates"),
})

// Setup and use translations:
t, err := New(packr.NewBox("./locales"), "en-US")
if err != nil {
log.Fatal(err)
}
app.Use(t.Middleware())
app.GET("/", func(c buffalo.Context) error {
return c.Render(200, r.HTML("index.html"))
})
return app
}

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

w := willie.New(app())
res := w.Request("/").Get()
r.Equal("Hello, World!\n", res.Body.String())
}
2 changes: 2 additions & 0 deletions middleware/i18n/locales/test.en-us.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- id: greeting
translation: "Hello, World!"
1 change: 1 addition & 0 deletions middleware/i18n/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= t("greeting") %>

0 comments on commit d3df3e0

Please sign in to comment.