mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 00:37:04 -06:00
I18n: Add default locale server config option (#51035)
* I18n: Set default locale in server config and expose in grafanaBootData * put default locale behind feature flag * update tests now that default locale is behind feature flag * little bit of PR feedback * update sample.ini
This commit is contained in:
parent
370d6a6f7b
commit
dcf786f3a9
@ -376,6 +376,9 @@ password_hint = password
|
||||
# Default UI theme ("dark" or "light")
|
||||
default_theme = dark
|
||||
|
||||
# Default locale (supported IETF language tag, such as en-US)
|
||||
default_locale = en-US
|
||||
|
||||
# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash.
|
||||
home_page =
|
||||
|
||||
|
@ -376,8 +376,11 @@
|
||||
# Default UI theme ("dark" or "light")
|
||||
;default_theme = dark
|
||||
|
||||
# Default locale (supported IETF language tag, such as en-US)
|
||||
;default_locale = en-US
|
||||
|
||||
# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash.
|
||||
; home_page =
|
||||
;home_page =
|
||||
|
||||
# External user management, these options affect the organization users view
|
||||
;external_manage_link_url =
|
||||
|
@ -691,12 +691,16 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read locale from accept-language
|
||||
acceptLang := c.Req.Header.Get("Accept-Language")
|
||||
// Set locale to the preference, otherwise fall back to the accept language header.
|
||||
// In practice, because the preference has configuration-backed default, the header
|
||||
// shouldn't frequently be used
|
||||
acceptLangHeader := c.Req.Header.Get("Accept-Language")
|
||||
locale := "en-US"
|
||||
|
||||
if len(acceptLang) > 0 {
|
||||
parts := strings.Split(acceptLang, ",")
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagInternationalization) && prefs.JSONData.Locale != "" {
|
||||
locale = prefs.JSONData.Locale
|
||||
} else if len(acceptLangHeader) > 0 {
|
||||
parts := strings.Split(acceptLangHeader, ",")
|
||||
locale = parts[0]
|
||||
}
|
||||
|
||||
|
@ -5,22 +5,25 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
store store
|
||||
cfg *setting.Cfg
|
||||
store store
|
||||
cfg *setting.Cfg
|
||||
features *featuremgmt.FeatureManager
|
||||
}
|
||||
|
||||
func ProvideService(db db.DB, cfg *setting.Cfg) pref.Service {
|
||||
func ProvideService(db db.DB, cfg *setting.Cfg, features *featuremgmt.FeatureManager) pref.Service {
|
||||
return &Service{
|
||||
store: &sqlStore{
|
||||
db: db,
|
||||
},
|
||||
cfg: cfg,
|
||||
cfg: cfg,
|
||||
features: features,
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +54,17 @@ func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreference
|
||||
res.HomeDashboardID = p.HomeDashboardID
|
||||
}
|
||||
if p.JSONData != nil {
|
||||
res.JSONData = p.JSONData
|
||||
if p.JSONData.Locale != "" {
|
||||
res.JSONData.Locale = p.JSONData.Locale
|
||||
}
|
||||
|
||||
if len(p.JSONData.Navbar.SavedItems) > 0 {
|
||||
res.JSONData.Navbar = p.JSONData.Navbar
|
||||
}
|
||||
|
||||
if p.JSONData.QueryHistory.HomeTab != "" {
|
||||
res.JSONData.QueryHistory.HomeTab = p.JSONData.QueryHistory.HomeTab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,5 +230,9 @@ func (s *Service) GetDefaults() *pref.Preference {
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagInternationalization) {
|
||||
defaults.JSONData.Locale = s.cfg.DefaultLocale
|
||||
}
|
||||
|
||||
return defaults
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestGet_empty(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
preference, err := prefService.Get(context.Background(), &pref.GetPreferenceQuery{})
|
||||
require.NoError(t, err)
|
||||
@ -27,9 +29,11 @@ func TestGet_empty(t *testing.T) {
|
||||
|
||||
func TestGetDefaults(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
prefService.cfg.DefaultLocale = "en-US"
|
||||
prefService.cfg.DefaultTheme = "light"
|
||||
prefService.cfg.DateFormats.DefaultTimezone = "UTC"
|
||||
|
||||
@ -62,11 +66,40 @@ func TestGetDefaults(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetDefaultsWithI18nFeatureFlag(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagInternationalization),
|
||||
}
|
||||
prefService.cfg.DefaultLocale = "en-US"
|
||||
prefService.cfg.DefaultTheme = "light"
|
||||
prefService.cfg.DateFormats.DefaultTimezone = "UTC"
|
||||
|
||||
t.Run("GetDefaults", func(t *testing.T) {
|
||||
preference := prefService.GetDefaults()
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "UTC",
|
||||
HomeDashboardID: 0,
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-US",
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preference); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
prefService.cfg.DefaultLocale = "en-US"
|
||||
|
||||
insertPrefs(t, prefService.store,
|
||||
pref.Preference{
|
||||
OrgID: 1,
|
||||
@ -74,6 +107,9 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
|
||||
Theme: "dark",
|
||||
Timezone: "UTC",
|
||||
WeekStart: "1",
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-GB",
|
||||
},
|
||||
},
|
||||
pref.Preference{
|
||||
OrgID: 1,
|
||||
@ -82,6 +118,9 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-AU",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@ -94,7 +133,9 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
HomeDashboardID: 4,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-AU",
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preference); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
@ -111,7 +152,9 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
|
||||
Timezone: "UTC",
|
||||
WeekStart: "1",
|
||||
HomeDashboardID: 1,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-GB",
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preference); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
@ -158,6 +201,10 @@ func TestGetDefaults_JSONData(t *testing.T) {
|
||||
orgPreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: orgNavbarPreferences,
|
||||
}
|
||||
orgPreferencesWithLocaleJsonData := pref.PreferenceJSONData{
|
||||
Navbar: orgNavbarPreferences,
|
||||
Locale: "en-GB",
|
||||
}
|
||||
team2PreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: team2NavbarPreferences,
|
||||
}
|
||||
@ -167,8 +214,9 @@ func TestGetDefaults_JSONData(t *testing.T) {
|
||||
|
||||
t.Run("users have precedence over org", func(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
insertPrefs(t, prefService.store,
|
||||
@ -191,10 +239,42 @@ func TestGetDefaults_JSONData(t *testing.T) {
|
||||
}, preference)
|
||||
})
|
||||
|
||||
t.Run("user JSONData with missing locale does not override org preference", func(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
insertPrefs(t, prefService.store,
|
||||
pref.Preference{
|
||||
OrgID: 1,
|
||||
JSONData: &orgPreferencesWithLocaleJsonData,
|
||||
},
|
||||
pref.Preference{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
JSONData: &userPreferencesJsonData,
|
||||
},
|
||||
)
|
||||
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 1}
|
||||
preference, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &pref.Preference{
|
||||
JSONData: &pref.PreferenceJSONData{
|
||||
Locale: "en-GB",
|
||||
Navbar: userNavbarPreferences,
|
||||
QueryHistory: queryPreference,
|
||||
},
|
||||
}, preference)
|
||||
})
|
||||
|
||||
t.Run("teams have precedence over org and are read in ascending order", func(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
insertPrefs(t, prefService.store,
|
||||
@ -227,8 +307,9 @@ func TestGetDefaults_JSONData(t *testing.T) {
|
||||
|
||||
func TestGetWithDefaults_teams(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
insertPrefs(t, prefService.store,
|
||||
pref.Preference{
|
||||
@ -273,8 +354,9 @@ func TestGetWithDefaults_teams(t *testing.T) {
|
||||
|
||||
func TestPatch_toCreate(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
themeValue := "light"
|
||||
@ -293,8 +375,9 @@ func TestPatch_toCreate(t *testing.T) {
|
||||
|
||||
func TestSave(t *testing.T) {
|
||||
prefService := &Service{
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
store: newFake(),
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
t.Run("insert", func(t *testing.T) {
|
||||
|
@ -397,8 +397,9 @@ type Cfg struct {
|
||||
|
||||
Quota QuotaSettings
|
||||
|
||||
DefaultTheme string
|
||||
HomePage string
|
||||
DefaultTheme string
|
||||
DefaultLocale string
|
||||
HomePage string
|
||||
|
||||
AutoAssignOrg bool
|
||||
AutoAssignOrgId int
|
||||
@ -1356,6 +1357,7 @@ func readUserSettings(iniFile *ini.File, cfg *Cfg) error {
|
||||
LoginHint = valueAsString(users, "login_hint", "")
|
||||
PasswordHint = valueAsString(users, "password_hint", "")
|
||||
cfg.DefaultTheme = valueAsString(users, "default_theme", "")
|
||||
cfg.DefaultLocale = valueAsString(users, "default_locale", "")
|
||||
cfg.HomePage = valueAsString(users, "home_page", "")
|
||||
ExternalUserMngLinkUrl = valueAsString(users, "external_manage_link_url", "")
|
||||
ExternalUserMngLinkName = valueAsString(users, "external_manage_link_name", "")
|
||||
|
Loading…
Reference in New Issue
Block a user