Skip to content

Commit

Permalink
1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
quenbyako committed Jul 22, 2020
1 parent 4510bf2 commit 96b83ec
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 82 deletions.
2 changes: 0 additions & 2 deletions AUTHORS

This file was deleted.

62 changes: 39 additions & 23 deletions context.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package l10n

import (
"errors"
"github.com/pkg/errors"
"github.com/xelaj/errs"

"github.com/iafan/Plurr/go/plurr"
)
Expand All @@ -16,18 +17,31 @@ type Context struct {
}

// GetLanguage returns the current language of the context.
func (lc *Context) GetLang() string {
func (lc *Context) Lang() string {
return lc.lang
}

// Tr returns a translated version of the string
func (lc *Context) Tr(key string) string {
return lc.tr(key, lc.lang)
res, err := lc.tr(key)
if err != nil {
return key
}
return res
}

func (lc *Context) TrWithError(key string) (string, error) {
return lc.tr(key)
}

// Trf returns a formatted version of the string
func (lc *Context) Trf(key string, params plurr.Params) (string, error) {
s, err := lc.plurr.Format(lc.Tr(key), params)
translated, err := lc.tr(key)
if err != nil {
return "", errors.Wrap(err, "translating")
}

s, err := lc.plurr.Format(translated, params)
if err != nil {
return "", errors.New(key + ": " + err.Error())
}
Expand All @@ -43,33 +57,35 @@ func (lc *Context) Strf(key string, params plurr.Params) string {
return r
}

func (lc *Context) tr(key, lang string) string {
resource, ok := lc.pool.Resources[lang]
if !ok {
err := lc.pool.PreloadResource(lang)
if err != nil {
return key
}

resource = lc.pool.Resources[lang]
// tr исключительно достает из пула только сами переводы, дополнением занимаются публичные функции
// tr будет стараться искать альтернативные ключи
func (lc *Context) tr(key string) (string, error) {
err := lc.pool.LoadResource(lc.lang)
if err != nil {
return key, errors.Wrap(err, "loading resource")
}

resource := lc.pool.Resources[lc.lang]

s, ok := resource[key]
if ok {
return s
return s, nil
}
return lc.trAlternate(key, lang)
}

func (lc *Context) trAlternate(key, lang string) string {
info, err := langInfo(lc.pool.resourcePath, lang)
if err != nil {
return key
alternativeLocale := ""
for _, locale := range lc.pool.locales {
if locale.Code == lc.lang {
alternativeLocale = locale.Extends
}
}
if alternativeLocale == "" {
return key, errs.NotFound("key", key)
}

if info.Extends != "" {
return lc.tr(key, info.Extends)
ctx, err := lc.pool.GetContext(alternativeLocale)
if err != nil {
return key, errors.Wrap(err, "getting context")
}

return key
return ctx.tr(key)
}
2 changes: 1 addition & 1 deletion examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func main() {
// create localization pool with "en" as a default language
// and load string resources
lp, err := l10n.NewPool("/path/to/your/system/locale", "YourAppName", "en_GB")
lp, err := l10n.NewPool("/home/r0ck3t/go/src/github.com/xelaj/go-l10n/examples/locale", "YourAppName", "en_GB")
if err != nil {
fmt.Println("you don't have en_GB localization!")
fmt.Println("'lp' will work, but return message keys only!")
Expand Down
141 changes: 141 additions & 0 deletions l10n.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package l10n

import (
"path/filepath"

"github.com/xelaj/errs"

"github.com/gobuffalo/envy"
"github.com/iafan/Plurr/go/plurr"
"github.com/pkg/errors"
"github.com/ungerik/go-dry"
"github.com/xelaj/v"
)

var (
AppName = ""
LocalesPath = ""
builtinPool *Pool
mainContext Translator
)

func Init() {
if builtinPool != nil {
return
}
if AppName == "" {
AppName = v.AppName
}
if LocalesPath == "" {
LocalesPath = "/usr/share/locale"
}

var err error
builtinPool, err = NewPool(LocalesPath, AppName, false)
dry.PanicIfErr(err)
mainContext, err = GetContext(envy.Get("LANGUAGE", "en_US"))
dry.PanicIfErr(err)
}

func SetLanguage(lang string) error {
Init()
ctx, err := builtinPool.GetContext(lang)
if err != nil {
return errors.Wrap(err, "getting language context")
}
mainContext = ctx
return nil
}

func Tr(key string) string {
Init()
return mainContext.Tr(key)
}

func Trf(key string, params plurr.Params) (string, error) {
Init()
return mainContext.Trf(key, params)
}

func Strf(key string, params plurr.Params) string {
Init()
return mainContext.Strf(key, params)
}

func GetContext(lang string) (Translator, error) {
Init()

err := LoadResource(lang)
if err != nil {
return nil, errors.Wrap(err, "loading resource")
}

return &Context{
pool: builtinPool,
lang: lang,
plurr: plurr.New().SetLocale(lang),
}, nil
}

func LoadResource(lang string) error {
Init()

res, err := walkDir(filepath.Join(builtinPool.resourcePath, lang, builtinPool.appName))
if err == nil {
builtinPool.Resources[lang] = res
}
return errors.Wrap(err, "can't load '"+lang+"'")
}

func MustAll(items []string) error {
Init()

errorsMultiple := &errs.MultipleErrors{}
for _, item := range items {
_, err := mainContext.TrWithError(item)
errorsMultiple.Add(err)
}
return errorsMultiple.Normalize()
}

type MsgCodeReturner interface {
Code() string
}

type codeGetter uint8

type MessageCode func(privateParamss ...interface{}) string

func Message(key string) MessageCode {
return func(privateParams ...interface{}) string {
if len(privateParams) > 0 {
if _, ok := privateParams[0].(codeGetter); !ok {
panic("do not use additional parameters")
}
return key
}
return Tr(key)
}
}

func (m MessageCode) Code() string {
return m(codeGetter(0))
}

type MessageWithParamsCode func(params map[string]interface{}, privateParams ...interface{}) string

func MessageWithParams(key string) MessageWithParamsCode {
return func(params map[string]interface{}, privateParams ...interface{}) string {
if len(privateParams) > 0 {
if _, ok := privateParams[0].(codeGetter); !ok {
panic("do not use additional parameters")
}
return key
}
return Strf(key, params)
}
}

func (m MessageWithParamsCode) Code() string {
return m(nil, codeGetter(0))
}
100 changes: 82 additions & 18 deletions loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,33 @@ package l10n

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
)

type translationFile map[string]struct {
Message string
Description string
}
"github.com/ungerik/go-dry"

"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)

// Load takes the JSON file name and returns string resources
// in a format compatible with loc.Resources map values.
// It will panic if there's a problem with loading/unmarshaling JSON.
func load(filename string) (Resource, error) {
t := make(Resource)
var tf translationFile

data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}

err = json.Unmarshal(data, &tf)
if err != nil {
return nil, err
}

// convert JSON data structure into a destination map format
for k, v := range tf {
t[k] = v.Message
switch ext := filepath.Ext(filename); ext {
case ".yaml", ".yml":
return loadYamlFile(data)
case ".json":
return loadJsonFile(data)
default:
return nil, errors.New("unknown format: " + ext)
}
return t, nil
}

func walkDir(path string) (Resource, error) {
Expand Down Expand Up @@ -76,3 +71,72 @@ func walkDir(path string) (Resource, error) {

return res, nil
}

func loadYamlFile(data []byte) (Resource, error) {
res := make(map[string]interface{})

err := yaml.Unmarshal(data, &res)
if err != nil {
return nil, errors.Wrap(err, "parsing file")
}

tr := make(Resource)

for name, data := range res {
switch value := data.(type) {
case map[interface{}]interface{}:
found := false
for ki, vi := range value {
k, ok := ki.(string)
if !ok {
return nil, errors.New("keys only strings")
}

if dry.StringInSlice(k, []string{"msg", "message"}) {
tr[name] = fmt.Sprint(vi)
found = true
break
}
}
if !found {
return nil, errors.New("resource " + name + "doesn't have message property")
}
default:
tr[name] = fmt.Sprint(value)
}
}

return tr, nil
}

func loadJsonFile(data []byte) (Resource, error) {
res := make(map[string]interface{})

err := json.Unmarshal(data, &res)
if err != nil {
return nil, errors.Wrap(err, "parsing file")
}

tr := make(Resource)

for name, data := range res {
switch value := data.(type) {
case map[string]interface{}:
found := false
for key, vi := range value {
if dry.StringInSlice(key, []string{"msg", "message"}) {
tr[name] = fmt.Sprint(vi)
found = true
break
}
}
if !found {
return nil, errors.New("resource " + name + "doesn't have message property")
}
default:
tr[name] = fmt.Sprint(value)
}
}

return tr, nil
}
Loading

0 comments on commit 96b83ec

Please sign in to comment.