diff --git a/README.md b/README.md index 38c052e..56388c2 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,11 @@ bundle, err := mf.NewBundle( mf.WithLangFallback(language.BritishEnglish, language.English), mf.WithLangFallback(language.Portuguese, language.Spanish), + // Load all yaml files in directory as messages + mf.WithYamlProvider(messagesDir), + + // or you could use your own custom message provider + // mf.WithProvider(sqlMessageProvider), // We assume that the translated messages are mostly correct. // However, if any errors occur during translation, @@ -77,12 +82,6 @@ bundle, err := mf.NewBundle( }), ) -if err != nil { - log.Fatal(err) -} - -// Load all yaml files in directory as messages -err = bundle.LoadDir(messagesDir) if err != nil { log.Fatal(err) } @@ -131,6 +130,8 @@ func main() { mf.WithLangFallback(language.BritishEnglish, language.English), mf.WithLangFallback(language.Portuguese, language.Spanish), + mf.WithYamlProvider(messagesDir), + mf.WithErrorHandler(func(err error, id string, ctx map[string]any) { slog.Error(err.Error(), slog.String("id", id), slog.Any("ctx", ctx)) }), @@ -140,11 +141,6 @@ func main() { log.Fatal(err) } - err = bundle.LoadDir(messagesDir) - if err != nil { - log.Fatal(err) - } - tr := bundle.Translator("es") slog.Info(tr.Trans("say_hello", mf.Arg("name", "Bob"))) diff --git a/example/base/main.go b/example/base/main.go index a90e809..8822cfd 100644 --- a/example/base/main.go +++ b/example/base/main.go @@ -2,7 +2,6 @@ package main import ( "embed" - "log" "log/slog" "math/rand/v2" @@ -20,6 +19,8 @@ func main() { mf.WithLangFallback(language.BritishEnglish, language.English), mf.WithLangFallback(language.Portuguese, language.Spanish), + mf.WithYamlProvider(messagesDir), + mf.WithErrorHandler(func(err error, id string, ctx map[string]any) { slog.Error(err.Error(), slog.String("id", id), slog.Any("ctx", ctx)) @@ -29,12 +30,7 @@ func main() { ) if err != nil { - log.Fatal(err) - } - - err = bundle.LoadDir(messagesDir) - if err != nil { - log.Fatal(err) + panic(err) } tr := bundle.Translator("en") diff --git a/example/base/var/messages.es.yaml b/example/base/var/messages.es.yaml index 5190802..efd5c9b 100644 --- a/example/base/var/messages.es.yaml +++ b/example/base/var/messages.es.yaml @@ -6,8 +6,8 @@ subtitle: >- } say: - hello: Hello, {name}! - goodbye: Goodbye, {name}! + hello: Hola, {name}! + goodbye: AdiĆ³s, {name}! user: profile: diff --git a/message/plural.go b/message/plural.go index 4f658c0..98ab26d 100644 --- a/message/plural.go +++ b/message/plural.go @@ -88,7 +88,7 @@ func (p *Plural) Eval(ctx Context) (string, error) { } if p.Offset > 0 { - offset := uint64(p.Offset) + offset := uint64(p.Offset) //nolint: gosec if offset < np.i { np.i -= offset } else { @@ -124,27 +124,27 @@ func toPluralForm(num any) (pm, error) { if i < 0 { i = -i } - return pm{i: uint64(i)}, nil + return pm{i: uint64(i)}, nil //nolint: gosec case int8: if i < 0 { i = -i } - return pm{i: uint64(i)}, nil + return pm{i: uint64(i)}, nil //nolint: gosec case int16: if i < 0 { i = -i } - return pm{i: uint64(i)}, nil + return pm{i: uint64(i)}, nil //nolint: gosec case int32: if i < 0 { i = -i } - return pm{i: uint64(i)}, nil + return pm{i: uint64(i)}, nil //nolint: gosec case int64: if i < 0 { i = -i } - return pm{i: uint64(i)}, nil + return pm{i: uint64(i)}, nil //nolint: gosec case uint: return pm{i: uint64(i)}, nil case uint8: diff --git a/mf/bundle.go b/mf/bundle.go index 702318b..e33a5b3 100644 --- a/mf/bundle.go +++ b/mf/bundle.go @@ -1,26 +1,20 @@ package mf import ( - "fmt" - "io" "io/fs" - "path" - "strings" "github.com/pkg/errors" "golang.org/x/text/language" ) type Bundle interface { - LoadMessages(rd fs.FS, path string, lang language.Tag) error - LoadDir(dir fs.FS) error Translator(lang string) Translator } type bundle struct { - fallbacks map[language.Tag]language.Tag - translators map[language.Tag]Translator - dictionaries map[language.Tag]Dictionary + fallbacks map[language.Tag]language.Tag + translators map[language.Tag]Translator + provider MessageProvider defaultLang language.Tag defaultErrorHandler ErrorHandler @@ -28,77 +22,31 @@ type bundle struct { type ErrorHandler func(err error, id string, ctx map[string]any) -type BundleOption func(b *bundle) +type BundleOption func(b *bundle) error func NewBundle(options ...BundleOption) (Bundle, error) { bundle := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, + fallbacks: map[language.Tag]language.Tag{}, + translators: map[language.Tag]Translator{}, defaultLang: language.Und, defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, } for _, option := range options { - option(bundle) - } - - // TODO: check fallbacks for cicles en -> es -> en -> ... - - return bundle, nil -} - -func (b *bundle) LoadMessages(rd fs.FS, path string, lang language.Tag) error { - yamlFile, err := rd.Open(path) - if err != nil { - return errors.Wrap(err, "unable to open file") - } - - yamlData, err := io.ReadAll(yamlFile) - if err != nil { - return errors.Wrap(err, "unable to read file") - } - - _, hasDictionary := b.dictionaries[lang] - if hasDictionary { - return fmt.Errorf("unable to load %s: language %s already has messages loaded", path, lang) - } - - b.dictionaries[lang], err = NewDictionary(yamlData) - if err != nil { - return errors.Wrap(err, "unable to create dictionary") - } - - return nil -} - -func (b *bundle) LoadDir(dir fs.FS) error { - return fs.WalkDir(dir, ".", func(p string, f fs.DirEntry, err error) error { + err := option(bundle) if err != nil { - return err - } - - if f.IsDir() { - return nil - } - - if path.Ext(f.Name()) != ".yaml" && path.Ext(f.Name()) != ".yml" { - return nil + return nil, err } + } - nameParts := strings.Split(f.Name(), ".") - if len(nameParts) < 2 { - return fmt.Errorf("no lang in file %s", f.Name()) - } + if bundle.provider == nil { + return nil, errors.New("you have add message provider with WithFSProvider or WithProvider") + } - tag, err := language.Parse(nameParts[len(nameParts)-2]) - if err != nil { - return errors.Wrap(err, "unable to parse language from filename") - } + // TODO: check fallbacks for cicles en -> es -> en -> ... - return b.LoadMessages(dir, p, tag) - }) + return bundle, nil } func (b *bundle) Translator(lang string) Translator { @@ -123,61 +71,59 @@ func (b *bundle) getTranlator(tag language.Tag) Translator { return tr } - dictionary, hasDictionary := b.dictionaries[tag] + var fallback Translator fallbackTag, hasFallback := b.fallbacks[tag] - - if hasDictionary { - var fallback Translator - - if hasFallback { - fallback = b.getTranlator(fallbackTag) - } else if tag != b.defaultLang { - fallback = b.getTranlator(b.defaultLang) - } - - return &translator{ - dictionary: dictionary, - fallback: fallback, - errorHandler: b.defaultErrorHandler, - lang: tag, - } - } - if hasFallback { - return b.getTranlator(fallbackTag) - } - - tr, ok = b.translators[b.defaultLang] - if ok { - return tr - } - - dictionary, hasDictionary = b.dictionaries[b.defaultLang] - if !hasDictionary { - dictionary = &dummyDictionary{} + fallback = b.getTranlator(fallbackTag) + } else if tag != b.defaultLang { + fallback = b.getTranlator(b.defaultLang) } return &translator{ - dictionary: dictionary, + provider: b.provider, + fallback: fallback, errorHandler: b.defaultErrorHandler, lang: tag, } } func WithDefaulLangFallback(l language.Tag) BundleOption { - return func(b *bundle) { + return func(b *bundle) error { b.defaultLang = l + + return nil + } +} + +func WithYamlProvider(dir fs.FS) BundleOption { + return func(b *bundle) error { + provider, err := NewYamlMessageProvider(dir) + b.provider = provider + + return err + } +} + +func WithProvider(provider MessageProvider) BundleOption { + return func(b *bundle) error { + b.provider = provider + + return nil } } func WithLangFallback(from language.Tag, to language.Tag) BundleOption { - return func(b *bundle) { + return func(b *bundle) error { b.fallbacks[from] = to + + return nil } } func WithErrorHandler(handler ErrorHandler) BundleOption { - return func(b *bundle) { + return func(b *bundle) error { b.defaultErrorHandler = handler + + return nil } } diff --git a/mf/bundle_test.go b/mf/bundle_test.go index 7eab3b0..6cc26f8 100644 --- a/mf/bundle_test.go +++ b/mf/bundle_test.go @@ -1,12 +1,12 @@ package mf import ( - "io/fs" "reflect" "runtime" "testing" "testing/fstest" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/text/language" @@ -16,6 +16,7 @@ func TestNewBundle(t *testing.T) { b, err := NewBundle( WithDefaulLangFallback(language.English), WithLangFallback(language.Portuguese, language.Spanish), + WithProvider(new(MockedProvider)), ) require.NoError(t, err) @@ -24,39 +25,36 @@ func TestNewBundle(t *testing.T) { func TestWithDefaulLangFallback(t *testing.T) { b := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, + fallbacks: map[language.Tag]language.Tag{}, + translators: map[language.Tag]Translator{}, defaultLang: language.Und, defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, } assert.Equal(t, language.Und, b.defaultLang) - WithDefaulLangFallback(language.Afrikaans)(b) + require.NoError(t, WithDefaulLangFallback(language.Afrikaans)(b)) assert.Equal(t, language.Afrikaans, b.defaultLang) } func TestWithLangFallback(t *testing.T) { b := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, + fallbacks: map[language.Tag]language.Tag{}, + translators: map[language.Tag]Translator{}, defaultLang: language.Und, defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, } assert.Equal(t, language.Und, b.fallbacks[language.AmericanEnglish]) - WithLangFallback(language.AmericanEnglish, language.English)(b) + require.NoError(t, WithLangFallback(language.AmericanEnglish, language.English)(b)) assert.Equal(t, language.English, b.fallbacks[language.AmericanEnglish]) } func TestWithErrorHandler(t *testing.T) { b := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, + fallbacks: map[language.Tag]language.Tag{}, + translators: map[language.Tag]Translator{}, defaultLang: language.Und, defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, @@ -64,88 +62,32 @@ func TestWithErrorHandler(t *testing.T) { assert.NotNil(t, b.defaultErrorHandler) errHandler := func(_ error, _ string, _ map[string]any) {} - WithErrorHandler(errHandler)(b) + require.NoError(t, WithErrorHandler(errHandler)(b)) funcName1 := runtime.FuncForPC(reflect.ValueOf(errHandler).Pointer()).Name() funcName2 := runtime.FuncForPC(reflect.ValueOf(b.defaultErrorHandler).Pointer()).Name() assert.Equal(t, funcName1, funcName2) } -func TestBundle_LoadMessages(t *testing.T) { - b := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, - - defaultLang: language.Und, - defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, - } - - fs := fstest.MapFS{ - "non_readable": { - Mode: fs.ModeDir, - }, - "foo.en.yaml": { - Data: []byte("foo: bar"), - }, - } - - require.Error(t, b.LoadMessages(fs, "file_not_exists.yaml", language.English)) - assert.Nil(t, b.dictionaries[language.English]) - - require.Error(t, b.LoadMessages(fs, "non_readable", language.English)) - assert.Nil(t, b.dictionaries[language.English]) - - require.NoError(t, b.LoadMessages(fs, "foo.en.yaml", language.English)) - assert.NotNil(t, b.dictionaries[language.English]) - - require.Error(t, b.LoadMessages(fs, "foo.en.yaml", language.English), "error when lang already loaded") - assert.NotNil(t, b.dictionaries[language.English]) -} - -func TestBundle_LoadDir(t *testing.T) { - b := &bundle{ - fallbacks: map[language.Tag]language.Tag{}, - translators: map[language.Tag]Translator{}, - dictionaries: map[language.Tag]Dictionary{}, - - defaultLang: language.Und, - defaultErrorHandler: func(_ error, _ string, _ map[string]any) {}, - } - - require.NoError(t, b.LoadDir(fstest.MapFS{"dir.en": {Mode: fs.ModeDir}}), "no error on empty fs") - assert.Nil(t, b.dictionaries[language.English], "does not loads dirs") - - require.NoError(t, b.LoadDir(fstest.MapFS{"messages.en.toml": {Data: []byte("foo=bar")}}), "no error on non yaml files") - assert.Nil(t, b.dictionaries[language.English], "but does not loads them") - - require.Error(t, b.LoadDir(fstest.MapFS{".yaml": {Data: []byte("foo: bar")}}), "error on invalid filename") - require.Error(t, b.LoadDir(fstest.MapFS{"messages.FOO.yaml": {Data: []byte("foo: bar")}}), "error on invalid lang in filename") - - require.NoError(t, b.LoadDir(fstest.MapFS{ - "messages.en.yaml": {Data: []byte("foo: bar")}, - "messages.es.yaml": {Data: []byte("foo: bar")}, - "messages.ru.yml": {Data: []byte("foo: bar")}, - }), "no error on normal yaml files") - assert.NotNil(t, b.dictionaries[language.English]) - assert.NotNil(t, b.dictionaries[language.Spanish]) - assert.NotNil(t, b.dictionaries[language.Russian]) -} - func TestBundle_Translator(t *testing.T) { - b, _ := NewBundle() + provider := new(MockedProvider) + b, err := NewBundle(WithProvider(provider)) + require.NoError(t, err) assert.NotNil(t, b.Translator("ru"), "even empty bundle returns some translator") + + provider.On("Get", language.Russian, "msg_id").Return("", errors.New("no message")) // call from actual translator + provider.On("Get", language.Und, "msg_id").Return("", errors.New("no message")) // call from fallback translator assert.Equal(t, "msg_id", b.Translator("ru").Trans("msg_id"), "even empty bundle returns some translator") - b, _ = NewBundle( + b, err = NewBundle( WithDefaulLangFallback(language.English), WithLangFallback(language.Portuguese, language.Spanish), + WithYamlProvider(fstest.MapFS{ + "messages.en.yaml": {Data: []byte("foo: en\nbar_id: enbar")}, + "messages.es.yaml": {Data: []byte("foo: es")}, + }), ) - - require.NoError(t, b.LoadDir(fstest.MapFS{ - "messages.en.yaml": {Data: []byte("foo: en\nbar_id: enbar")}, - "messages.es.yaml": {Data: []byte("foo: es")}, - })) + require.NoError(t, err) assert.Equal(t, "en", b.Translator("en").Trans("foo"), "en loaded from file") assert.Equal(t, "en", b.Translator("pl").Trans("foo"), "fallback to defalt lang") diff --git a/mf/dictionary.go b/mf/dictionary.go index be18640..c3b97ce 100644 --- a/mf/dictionary.go +++ b/mf/dictionary.go @@ -10,14 +10,14 @@ type Dictionary interface { Get(path string) (string, error) } -type dummyDictionary struct{} +type DummyDictionary struct{} -func (*dummyDictionary) Get(id string) (string, error) { +func (*DummyDictionary) Get(id string) (string, error) { return "", fmt.Errorf("no message with id %s", id) } -func NewDictionary(yaml []byte) (Dictionary, error) { - d := &dictionary{ +func NewYamlDictionary(yaml []byte) (*YamlDictionary, error) { + d := &YamlDictionary{ flatMap: map[string]string{}, } @@ -33,11 +33,11 @@ func NewDictionary(yaml []byte) (Dictionary, error) { return d, nil } -type dictionary struct { +type YamlDictionary struct { flatMap map[string]string } -func (d *dictionary) Get(id string) (string, error) { +func (d *YamlDictionary) Get(id string) (string, error) { msg, ok := d.flatMap[id] if !ok { return "", fmt.Errorf("no message with id %s", id) @@ -46,7 +46,7 @@ func (d *dictionary) Get(id string) (string, error) { return msg, nil } -func (d *dictionary) buildFlatMap(prefix string, yn *y3.Node) { +func (d *YamlDictionary) buildFlatMap(prefix string, yn *y3.Node) { for i := 0; i < len(yn.Content); i++ { n := yn.Content[i] diff --git a/mf/dictionary_test.go b/mf/dictionary_test.go index 773b7e2..7afb012 100644 --- a/mf/dictionary_test.go +++ b/mf/dictionary_test.go @@ -8,7 +8,7 @@ import ( ) func TestDictionaryGet(t *testing.T) { - d, err := NewDictionary([]byte(` + d, err := NewYamlDictionary([]byte(` one: two: msg1-2 foo: bar @@ -100,7 +100,7 @@ foo: } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d, err := NewDictionary(tt.yaml) + d, err := NewYamlDictionary(tt.yaml) if (err != nil) != tt.wantErr { t.Errorf("NewDictionary() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/mf/provider.go b/mf/provider.go new file mode 100644 index 0000000..1f5a3f8 --- /dev/null +++ b/mf/provider.go @@ -0,0 +1,87 @@ +package mf + +import ( + "fmt" + "io" + "io/fs" + "path" + "strings" + + "github.com/pkg/errors" + "golang.org/x/text/language" +) + +type MessageProvider interface { + Get(lang language.Tag, id string) (string, error) +} + +type YamlMessageProvider struct { + dictionaries map[language.Tag]*YamlDictionary +} + +func (p *YamlMessageProvider) Get(lang language.Tag, path string) (string, error) { + d, hasDictionary := p.dictionaries[lang] + if !hasDictionary { + return "", errors.Errorf("no dictionary for lang %s", lang) + } + + return d.Get(path) +} + +func NewYamlMessageProvider(dir fs.FS) (*YamlMessageProvider, error) { + provider := YamlMessageProvider{ + dictionaries: map[language.Tag]*YamlDictionary{}, + } + + err := fs.WalkDir(dir, ".", func(p string, f fs.DirEntry, err error) error { + if err != nil { + return err + } + + if f.IsDir() { + return nil + } + + if path.Ext(f.Name()) != ".yaml" && path.Ext(f.Name()) != ".yml" { + return nil + } + + nameParts := strings.Split(f.Name(), ".") + if len(nameParts) < 2 { + return fmt.Errorf("no lang in file %s", f.Name()) + } + + tag, err := language.Parse(nameParts[len(nameParts)-2]) + if err != nil { + return errors.Wrap(err, "unable to parse language from filename") + } + + return provider.loadMessages(dir, p, tag) + }) + + return &provider, err +} + +func (p *YamlMessageProvider) loadMessages(rd fs.FS, path string, lang language.Tag) error { + yamlFile, err := rd.Open(path) + if err != nil { + return errors.Wrap(err, "unable to open file") + } + + yamlData, err := io.ReadAll(yamlFile) + if err != nil { + return errors.Wrap(err, "unable to read file") + } + + _, hasDictionary := p.dictionaries[lang] + if hasDictionary { + return fmt.Errorf("unable to load %s: language %s already has messages loaded", path, lang) + } + + p.dictionaries[lang], err = NewYamlDictionary(yamlData) + if err != nil { + return errors.Wrap(err, "unable to create dictionary") + } + + return nil +} diff --git a/mf/provider_test.go b/mf/provider_test.go new file mode 100644 index 0000000..171a14b --- /dev/null +++ b/mf/provider_test.go @@ -0,0 +1,64 @@ +package mf + +import ( + "io/fs" + "testing" + "testing/fstest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" +) + +func TestNewFSProvider(t *testing.T) { + p, err := NewYamlMessageProvider(fstest.MapFS{"dir.en": {Mode: fs.ModeDir}}) + require.NoError(t, err, "no error on empty fs") + assert.Empty(t, p.dictionaries, "does not loads dirs") + + p, err = NewYamlMessageProvider(fstest.MapFS{"messages.en.toml": {Data: []byte("foo=bar")}}) + require.NoError(t, err, "no error on non yaml files") + assert.Empty(t, p.dictionaries, "but does not loads them") + + _, err = NewYamlMessageProvider(fstest.MapFS{".yaml": {Data: []byte("foo: bar")}}) + require.Error(t, err, "error on invalid filename") + + _, err = NewYamlMessageProvider(fstest.MapFS{"messages.FOO.yaml": {Data: []byte("foo: bar")}}) + require.Error(t, err, "error on invalid lang in filename") + + p, err = NewYamlMessageProvider(fstest.MapFS{ + "messages.en.yaml": {Data: []byte("foo: bar")}, + "messages.es.yaml": {Data: []byte("foo: bar")}, + "messages.ru.yml": {Data: []byte("foo: bar")}, + }) + require.NoError(t, err, "no error on normal yaml files") + assert.NotEmpty(t, p.dictionaries, "has dictionaries") + assert.NotNil(t, p.dictionaries[language.English]) + assert.NotNil(t, p.dictionaries[language.Spanish]) + assert.NotNil(t, p.dictionaries[language.Russian]) +} + +func TestFSProvider_loadMessages(t *testing.T) { + p := &YamlMessageProvider{ + dictionaries: map[language.Tag]*YamlDictionary{}, + } + fs := fstest.MapFS{ + "non_readable": { + Mode: fs.ModeDir, + }, + "foo.en.yaml": { + Data: []byte("foo: bar"), + }, + } + + require.Error(t, p.loadMessages(fs, "file_not_exists.yaml", language.English)) + assert.Nil(t, p.dictionaries[language.English]) + + require.Error(t, p.loadMessages(fs, "non_readable", language.English)) + assert.Nil(t, p.dictionaries[language.English]) + + require.NoError(t, p.loadMessages(fs, "foo.en.yaml", language.English)) + assert.NotNil(t, p.dictionaries[language.English]) + + require.Error(t, p.loadMessages(fs, "foo.en.yaml", language.English), "error when lang already loaded") + assert.NotNil(t, p.dictionaries[language.English]) +} diff --git a/mf/translator.go b/mf/translator.go index 4c02002..07f8f9f 100644 --- a/mf/translator.go +++ b/mf/translator.go @@ -15,14 +15,14 @@ type Translator interface { } type translator struct { - dictionary Dictionary + provider MessageProvider fallback Translator errorHandler ErrorHandler lang language.Tag } func (tr *translator) Trans(id string, args ...TranslationArg) string { - yaml, err := tr.dictionary.Get(id) + yaml, err := tr.provider.Get(tr.lang, id) if err != nil { if tr.fallback != nil { return tr.fallback.Trans(id, args...) diff --git a/mf/translator_test.go b/mf/translator_test.go index a7c4c53..2422718 100644 --- a/mf/translator_test.go +++ b/mf/translator_test.go @@ -469,13 +469,15 @@ func Test_translator_Trans(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - dictionary := new(MockedDictionary) - dictionary.On("Get", "msg_id").Return(tt.msg, nil) + provider := new(MockedProvider) + provider.On("Get", tt.lang, "msg_id").Return(tt.msg, nil) + + t.Log(len(provider.ExpectedCalls)) var testErr error tr := &translator{ - dictionary: dictionary, + provider: provider, errorHandler: func(err error, _ string, _ map[string]any) { t.Log(err.Error()) testErr = err @@ -495,6 +497,16 @@ func Test_translator_Trans(t *testing.T) { } } +type MockedProvider struct { + mock.Mock +} + +func (m *MockedProvider) Get(lang language.Tag, path string) (string, error) { + args := m.Called(lang, path) + + return args.String(0), args.Error(1) +} + type MockedDictionary struct { mock.Mock }