|
|
@ -12,16 +12,17 @@ |
|
|
|
// License for the specific language governing permissions and limitations
|
|
|
|
// License for the specific language governing permissions and limitations
|
|
|
|
// under the License.
|
|
|
|
// under the License.
|
|
|
|
|
|
|
|
|
|
|
|
// Package i18n is a middleware that provides app Internationalization and Localization of Macaron.
|
|
|
|
// Package i18n provides an Internationalization and Localization middleware for Macaron applications.
|
|
|
|
package i18n |
|
|
|
package i18n |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
|
|
|
|
"log" |
|
|
|
|
|
|
|
"os" |
|
|
|
"path" |
|
|
|
"path" |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
|
|
"gitea.com/macaron/macaron" |
|
|
|
"gitea.com/macaron/macaron" |
|
|
|
"github.com/unknwon/com" |
|
|
|
|
|
|
|
"github.com/unknwon/i18n" |
|
|
|
"github.com/unknwon/i18n" |
|
|
|
"golang.org/x/text/language" |
|
|
|
"golang.org/x/text/language" |
|
|
|
) |
|
|
|
) |
|
|
@ -32,6 +33,16 @@ func Version() string { |
|
|
|
return _VERSION |
|
|
|
return _VERSION |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// isFile returns true if given path is a file,
|
|
|
|
|
|
|
|
// or returns false when it's a directory or does not exist.
|
|
|
|
|
|
|
|
func isFile(filePath string) bool { |
|
|
|
|
|
|
|
f, e := os.Stat(filePath) |
|
|
|
|
|
|
|
if e != nil { |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return !f.IsDir() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// initLocales initializes language type list and Accept-Language header matcher.
|
|
|
|
// initLocales initializes language type list and Accept-Language header matcher.
|
|
|
|
func initLocales(opt Options) language.Matcher { |
|
|
|
func initLocales(opt Options) language.Matcher { |
|
|
|
tags := make([]language.Tag, len(opt.Langs)) |
|
|
|
tags := make([]language.Tag, len(opt.Langs)) |
|
|
@ -41,7 +52,7 @@ func initLocales(opt Options) language.Matcher { |
|
|
|
// Append custom locale file.
|
|
|
|
// Append custom locale file.
|
|
|
|
custom := []interface{}{} |
|
|
|
custom := []interface{}{} |
|
|
|
customPath := path.Join(opt.CustomDirectory, fname) |
|
|
|
customPath := path.Join(opt.CustomDirectory, fname) |
|
|
|
if com.IsFile(customPath) { |
|
|
|
if isFile(customPath) { |
|
|
|
custom = append(custom, customPath) |
|
|
|
custom = append(custom, customPath) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -54,7 +65,7 @@ func initLocales(opt Options) language.Matcher { |
|
|
|
|
|
|
|
|
|
|
|
err := i18n.SetMessageWithDesc(lang, opt.Names[i], locale, custom...) |
|
|
|
err := i18n.SetMessageWithDesc(lang, opt.Names[i], locale, custom...) |
|
|
|
if err != nil && err != i18n.ErrLangAlreadyExist { |
|
|
|
if err != nil && err != i18n.ErrLangAlreadyExist { |
|
|
|
panic(fmt.Errorf("fail to set message file(%s): %v", lang, err)) |
|
|
|
log.Printf("ERROR: failed to set message file(%s) for language %s: %v", lang, opt.Names[i], err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return language.NewMatcher(tags) |
|
|
|
return language.NewMatcher(tags) |
|
|
@ -98,6 +109,10 @@ type Options struct { |
|
|
|
Section string |
|
|
|
Section string |
|
|
|
// Domain used for `lang` cookie. Default is ""
|
|
|
|
// Domain used for `lang` cookie. Default is ""
|
|
|
|
CookieDomain string |
|
|
|
CookieDomain string |
|
|
|
|
|
|
|
// Set the Secure flag on the `lang` cookie. Default is disabled.
|
|
|
|
|
|
|
|
Secure bool |
|
|
|
|
|
|
|
// Set the HTTP Only flag on the `lang` cookie. Default is disabled.
|
|
|
|
|
|
|
|
CookieHttpOnly bool |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func prepareOptions(options []Options) Options { |
|
|
|
func prepareOptions(options []Options) Options { |
|
|
@ -195,7 +210,7 @@ func I18n(options ...Options) macaron.Handler { |
|
|
|
|
|
|
|
|
|
|
|
// Save language information in cookies.
|
|
|
|
// Save language information in cookies.
|
|
|
|
if !hasCookie { |
|
|
|
if !hasCookie { |
|
|
|
ctx.SetCookie("lang", curLang.Lang, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"), opt.CookieDomain) |
|
|
|
ctx.SetCookie("lang", curLang.Lang, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"), opt.CookieDomain, opt.Secure, opt.CookieHttpOnly) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
restLangs := make([]LangType, 0, i18n.Count()-1) |
|
|
|
restLangs := make([]LangType, 0, i18n.Count()-1) |
|
|
@ -210,7 +225,7 @@ func I18n(options ...Options) macaron.Handler { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Set language properties.
|
|
|
|
// Set language properties.
|
|
|
|
locale := Locale{i18n.Locale{lang}} |
|
|
|
locale := Locale{Locale: i18n.Locale{Lang: lang}} |
|
|
|
ctx.Map(locale) |
|
|
|
ctx.Map(locale) |
|
|
|
ctx.Locale = locale |
|
|
|
ctx.Locale = locale |
|
|
|
ctx.Data[opt.TmplName] = locale |
|
|
|
ctx.Data[opt.TmplName] = locale |
|
|
@ -221,7 +236,7 @@ func I18n(options ...Options) macaron.Handler { |
|
|
|
ctx.Data["RestLangs"] = restLangs |
|
|
|
ctx.Data["RestLangs"] = restLangs |
|
|
|
|
|
|
|
|
|
|
|
if opt.Redirect && isNeedRedir { |
|
|
|
if opt.Redirect && isNeedRedir { |
|
|
|
ctx.Redirect(opt.SubURL + ctx.Req.RequestURI[:strings.Index(ctx.Req.RequestURI, "?")]) |
|
|
|
ctx.Redirect(opt.SubURL + path.Clean(ctx.Req.RequestURI[:strings.Index(ctx.Req.RequestURI, "?")])) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|