Skip to content

Commit

Permalink
gi18n-support-std-fs
Browse files Browse the repository at this point in the history
  • Loading branch information
ynwcel committed Nov 20, 2024
1 parent 0d1aed0 commit 2025b8c
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 2 deletions.
9 changes: 8 additions & 1 deletion i18n/gi18n/gi18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@
// Package gi18n implements internationalization and localization.
package gi18n

import "context"
import (
"context"
"io/fs"
)

// SetPath sets the directory path storing i18n files.
func SetPath(path string) error {
return Instance().SetPath(path)
}

func SetPathFS(dirfs fs.FS) error {
return Instance().SetPathFS(dirfs)
}

// SetLanguage sets the language for translator.
func SetLanguage(language string) {
Instance().SetLanguage(language)
Expand Down
62 changes: 61 additions & 1 deletion i18n/gi18n/gi18n_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package gi18n
import (
"context"
"fmt"
"io/fs"
"path/filepath"
"strings"
"sync"

Expand All @@ -30,6 +32,7 @@ const (
pathTypeNone pathType = "none"
pathTypeNormal pathType = "normal"
pathTypeGres pathType = "gres"
pathTypeFS pathType = "stdfs"
)

// Manager for i18n contents, it is concurrent safe, supporting hot reload.
Expand All @@ -43,6 +46,7 @@ type Manager struct {

// Options is used for i18n object configuration.
type Options struct {
PathFs fs.FS // I18n files storage fs.FS.
Path string // I18n files storage path.
Language string // Default local language.
Delimiters []string // Delimiters for variable parsing.
Expand All @@ -68,7 +72,11 @@ func New(options ...Options) *Manager {
var pathType = pathTypeNone
if len(options) > 0 {
opts = options[0]
pathType = opts.checkPathType(opts.Path)
if opts.PathFs != nil {
pathType = pathTypeFS
} else {
pathType = opts.checkPathType(opts.Path)
}
} else {
opts = Options{}
for _, folder := range searchFolders {
Expand Down Expand Up @@ -142,6 +150,13 @@ func (m *Manager) SetPath(path string) error {
return nil
}

func (m *Manager) SetPathFS(pathfs fs.FS) error {
m.pathType = pathTypeFS
m.options.PathFs = pathfs
m.reset()
return nil
}

// SetLanguage sets the language for translator.
func (m *Manager) SetLanguage(language string) {
m.options.Language = language
Expand Down Expand Up @@ -245,6 +260,51 @@ func (m *Manager) init(ctx context.Context) {
m.mu.Lock()
defer m.mu.Unlock()
switch m.pathType {
case pathTypeFS:
files1, err1 := fs.Glob(m.options.PathFs, "*.*")
files2, err2 := fs.Glob(m.options.PathFs, "*/*.*")
if err1 != nil || err2 != nil {
if err1 != nil {
intlog.Errorf(ctx, "load i18n files failed: %+v", err1)
} else {
intlog.Errorf(ctx, "load i18n files failed: %+v", err2)
}
return
}
files := append(files1, files2...)
if len(files) > 0 {
var (
name string
lang string
array []string
content []byte
err error
)
m.data = make(map[string]map[string]string)
for _, file := range files {
if content, err = fs.ReadFile(m.options.PathFs, file); err != nil {
intlog.Errorf(ctx, "get i18n file content failed: %+v", err)
continue
}
name = strings.TrimSuffix(filepath.Base(file), filepath.Ext(file))
array = strings.Split(file, "/")
if len(array) > 1 {
lang = array[0]
} else if len(array) == 1 {
lang = gfile.Name(array[0])
}
if m.data[lang] == nil {
m.data[lang] = make(map[string]string)
}
if j, err := gjson.LoadContent(content); err == nil {
for k, v := range j.Var().Map() {
m.data[lang][k] = gconv.String(v)
}
} else {
intlog.Errorf(ctx, "load i18n file '%s' failed: %+v", name, err)
}
}
}
case pathTypeGres:
files := m.options.Resource.ScanDirFile(m.options.Path, "*.*", true)
if len(files) > 0 {
Expand Down
83 changes: 83 additions & 0 deletions i18n/gi18n/gi18n_z_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package gi18n_test

import (
"os"
"time"

"github.com/gogf/gf/v2/encoding/gbase64"
Expand Down Expand Up @@ -77,6 +78,58 @@ func Test_Basic(t *testing.T) {
})
}

func Test_BasicFS(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
i18n := gi18n.New(gi18n.Options{
PathFs: os.DirFS(gtest.DataPath("i18n")),
})
i18n.SetLanguage("none")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")

i18n.SetLanguage("ja")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界")

i18n.SetLanguage("zh-CN")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界")
i18n.SetDelimiters("{$", "}")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")
t.Assert(i18n.T(context.Background(), "{$hello}{$world}"), "你好世界")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")
t.Assert(i18n.T(context.Background(), "{$你好} {$世界}"), "hello world")
// undefined variables.
t.Assert(i18n.T(context.Background(), "{$你好1}{$世界1}"), "{$你好1}{$世界1}")
})

gtest.C(t, func(t *gtest.T) {
i18n := gi18n.New(gi18n.Options{
PathFs: os.DirFS(gtest.DataPath("i18n-file")),
})
i18n.SetLanguage("none")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")

i18n.SetLanguage("ja")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界")

i18n.SetLanguage("zh-CN")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界")
t.Assert(i18n.T(context.Background(), "{#你好} {#世界}"), "hello world")
})

gtest.C(t, func(t *gtest.T) {
i18n := gi18n.New(gi18n.Options{
PathFs: os.DirFS(gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-dir"),
})
i18n.SetLanguage("none")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")

i18n.SetLanguage("ja")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界")

i18n.SetLanguage("zh-CN")
t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界")
})
}

func Test_TranslateFormat(t *testing.T) {
// Tf
gtest.C(t, func(t *gtest.T) {
Expand Down Expand Up @@ -121,6 +174,36 @@ func Test_DefaultManager(t *testing.T) {
})
}

func Test_FSDefaultManager(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err := gi18n.SetPathFS(os.DirFS(gtest.DataPath("i18n")))
t.AssertNil(err)

gi18n.SetLanguage("none")
t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")

gi18n.SetLanguage("ja")
t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界")

gi18n.SetLanguage("zh-CN")
t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "你好世界")
})

gtest.C(t, func(t *gtest.T) {
err := gi18n.SetPathFS(os.DirFS(gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-dir"))
t.AssertNil(err)

gi18n.SetLanguage("none")
t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "{#hello}{#world}")

gi18n.SetLanguage("ja")
t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "こんにちは世界")

gi18n.SetLanguage("zh-CN")
t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "你好世界")
})
}

func Test_Instance(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gi18n.Instance()
Expand Down

0 comments on commit 2025b8c

Please sign in to comment.