diff options
Diffstat (limited to 'vendor/gitea.com/macaron/i18n/i18n.go')
-rw-r--r-- | vendor/gitea.com/macaron/i18n/i18n.go | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/vendor/gitea.com/macaron/i18n/i18n.go b/vendor/gitea.com/macaron/i18n/i18n.go new file mode 100644 index 0000000000..fd114a5bf7 --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/i18n.go @@ -0,0 +1,227 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package i18n is a middleware that provides app Internationalization and Localization of Macaron. +package i18n + +import ( + "fmt" + "path" + "strings" + + "gitea.com/macaron/macaron" + "github.com/unknwon/com" + "github.com/unknwon/i18n" + "golang.org/x/text/language" +) + +const _VERSION = "0.4.0" + +func Version() string { + return _VERSION +} + +// initLocales initializes language type list and Accept-Language header matcher. +func initLocales(opt Options) language.Matcher { + tags := make([]language.Tag, len(opt.Langs)) + for i, lang := range opt.Langs { + tags[i] = language.Raw.Make(lang) + fname := fmt.Sprintf(opt.Format, lang) + // Append custom locale file. + custom := []interface{}{} + customPath := path.Join(opt.CustomDirectory, fname) + if com.IsFile(customPath) { + custom = append(custom, customPath) + } + + var locale interface{} + if data, ok := opt.Files[fname]; ok { + locale = data + } else { + locale = path.Join(opt.Directory, fname) + } + + err := i18n.SetMessageWithDesc(lang, opt.Names[i], locale, custom...) + if err != nil && err != i18n.ErrLangAlreadyExist { + panic(fmt.Errorf("fail to set message file(%s): %v", lang, err)) + } + } + return language.NewMatcher(tags) +} + +// A Locale describles the information of localization. +type Locale struct { + i18n.Locale +} + +// Language returns language current locale represents. +func (l Locale) Language() string { + return l.Lang +} + +// Options represents a struct for specifying configuration options for the i18n middleware. +type Options struct { + // Suburl of path. Default is empty. + SubURL string + // Directory to load locale files. Default is "conf/locale" + Directory string + // File stores actual data of locale files. Used for in-memory purpose. + Files map[string][]byte + // Custom directory to overload locale files. Default is "custom/conf/locale" + CustomDirectory string + // Langauges that will be supported, order is meaningful. + Langs []string + // Human friendly names corresponding to Langs list. + Names []string + // Default language locale, leave empty to remain unset. + DefaultLang string + // Locale file naming style. Default is "locale_%s.ini". + Format string + // Name of language parameter name in URL. Default is "lang". + Parameter string + // Redirect when user uses get parameter to specify language. + Redirect bool + // Name that maps into template variable. Default is "i18n". + TmplName string + // Configuration section name. Default is "i18n". + Section string + // Domain used for `lang` cookie. Default is "" + CookieDomain string +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + if len(opt.Section) == 0 { + opt.Section = "i18n" + } + sec := macaron.Config().Section(opt.Section) + + opt.SubURL = strings.TrimSuffix(opt.SubURL, "/") + + if len(opt.Langs) == 0 { + opt.Langs = sec.Key("LANGS").Strings(",") + } + if len(opt.Names) == 0 { + opt.Names = sec.Key("NAMES").Strings(",") + } + if len(opt.Langs) == 0 { + panic("no language is specified") + } else if len(opt.Langs) != len(opt.Names) { + panic("length of langs is not same as length of names") + } + i18n.SetDefaultLang(opt.DefaultLang) + + if len(opt.Directory) == 0 { + opt.Directory = sec.Key("DIRECTORY").MustString("conf/locale") + } + if len(opt.CustomDirectory) == 0 { + opt.CustomDirectory = sec.Key("CUSTOM_DIRECTORY").MustString("custom/conf/locale") + } + if len(opt.Format) == 0 { + opt.Format = sec.Key("FORMAT").MustString("locale_%s.ini") + } + if len(opt.Parameter) == 0 { + opt.Parameter = sec.Key("PARAMETER").MustString("lang") + } + if !opt.Redirect { + opt.Redirect = sec.Key("REDIRECT").MustBool() + } + if len(opt.TmplName) == 0 { + opt.TmplName = sec.Key("TMPL_NAME").MustString("i18n") + } + + return opt +} + +type LangType struct { + Lang, Name string +} + +// I18n is a middleware provides localization layer for your application. +// Paramenter langs must be in the form of "en-US", "zh-CN", etc. +// Otherwise it may not recognize browser input. +func I18n(options ...Options) macaron.Handler { + opt := prepareOptions(options) + m := initLocales(opt) + return func(ctx *macaron.Context) { + isNeedRedir := false + hasCookie := false + + // 1. Check URL arguments. + lang := ctx.Query(opt.Parameter) + + // 2. Get language information from cookies. + if len(lang) == 0 { + lang = ctx.GetCookie("lang") + hasCookie = true + } else { + isNeedRedir = true + } + + // Check again in case someone modify by purpose. + if !i18n.IsExist(lang) { + lang = "" + isNeedRedir = false + hasCookie = false + } + + // 3. Get language information from 'Accept-Language'. + // The first element in the list is chosen to be the default language automatically. + if len(lang) == 0 { + tags, _, _ := language.ParseAcceptLanguage(ctx.Req.Header.Get("Accept-Language")) + tag, _, _ := m.Match(tags...) + lang = tag.String() + isNeedRedir = false + } + + curLang := LangType{ + Lang: lang, + } + + // Save language information in cookies. + if !hasCookie { + ctx.SetCookie("lang", curLang.Lang, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"), opt.CookieDomain) + } + + restLangs := make([]LangType, 0, i18n.Count()-1) + langs := i18n.ListLangs() + names := i18n.ListLangDescs() + for i, v := range langs { + if lang != v { + restLangs = append(restLangs, LangType{v, names[i]}) + } else { + curLang.Name = names[i] + } + } + + // Set language properties. + locale := Locale{i18n.Locale{lang}} + ctx.Map(locale) + ctx.Locale = locale + ctx.Data[opt.TmplName] = locale + ctx.Data["Tr"] = i18n.Tr + ctx.Data["Lang"] = locale.Lang + ctx.Data["LangName"] = curLang.Name + ctx.Data["AllLangs"] = append([]LangType{curLang}, restLangs...) + ctx.Data["RestLangs"] = restLangs + + if opt.Redirect && isNeedRedir { + ctx.Redirect(opt.SubURL + ctx.Req.RequestURI[:strings.Index(ctx.Req.RequestURI, "?")]) + } + } +} |