add package gi18n

This commit is contained in:
John
2019-09-01 00:30:01 +08:00
parent 6c61704e60
commit 15a6680833
54 changed files with 211 additions and 99 deletions

View File

@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/gogf/gf/util/gi18n"
"github.com/gogf/gf/i18n/gi18n"
)
func main() {

View File

@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/gogf/gf/util/gi18n"
"github.com/gogf/gf/i18n/gi18n"
)
func main() {

View File

@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/gogf/gf/util/gi18n"
"github.com/gogf/gf/i18n/gi18n"
)
func main() {

View File

@ -9,12 +9,12 @@ import (
)
func main() {
t := g.I18n()
t.SetLanguage("ja")
err := t.SetPath("/i18n-dir")
m := g.I18n()
m.SetLanguage("ja")
err := m.SetPath("/i18n-dir")
if err != nil {
panic(err)
}
fmt.Println(t.Translate(`hello`))
fmt.Println(t.Translate(`{{hello}}{{world}}!`))
fmt.Println(m.Translate(`hello`))
fmt.Println(m.Translate(`{{hello}}{{world}}!`))
}

View File

@ -33,7 +33,7 @@ golang version >= 1.10
# Architecture
<div align=center>
<img src="https://gfcdn.johng.cn/images/arch.png"/>
<img src="https://goframe.org/images/arch.png"/>
</div>
# Quick Start

View File

@ -37,7 +37,7 @@ golang版本 >= 1.10
# 架构
<div align=center>
<img src="https://gfcdn.johng.cn/images/arch.png"/>
<img src="https://goframe.org/images/arch.png"/>
</div>

View File

@ -11,13 +11,13 @@ import (
"github.com/gogf/gf/database/gkvdb"
"github.com/gogf/gf/database/gredis"
"github.com/gogf/gf/frame/gins"
"github.com/gogf/gf/i18n/gi18n"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/net/gtcp"
"github.com/gogf/gf/net/gudp"
"github.com/gogf/gf/os/gcfg"
"github.com/gogf/gf/os/gres"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gi18n"
)
// Server returns an instance of http server with specified name.
@ -57,9 +57,9 @@ func Resource(name ...string) *gres.Resource {
return gins.Resource(name...)
}
// I18n returns an instance of gi18n.Translator.
// I18n returns an instance of gi18n.Manager.
// The parameter <name> is the name for the instance.
func I18n(name ...string) *gi18n.Translator {
func I18n(name ...string) *gi18n.Manager {
return gins.I18n(name...)
}

View File

@ -18,6 +18,7 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/database/gredis"
"github.com/gogf/gf/i18n/gi18n"
"github.com/gogf/gf/os/gcfg"
"github.com/gogf/gf/os/gfsnotify"
"github.com/gogf/gf/os/glog"
@ -26,7 +27,6 @@ import (
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gi18n"
)
const (
@ -86,9 +86,9 @@ func Resource(name ...string) *gres.Resource {
return gres.Instance(name...)
}
// I18n returns an instance of gi18n.Translator.
// I18n returns an instance of gi18n.Manager.
// The parameter <name> is the name for the instance.
func I18n(name ...string) *gi18n.Translator {
func I18n(name ...string) *gi18n.Manager {
return gi18n.Instance(name...)
}

View File

@ -8,31 +8,31 @@
package gi18n
var (
defaultTranslator = Instance()
defaultManager = Instance()
)
// SetPath sets the directory path storing i18n files.
func SetPath(path string) error {
return defaultTranslator.SetPath(path)
return defaultManager.SetPath(path)
}
// SetLanguage sets the language for translator.
func SetLanguage(language string) {
defaultTranslator.SetLanguage(language)
defaultManager.SetLanguage(language)
}
// SetDelimiters sets the delimiters for translator.
func SetDelimiters(left, right string) {
defaultTranslator.SetDelimiters(left, right)
defaultManager.SetDelimiters(left, right)
}
// T is alias of Translate.
func T(content string, language ...string) string {
return defaultTranslator.T(content, language...)
return defaultManager.T(content, language...)
}
// Translate translates <content> with configured language.
// The parameter <language> specifies custom translation language ignoring configured language.
func Translate(content string, language ...string) string {
return defaultTranslator.Translate(content, language...)
return defaultManager.Translate(content, language...)
}

View File

@ -20,12 +20,12 @@ var (
// Instance returns an instance of Resource.
// The parameter <name> is the name for the instance.
func Instance(name ...string) *Translator {
func Instance(name ...string) *Manager {
key := DEFAULT_NAME
if len(name) > 0 && name[0] != "" {
key = name[0]
}
return instances.GetOrSetFuncLock(key, func() interface{} {
return New()
}).(*Translator)
}).(*Manager)
}

View File

@ -24,8 +24,8 @@ import (
"github.com/gogf/gf/os/gres"
)
// Translator, it is concurrent safe, supporting hot reload.
type Translator struct {
// Manager, it is concurrent safe, supporting hot reload.
type Manager struct {
mu sync.RWMutex
data map[string]map[string]string // Translating map.
pattern string // Pattern for regex parsing.
@ -42,7 +42,7 @@ var (
defaultDelimiters = []string{"{#", "}"}
)
func New(options ...Options) *Translator {
func New(options ...Options) *Manager {
var opts Options
if len(options) > 0 {
opts = options[0]
@ -52,7 +52,7 @@ func New(options ...Options) *Translator {
if len(opts.Delimiters) == 0 {
opts.Delimiters = defaultDelimiters
}
return &Translator{
return &Manager{
options: opts,
pattern: fmt.Sprintf(
`%s(\w+)%s`,
@ -77,45 +77,45 @@ func DefaultOptions() Options {
}
// SetPath sets the directory path storing i18n files.
func (t *Translator) SetPath(path string) error {
func (m *Manager) SetPath(path string) error {
if gres.Contains(path) {
t.options.Path = path
m.options.Path = path
} else {
realPath, _ := gfile.Search(path)
if realPath == "" {
return errors.New(fmt.Sprintf(`%s does not exist`, path))
}
t.options.Path = realPath
m.options.Path = realPath
}
return nil
}
// SetLanguage sets the language for translator.
func (t *Translator) SetLanguage(language string) {
t.options.Language = language
func (m *Manager) SetLanguage(language string) {
m.options.Language = language
}
// SetDelimiters sets the delimiters for translator.
func (t *Translator) SetDelimiters(left, right string) {
t.pattern = fmt.Sprintf(`%s(\w+)%s`, gregex.Quote(left), gregex.Quote(right))
func (m *Manager) SetDelimiters(left, right string) {
m.pattern = fmt.Sprintf(`%s(\w+)%s`, gregex.Quote(left), gregex.Quote(right))
}
// T is alias of Translate.
func (t *Translator) T(content string, language ...string) string {
return t.Translate(content, language...)
func (m *Manager) T(content string, language ...string) string {
return m.Translate(content, language...)
}
// Translate translates <content> with configured language.
// The parameter <language> specifies custom translation language ignoring configured language.
func (t *Translator) Translate(content string, language ...string) string {
t.init()
t.mu.RLock()
defer t.mu.RUnlock()
func (m *Manager) Translate(content string, language ...string) string {
m.init()
m.mu.RLock()
defer m.mu.RUnlock()
var data map[string]string
if len(language) > 0 {
data = t.data[language[0]]
data = m.data[language[0]]
} else {
data = t.data[t.options.Language]
data = m.data[m.options.Language]
}
if data == nil {
return content
@ -125,7 +125,7 @@ func (t *Translator) Translate(content string, language ...string) string {
return v
}
// Parse content as variables container.
result, _ := gregex.ReplaceStringFuncMatch(t.pattern, content, func(match []string) string {
result, _ := gregex.ReplaceStringFuncMatch(m.pattern, content, func(match []string) string {
if v, ok := data[match[1]]; ok {
return v
}
@ -134,73 +134,73 @@ func (t *Translator) Translate(content string, language ...string) string {
return result
}
func (t *Translator) init() {
t.mu.RLock()
if t.data != nil {
t.mu.RUnlock()
func (m *Manager) init() {
m.mu.RLock()
if m.data != nil {
m.mu.RUnlock()
return
}
t.mu.RUnlock()
m.mu.RUnlock()
t.mu.Lock()
defer t.mu.Unlock()
if gres.Contains(t.options.Path) {
files := gres.ScanDirFile(t.options.Path, "*.*", true)
m.mu.Lock()
defer m.mu.Unlock()
if gres.Contains(m.options.Path) {
files := gres.ScanDirFile(m.options.Path, "*.*", true)
if len(files) > 0 {
var path string
var name string
var lang string
var array []string
t.data = make(map[string]map[string]string)
m.data = make(map[string]map[string]string)
for _, file := range files {
name = file.Name()
path = name[len(t.options.Path)+1:]
path = name[len(m.options.Path)+1:]
array = strings.Split(path, "/")
if len(array) > 1 {
lang = array[0]
} else {
lang = gfile.Name(array[0])
}
if t.data[lang] == nil {
t.data[lang] = make(map[string]string)
if m.data[lang] == nil {
m.data[lang] = make(map[string]string)
}
j, _ := gjson.LoadContent(file.Content())
if j != nil {
for k, v := range j.ToMap() {
t.data[lang][k] = gconv.String(v)
m.data[lang][k] = gconv.String(v)
}
}
}
}
} else {
files, _ := gfile.ScanDirFile(t.options.Path, "*.*", true)
files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true)
if len(files) > 0 {
var path string
var lang string
var array []string
t.data = make(map[string]map[string]string)
m.data = make(map[string]map[string]string)
for _, file := range files {
path = file[len(t.options.Path)+1:]
path = file[len(m.options.Path)+1:]
array = strings.Split(path, "/")
if len(array) > 1 {
lang = array[0]
} else {
lang = gfile.Name(array[0])
}
if t.data[lang] == nil {
t.data[lang] = make(map[string]string)
if m.data[lang] == nil {
m.data[lang] = make(map[string]string)
}
j, _ := gjson.LoadContent(gfile.GetBytes(file))
if j != nil {
for k, v := range j.ToMap() {
t.data[lang][k] = gconv.String(v)
m.data[lang][k] = gconv.String(v)
}
}
}
_, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) {
t.mu.Lock()
t.data = nil
t.mu.Unlock()
m.mu.Lock()
m.data = nil
m.mu.Unlock()
gfsnotify.Exit()
})
}

View File

@ -0,0 +1,143 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gi18n_test
import (
"testing"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/i18n/gi18n"
"github.com/gogf/gf/debug/gdebug"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/test/gtest"
_ "github.com/gogf/gf/os/gres/testdata"
)
func Test_Basic(t *testing.T) {
gtest.Case(t, func() {
t := gi18n.New(gi18n.Options{
Path: gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n",
})
t.SetLanguage("none")
gtest.Assert(t.T("{#hello}{#world}"), "{#hello}{#world}")
t.SetLanguage("ja")
gtest.Assert(t.T("{#hello}{#world}"), "こんにちは世界")
t.SetLanguage("zh-CN")
gtest.Assert(t.T("{#hello}{#world}"), "你好世界")
t.SetDelimiters("{$", "}")
gtest.Assert(t.T("{#hello}{#world}"), "{#hello}{#world}")
gtest.Assert(t.T("{$hello}{$world}"), "你好世界")
})
gtest.Case(t, func() {
t := gi18n.New(gi18n.Options{
Path: gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-file",
})
t.SetLanguage("none")
gtest.Assert(t.T("{#hello}{#world}"), "{#hello}{#world}")
t.SetLanguage("ja")
gtest.Assert(t.T("{#hello}{#world}"), "こんにちは世界")
t.SetLanguage("zh-CN")
gtest.Assert(t.T("{#hello}{#world}"), "你好世界")
})
gtest.Case(t, func() {
t := gi18n.New(gi18n.Options{
Path: gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-dir",
})
t.SetLanguage("none")
gtest.Assert(t.T("{#hello}{#world}"), "{#hello}{#world}")
t.SetLanguage("ja")
gtest.Assert(t.T("{#hello}{#world}"), "こんにちは世界")
t.SetLanguage("zh-CN")
gtest.Assert(t.T("{#hello}{#world}"), "你好世界")
})
}
func Test_DefaultManager(t *testing.T) {
gtest.Case(t, func() {
err := gi18n.SetPath(gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n")
gtest.Assert(err, nil)
gi18n.SetLanguage("none")
gtest.Assert(gi18n.T("{#hello}{#world}"), "{#hello}{#world}")
gi18n.SetLanguage("ja")
gtest.Assert(gi18n.T("{#hello}{#world}"), "こんにちは世界")
gi18n.SetLanguage("zh-CN")
gtest.Assert(gi18n.T("{#hello}{#world}"), "你好世界")
})
gtest.Case(t, func() {
err := gi18n.SetPath(gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-dir")
gtest.Assert(err, nil)
gi18n.SetLanguage("none")
gtest.Assert(gi18n.Translate("{#hello}{#world}"), "{#hello}{#world}")
gi18n.SetLanguage("ja")
gtest.Assert(gi18n.Translate("{#hello}{#world}"), "こんにちは世界")
gi18n.SetLanguage("zh-CN")
gtest.Assert(gi18n.Translate("{#hello}{#world}"), "你好世界")
})
}
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
m := gi18n.Instance()
err := m.SetPath("/i18n-dir")
gtest.Assert(err, nil)
m.SetLanguage("zh-CN")
gtest.Assert(m.T("{#hello}{#world}"), "你好世界")
})
gtest.Case(t, func() {
m := gi18n.Instance()
gtest.Assert(m.T("{#hello}{#world}"), "你好世界")
})
gtest.Case(t, func() {
gtest.Assert(g.I18n().T("{#hello}{#world}"), "你好世界")
})
gtest.Case(t, func() {
m := gi18n.Instance(gconv.String(gtime.Nanosecond()))
gtest.Assert(m.T("{#hello}{#world}"), "{#hello}{#world}")
})
}
func Test_Resource(t *testing.T) {
gtest.Case(t, func() {
m := g.I18n("resource")
err := m.SetPath("/i18n-dir")
gtest.Assert(err, nil)
m.SetLanguage("none")
gtest.Assert(m.T("{#hello}{#world}"), "{#hello}{#world}")
m.SetLanguage("ja")
gtest.Assert(m.T("{#hello}{#world}"), "こんにちは世界")
m.SetLanguage("zh-CN")
gtest.Assert(m.T("{#hello}{#world}"), "你好世界")
})
}

View File

@ -13,7 +13,7 @@ import (
"strings"
"text/template"
"github.com/gogf/gf/util/gi18n"
"github.com/gogf/gf/i18n/gi18n"
"github.com/gogf/gf/os/gfcache"

View File

@ -1,31 +0,0 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gi18n_test
import (
"testing"
"github.com/gogf/gf/util/gi18n"
"github.com/gogf/gf/debug/gdebug"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/test/gtest"
)
func Test_Basic(t *testing.T) {
gtest.Case(t, func() {
t := gi18n.New(gi18n.Options{
Path: gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n",
})
t.SetLanguage("none")
gtest.Assert(t.T("{#hello}{#world}"), "{#hello}{#world}")
t.SetLanguage("ja")
gtest.Assert(t.T("{#hello}{#world}"), "こんにちは世界")
})
}