Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

Support dynamic locales #141

Merged
merged 1 commit into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ ifneq ($(HAS_SERVER),)
cd server && env GOOS=linux GOARCH=amd64 $(GO) build -o dist/plugin-linux-amd64;
cd server && env GOOS=darwin GOARCH=amd64 $(GO) build -o dist/plugin-darwin-amd64;
cd server && env GOOS=windows GOARCH=amd64 $(GO) build -o dist/plugin-windows-amd64.exe;
cd server && cp -a i18n dist/i18n
endif

## Ensures NPM dependencies are installed without having to run this all the time.
Expand Down
File renamed without changes.
1 change: 0 additions & 1 deletion server/activate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func (p *Plugin) OnActivate() error {
p.router = p.InitAPI()
p.ensureBotExists()
p.emptyTime = time.Time{}.AddDate(1, 1, 1)
p.supportedLocales = []string{"en"}

for _, team := range teams {
if err := p.registerCommand(team.Id); err != nil {
Expand Down
35 changes: 19 additions & 16 deletions server/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,45 @@
package main

import (
"fmt"
"io/ioutil"
"path"
"path/filepath"
"strings"

"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils/fileutils"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/pkg/errors"
)

var locales = make(map[string]string)

func (p *Plugin) TranslationsPreInit() error {

i18nDirectory, found := fileutils.FindDir(*p.ServerConfig.PluginSettings.Directory + "/" + manifest.Id + "/server/dist/i18n/")
if !found {
return fmt.Errorf("unable to find i18n directory")
bundlePath, err := p.API.GetBundlePath()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome!

if err != nil {
return errors.Wrap(err, "unable to find i18n directory")
}

files, _ := ioutil.ReadDir(i18nDirectory)
i18nDirectory := path.Join(bundlePath, "assets", "i18n")
files, err := ioutil.ReadDir(i18nDirectory)
if err != nil {
return errors.Wrap(err, "unable to read i18n directory")
}
for _, f := range files {
if filepath.Ext(f.Name()) == ".json" {
filename := f.Name()
locales[strings.Split(filename, ".")[0]] = filepath.Join(i18nDirectory, filename)

filename := f.Name()
if filepath.Ext(filename) == ".json" {
if err := i18n.LoadTranslationFile(filepath.Join(i18nDirectory, filename)); err != nil {
return err
p.API.LogError("Failed to load translation file", "filename", filename, "err", err.Error())
continue
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better continuing here and logging, nice.

}

p.API.LogDebug("Loaded translation file", "filename", filename)
p.locales[strings.TrimSuffix(filename, filepath.Ext(filename))] = filepath.Join(i18nDirectory, filename)
}
}

return nil
}

func GetUserTranslations(locale string) i18n.TranslateFunc {
if _, ok := locales[locale]; !ok {
func (p *Plugin) GetUserTranslations(locale string) i18n.TranslateFunc {
if _, ok := p.locales[locale]; !ok {
locale = model.DEFAULT_LOCALE
}

Expand Down
110 changes: 110 additions & 0 deletions server/i18n_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/mattermost/mattermost-server/plugin/plugintest"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestTranslationsPreInit(t *testing.T) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for adding test coverage on this. I learned some things from your style.

tmpDir, err := ioutil.TempDir("", "TestTranslationsPreInit")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)

assetsPath := filepath.Join(tmpDir, "assets")
err = os.Mkdir(assetsPath, 0777)
require.NoError(t, err)

i18nPath := filepath.Join(tmpDir, "assets", "i18n")

t.Run("failure to get bundle path", func(t *testing.T) {
api := &plugintest.API{}
api.On("GetBundlePath", mock.Anything).Return(tmpDir, nil)

defer api.AssertExpectations(t)

p := &Plugin{}
p.API = api
err := p.TranslationsPreInit()
require.EqualError(t, err, fmt.Sprintf("unable to read i18n directory: open %s: no such file or directory", i18nPath))
})

t.Run("failure to read i18n directory", func(t *testing.T) {
api := &plugintest.API{}
api.On("GetBundlePath", mock.Anything).Return(tmpDir, nil)

defer api.AssertExpectations(t)

file, err := os.Create(i18nPath)
require.NoError(t, err)
file.Close()
defer os.Remove(file.Name())

p := &Plugin{}
p.API = api
err = p.TranslationsPreInit()
require.EqualError(t, err, fmt.Sprintf("unable to read i18n directory: readdirent: invalid argument"))
})

t.Run("no i18n files", func(t *testing.T) {
api := &plugintest.API{}
api.On("GetBundlePath", mock.Anything).Return(tmpDir, nil)

defer api.AssertExpectations(t)

err := os.Mkdir(i18nPath, 0777)
require.NoError(t, err)
defer os.Remove(i18nPath)

p := &Plugin{}
p.API = api
err = p.TranslationsPreInit()
require.NoError(t, err)
})

t.Run("various i18n files", func(t *testing.T) {
api := &plugintest.API{}
api.On("GetBundlePath", mock.Anything).Return(tmpDir, nil)
api.On("LogError", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Run(func(args mock.Arguments) {
t.Helper()
t.Log(args...)
})
api.On("LogDebug", mock.Anything, mock.Anything, mock.Anything).Maybe().Run(func(args mock.Arguments) {
t.Helper()
t.Log(args...)
})

defer api.AssertExpectations(t)

err := os.Mkdir(i18nPath, 0777)
require.NoError(t, err)

err = ioutil.WriteFile(filepath.Join(i18nPath, "not-i18n.txt"), []byte{}, 0777)
require.NoError(t, err)

err = ioutil.WriteFile(filepath.Join(i18nPath, "invalid.json"), []byte{}, 0777)
require.NoError(t, err)

err = ioutil.WriteFile(filepath.Join(i18nPath, "en.json"), []byte(`[{"id":"id","translation":"translation"}]`), 0777)
require.NoError(t, err)

err = ioutil.WriteFile(filepath.Join(i18nPath, "es.json"), []byte(`[{"id":"id","translation":"translation2"}]`), 0777)
require.NoError(t, err)

p := NewPlugin()
p.API = api
err = p.TranslationsPreInit()
require.NoError(t, err)

require.Equal(t, map[string]string{
"en": filepath.Join(i18nPath, "en.json"),
"es": filepath.Join(i18nPath, "es.json"),
}, p.locales)
})
}
8 changes: 5 additions & 3 deletions server/plugin.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package main

import (
"github.com/gorilla/mux"
"io/ioutil"
"time"

"github.com/gorilla/mux"

"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin"
)
Expand All @@ -26,13 +27,14 @@ type Plugin struct {

defaultTime time.Time

supportedLocales []string

readFile func(path string) ([]byte, error)

locales map[string]string
}

func NewPlugin() *Plugin {
return &Plugin{
readFile: ioutil.ReadFile,
locales: make(map[string]string),
}
}
4 changes: 2 additions & 2 deletions server/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (

func (p *Plugin) translation(user *model.User) (i18n.TranslateFunc, string) {
locale := "en"
for _, l := range p.supportedLocales {
for l := range p.locales {
if user.Locale == l {
locale = user.Locale
break
}
}
return GetUserTranslations(locale), locale
return p.GetUserTranslations(locale), locale
}

func (p *Plugin) location(user *model.User) *time.Location {
Expand Down