From b00a60ab71b2bd4640c8608d71805ba9caae97d9 Mon Sep 17 00:00:00 2001 From: enahum Date: Wed, 1 Jun 2016 09:56:13 -0300 Subject: [PATCH] PLT-1800 Load server side locale from the config.json (#3135) * PLT-1800 Load server side locale from the config.json * Add support for locales with country specifics * Fix localization on served locale file as plain/text * Remove github.com/cloudfoundry/jibber_jabber as vendor dependency --- NOTICE.txt | 211 ------------------ api/api.go | 1 - api/apitestlib.go | 4 +- api/user.go | 1 + config/config.json | 7 +- glide.lock | 2 - glide.yaml | 1 - i18n/{pt.json => pt-BR.json} | 0 mattermost.go | 2 +- model/config.go | 48 ++-- model/user.go | 2 - store/sql_store_test.go | 2 +- utils/config.go | 3 +- utils/config_test.go | 2 +- utils/i18n.go | 31 ++- .../cloudfoundry/jibber_jabber/.travis.yml | 11 - .../cloudfoundry/jibber_jabber/LICENSE | 201 ----------------- .../cloudfoundry/jibber_jabber/README.md | 44 ---- .../ci/scripts/windows-64-test.bat | 5 - .../jibber_jabber/jibber_jabber.go | 22 -- .../jibber_jabber/jibber_jabber_suite_test.go | 13 -- .../jibber_jabber/jibber_jabber_unix.go | 57 ----- .../jibber_jabber/jibber_jabber_unix_test.go | 104 --------- .../jibber_jabber/jibber_jabber_windows.go | 114 ---------- .../jibber_jabber_windows_test.go | 51 ----- web/web_test.go | 2 +- webapp/actions/global_actions.jsx | 17 +- .../admin_console/admin_sidebar.jsx | 9 + .../admin_console/localization_settings.jsx | 145 ++++++++++++ .../admin_console/multiselect_settings.jsx | 80 +++++++ webapp/components/root.jsx | 8 +- .../user_settings/user_settings_display.jsx | 11 +- webapp/i18n/i18n.jsx | 40 +++- webapp/i18n/{pt.json => pt-BR.json} | 0 webapp/package.json | 3 +- webapp/root.jsx | 7 +- webapp/sass/styles.scss | 1 + webapp/stores/user_store.jsx | 6 + 38 files changed, 375 insertions(+), 893 deletions(-) rename i18n/{pt.json => pt-BR.json} (100%) delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/LICENSE delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/README.md delete mode 100755 vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go delete mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go create mode 100644 webapp/components/admin_console/localization_settings.jsx create mode 100644 webapp/components/admin_console/multiselect_settings.jsx rename webapp/i18n/{pt.json => pt-BR.json} (100%) diff --git a/NOTICE.txt b/NOTICE.txt index 56a6567d4a..1906ae6b22 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1967,217 +1967,6 @@ THE SOFTWARE. --- -This product contains a modified portion of 'jibber_jabber', a GoLang Library that can be used to detect an operating system's current language. - -* HOMEPAGE: - * https://github.com/cloudfoundry-attic/jibber_jabber - -* LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2014 Pivotal - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - This product contains a modified portion of 'manners', a package imaging providing basic image manipulation functions (resize, rotate, flip, crop, etc.) by Grigory Dryapak. * HOMEPAGE: diff --git a/api/api.go b/api/api.go index e62d34dcc2..63a460dc54 100644 --- a/api/api.go +++ b/api/api.go @@ -10,7 +10,6 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" - _ "github.com/cloudfoundry/jibber_jabber" _ "github.com/nicksnyder/go-i18n/i18n" ) diff --git a/api/apitestlib.go b/api/apitestlib.go index ab342c6b72..a685528d18 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -30,7 +30,7 @@ type TestHelper struct { func SetupEnterprise() *TestHelper { if Srv == nil { utils.LoadConfig("config.json") - utils.InitTranslations() + utils.InitTranslations(utils.Cfg.LocalizationSettings) utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 utils.DisableDebugLogForTest() utils.License.Features.SetDefaults() @@ -50,7 +50,7 @@ func SetupEnterprise() *TestHelper { func Setup() *TestHelper { if Srv == nil { utils.LoadConfig("config.json") - utils.InitTranslations() + utils.InitTranslations(utils.Cfg.LocalizationSettings) utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 utils.DisableDebugLogForTest() NewServer() diff --git a/api/user.go b/api/user.go index caf5734637..4d4518824b 100644 --- a/api/user.go +++ b/api/user.go @@ -239,6 +239,7 @@ func CreateUser(user *model.User) (*model.User, *model.AppError) { } user.MakeNonNil() + user.Locale = *utils.Cfg.LocalizationSettings.DefaultClientLocale if result := <-Srv.Store.User().Save(user); result.Err != nil { l4g.Error(utils.T("api.user.create_user.save.error"), result.Err) diff --git a/config/config.json b/config/config.json index 0c2faa3c55..582a7244ca 100644 --- a/config/config.json +++ b/config/config.json @@ -155,5 +155,10 @@ "Enable": false, "Directory": "./data/", "EnableDaily": false + }, + "LocalizationSettings": { + "DefaultServerLocale": "en", + "DefaultClientLocale": "en", + "AvailableLocales": "en,es,fr,ja,pt-BR" } -} +} \ No newline at end of file diff --git a/glide.lock b/glide.lock index e9450f1bbf..b1bde28473 100644 --- a/glide.lock +++ b/glide.lock @@ -5,8 +5,6 @@ imports: version: e5dc62318d9bd58682f1dceb53a4b24e8253682f - name: github.com/braintree/manners version: 82a8879fc5fd0381fa8b2d8033b19bf255252088 -- name: github.com/cloudfoundry/jibber_jabber - version: bcc4c8345a21301bf47c032ff42dd1aae2fe3027 - name: github.com/dgryski/dgoogauth version: 67642ac6f9144f6610279e37e7be9af13f1cd668 - name: github.com/disintegration/imaging diff --git a/glide.yaml b/glide.yaml index 21ff7062d4..81335ef385 100644 --- a/glide.yaml +++ b/glide.yaml @@ -3,7 +3,6 @@ import: - package: github.com/NYTimes/gziphandler - package: github.com/alecthomas/log4go - package: github.com/braintree/manners -- package: github.com/cloudfoundry/jibber_jabber - package: github.com/dgryski/dgoogauth - package: github.com/disintegration/imaging - package: github.com/go-gorp/gorp diff --git a/i18n/pt.json b/i18n/pt-BR.json similarity index 100% rename from i18n/pt.json rename to i18n/pt-BR.json diff --git a/mattermost.go b/mattermost.go index 9929f59739..bebb55c3bc 100644 --- a/mattermost.go +++ b/mattermost.go @@ -79,7 +79,6 @@ func main() { parseCmds() - utils.InitTranslations() if errstr := doLoadConfig(flagConfigFile); errstr != "" { l4g.Exit(utils.T("mattermost.unable_to_load_config"), errstr) return @@ -88,6 +87,7 @@ func main() { if flagRunCmds { utils.ConfigureCmdLineLog() } + utils.InitTranslations(utils.Cfg.LocalizationSettings) pwd, _ := os.Getwd() l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise) diff --git a/model/config.go b/model/config.go index 674a352f0a..08b00b90f6 100644 --- a/model/config.go +++ b/model/config.go @@ -204,20 +204,27 @@ type ComplianceSettings struct { EnableDaily *bool } +type LocalizationSettings struct { + DefaultServerLocale *string + DefaultClientLocale *string + AvailableLocales *string +} + type Config struct { - ServiceSettings ServiceSettings - TeamSettings TeamSettings - SqlSettings SqlSettings - LogSettings LogSettings - FileSettings FileSettings - EmailSettings EmailSettings - RateLimitSettings RateLimitSettings - PrivacySettings PrivacySettings - SupportSettings SupportSettings - GitLabSettings SSOSettings - GoogleSettings SSOSettings - LdapSettings LdapSettings - ComplianceSettings ComplianceSettings + ServiceSettings ServiceSettings + TeamSettings TeamSettings + SqlSettings SqlSettings + LogSettings LogSettings + FileSettings FileSettings + EmailSettings EmailSettings + RateLimitSettings RateLimitSettings + PrivacySettings PrivacySettings + SupportSettings SupportSettings + GitLabSettings SSOSettings + GoogleSettings SSOSettings + LdapSettings LdapSettings + ComplianceSettings ComplianceSettings + LocalizationSettings LocalizationSettings } func (o *Config) ToJson() string { @@ -513,6 +520,21 @@ func (o *Config) SetDefaults() { o.LdapSettings.NicknameAttribute = new(string) *o.LdapSettings.NicknameAttribute = "" } + + if o.LocalizationSettings.DefaultServerLocale == nil { + o.LocalizationSettings.DefaultServerLocale = new(string) + *o.LocalizationSettings.DefaultServerLocale = DEFAULT_LOCALE + } + + if o.LocalizationSettings.DefaultClientLocale == nil { + o.LocalizationSettings.DefaultClientLocale = new(string) + *o.LocalizationSettings.DefaultClientLocale = DEFAULT_LOCALE + } + + if o.LocalizationSettings.AvailableLocales == nil { + o.LocalizationSettings.AvailableLocales = new(string) + *o.LocalizationSettings.AvailableLocales = *o.LocalizationSettings.DefaultClientLocale + } } func (o *Config) IsValid() *AppError { diff --git a/model/user.go b/model/user.go index b7717c4ffc..ab076894a1 100644 --- a/model/user.go +++ b/model/user.go @@ -136,7 +136,6 @@ func (u *User) PreSave() { u.Username = strings.ToLower(u.Username) u.Email = strings.ToLower(u.Email) - u.Locale = strings.ToLower(u.Locale) u.CreateAt = GetMillis() u.UpdateAt = u.CreateAt @@ -166,7 +165,6 @@ func (u *User) PreSave() { func (u *User) PreUpdate() { u.Username = strings.ToLower(u.Username) u.Email = strings.ToLower(u.Email) - u.Locale = strings.ToLower(u.Locale) u.UpdateAt = GetMillis() if u.AuthData != nil && *u.AuthData == "" { diff --git a/store/sql_store_test.go b/store/sql_store_test.go index 474a68ac7d..19ae2caaed 100644 --- a/store/sql_store_test.go +++ b/store/sql_store_test.go @@ -16,7 +16,7 @@ var store Store func Setup() { if store == nil { utils.LoadConfig("config.json") - utils.InitTranslations() + utils.InitTranslations(utils.Cfg.LocalizationSettings) store = NewSqlStore() store.MarkSystemRanUnitTests() diff --git a/utils/config.go b/utils/config.go index 313b4e29ca..a3969fc40f 100644 --- a/utils/config.go +++ b/utils/config.go @@ -246,7 +246,8 @@ func getClientConfig(c *model.Config) map[string]string { props["WebsocketPort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketPort) props["WebsocketSecurePort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketSecurePort) - props["AllowCorsFrom"] = *c.ServiceSettings.AllowCorsFrom + props["DefaultClientLocale"] = *c.LocalizationSettings.DefaultClientLocale + props["AvailableLocales"] = *c.LocalizationSettings.AvailableLocales if IsLicensed { if *License.Features.CustomBrand { diff --git a/utils/config_test.go b/utils/config_test.go index 6f36b30c39..96ef496964 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -9,5 +9,5 @@ import ( func TestConfig(t *testing.T) { LoadConfig("config.json") - InitTranslations() + InitTranslations(Cfg.LocalizationSettings) } diff --git a/utils/i18n.go b/utils/i18n.go index 2503cd5003..b3e10a8318 100644 --- a/utils/i18n.go +++ b/utils/i18n.go @@ -7,15 +7,16 @@ import ( "strings" l4g "github.com/alecthomas/log4go" - "github.com/cloudfoundry/jibber_jabber" "github.com/mattermost/platform/model" "github.com/nicksnyder/go-i18n/i18n" ) var T i18n.TranslateFunc var locales map[string]string = make(map[string]string) +var settings model.LocalizationSettings -func InitTranslations() { +func InitTranslations(localizationSettings model.LocalizationSettings) { + settings = localizationSettings InitTranslationsWithDir("i18n") } @@ -34,14 +35,10 @@ func InitTranslationsWithDir(dir string) { } func GetTranslationsBySystemLocale() i18n.TranslateFunc { - locale := model.DEFAULT_LOCALE - if userLanguage, err := jibber_jabber.DetectLanguage(); err == nil { - if _, ok := locales[userLanguage]; ok { - locale = userLanguage - } else { - l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE) - locale = model.DEFAULT_LOCALE - } + locale := *settings.DefaultServerLocale + if _, ok := locales[locale]; !ok { + l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE) + locale = model.DEFAULT_LOCALE } if locales[locale] == "" { @@ -72,10 +69,20 @@ func SetTranslations(locale string) i18n.TranslateFunc { } func GetTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) { + // This is for checking against locales like pt_BR or zn_CN + headerLocaleFull := strings.Split(r.Header.Get("Accept-Language"), ",")[0] + // This is for checking agains locales like en, es headerLocale := strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0] - if locales[headerLocale] != "" { + defaultLocale := *settings.DefaultClientLocale + if locales[headerLocaleFull] != "" { + translations := TfuncWithFallback(headerLocaleFull) + return translations, headerLocaleFull + } else if locales[headerLocale] != "" { translations := TfuncWithFallback(headerLocale) return translations, headerLocale + } else if locales[defaultLocale] != "" { + translations := TfuncWithFallback(defaultLocale) + return translations, headerLocale } translations := TfuncWithFallback(model.DEFAULT_LOCALE) @@ -89,7 +96,7 @@ func TfuncWithFallback(pref string) i18n.TranslateFunc { return translated } - t, _ := i18n.Tfunc("en") + t, _ := i18n.Tfunc(model.DEFAULT_LOCALE) return t(translationID, args...) } } diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml b/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml deleted file mode 100644 index b19c2e5353..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go -go: - - 1.2 -before_install: -- go get github.com/onsi/ginkgo/... -- go get github.com/onsi/gomega/... -- go install github.com/onsi/ginkgo/ginkgo -script: PATH=$PATH:$HOME/gopath/bin ginkgo -r . -branches: - only: - - master diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE b/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE deleted file mode 100644 index 915b208920..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2014 Pivotal - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/README.md b/vendor/github.com/cloudfoundry/jibber_jabber/README.md deleted file mode 100644 index d696eb6b6d..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Jibber Jabber [![Build Status](https://travis-ci.org/cloudfoundry/jibber_jabber.svg?branch=master)](https://travis-ci.org/cloudfoundry/jibber_jabber) -Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language. - -### OS Support - -OSX and Linux via the `LC_ALL` and `LANG` environment variables. These are standard variables that are used in ALL versions of UNIX for language detection. - -Windows via [GetUserDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318136.aspx) and [GetSystemDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318122.aspx) system calls. These calls are supported in Windows Vista and up. - -# Usage -Add the following line to your go `import`: - -``` - "github.com/cloudfoundry/jibber_jabber" -``` - -### DetectIETF -`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code. - -``` - userLocale, err := jibber_jabber.DetectIETF() - println("Locale:", userLocale) -``` - -### DetectLanguage -`DetectLanguage` will return the current languge as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code. - -``` - userLanguage, err := jibber_jabber.DetectLanguage() - println("Language:", userLanguage) -``` - -### DetectTerritory -`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code. - -``` - localeTerritory, err := jibber_jabber.DetectTerritory() - println("Territory:", localeTerritory) -``` - -### Errors -All the Detect commands will return an error if they are unable to read the Locale from the system. - -For Windows, additional error information is provided due to the nature of the system call being used. diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat b/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat deleted file mode 100755 index b9a87bf7a9..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat +++ /dev/null @@ -1,5 +0,0 @@ -git fetch -git checkout %GIT_COMMIT% - -SET GOPATH=%CD%\Godeps\_workspace;c:\Users\Administrator\go -c:\Go\bin\go test -v . diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go deleted file mode 100644 index 45d288ea87..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go +++ /dev/null @@ -1,22 +0,0 @@ -package jibber_jabber - -import ( - "strings" -) - -const ( - COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE = "Could not detect Language" -) - -func splitLocale(locale string) (string, string) { - formattedLocale := strings.Split(locale, ".")[0] - formattedLocale = strings.Replace(formattedLocale, "-", "_", -1) - - pieces := strings.Split(formattedLocale, "_") - language := pieces[0] - territory := "" - if len(pieces) > 1 { - territory = strings.Split(formattedLocale, "_")[1] - } - return language, territory -} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go deleted file mode 100644 index 3da19c84bf..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package jibber_jabber_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestJibberJabber(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Jibber Jabber Suite") -} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go deleted file mode 100644 index 374d761763..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build darwin freebsd linux netbsd openbsd - -package jibber_jabber - -import ( - "errors" - "os" - "strings" -) - -func getLangFromEnv() (locale string) { - locale = os.Getenv("LC_ALL") - if locale == "" { - locale = os.Getenv("LANG") - } - return -} - -func getUnixLocale() (unix_locale string, err error) { - unix_locale = getLangFromEnv() - if unix_locale == "" { - err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE) - } - - return -} - -func DetectIETF() (locale string, err error) { - unix_locale, err := getUnixLocale() - if err == nil { - language, territory := splitLocale(unix_locale) - locale = language - if territory != "" { - locale = strings.Join([]string{language, territory}, "-") - } - } - - return -} - -func DetectLanguage() (language string, err error) { - unix_locale, err := getUnixLocale() - if err == nil { - language, _ = splitLocale(unix_locale) - } - - return -} - -func DetectTerritory() (territory string, err error) { - unix_locale, err := getUnixLocale() - if err == nil { - _, territory = splitLocale(unix_locale) - } - - return -} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go deleted file mode 100644 index a5e3074a2e..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// +build darwin freebsd linux netbsd openbsd - -package jibber_jabber_test - -import ( - "os" - - . "github.com/cloudfoundry/jibber_jabber" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Unix", func() { - AfterEach(func() { - os.Setenv("LC_ALL", "") - os.Setenv("LANG", "en_US.UTF-8") - }) - - Describe("#DetectIETF", func() { - Context("Returns IETF encoded locale", func() { - It("should return the locale set to LC_ALL", func() { - os.Setenv("LC_ALL", "fr_FR.UTF-8") - result, _ := DetectIETF() - Ω(result).Should(Equal("fr-FR")) - }) - - It("should return the locale set to LANG if LC_ALL isn't set", func() { - os.Setenv("LANG", "fr_FR.UTF-8") - - result, _ := DetectIETF() - Ω(result).Should(Equal("fr-FR")) - }) - - It("should return an error if it cannot detect a locale", func() { - os.Setenv("LANG", "") - - _, err := DetectIETF() - Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) - }) - }) - - Context("when the locale is simply 'fr'", func() { - BeforeEach(func() { - os.Setenv("LANG", "fr") - }) - - It("should return the locale without a territory", func() { - language, err := DetectIETF() - Ω(err).ShouldNot(HaveOccurred()) - Ω(language).Should(Equal("fr")) - }) - }) - }) - - Describe("#DetectLanguage", func() { - Context("Returns encoded language", func() { - It("should return the language set to LC_ALL", func() { - os.Setenv("LC_ALL", "fr_FR.UTF-8") - result, _ := DetectLanguage() - Ω(result).Should(Equal("fr")) - }) - - It("should return the language set to LANG if LC_ALL isn't set", func() { - os.Setenv("LANG", "fr_FR.UTF-8") - - result, _ := DetectLanguage() - Ω(result).Should(Equal("fr")) - }) - - It("should return an error if it cannot detect a language", func() { - os.Setenv("LANG", "") - - _, err := DetectLanguage() - Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) - }) - }) - }) - - Describe("#DetectTerritory", func() { - Context("Returns encoded territory", func() { - It("should return the territory set to LC_ALL", func() { - os.Setenv("LC_ALL", "fr_FR.UTF-8") - result, _ := DetectTerritory() - Ω(result).Should(Equal("FR")) - }) - - It("should return the territory set to LANG if LC_ALL isn't set", func() { - os.Setenv("LANG", "fr_FR.UTF-8") - - result, _ := DetectTerritory() - Ω(result).Should(Equal("FR")) - }) - - It("should return an error if it cannot detect a territory", func() { - os.Setenv("LANG", "") - - _, err := DetectTerritory() - Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) - }) - }) - }) - -}) diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go deleted file mode 100644 index 1acd96c38b..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go +++ /dev/null @@ -1,114 +0,0 @@ -// +build windows - -package jibber_jabber - -import ( - "errors" - "syscall" - "unsafe" -) - -const LOCALE_NAME_MAX_LENGTH uint32 = 85 - -var SUPPORTED_LOCALES = map[uintptr]string{ - 0x0407: "de-DE", - 0x0409: "en-US", - 0x0c0a: "es-ES", //or is it 0x040a - 0x040c: "fr-FR", - 0x0410: "it-IT", - 0x0411: "ja-JA", - 0x0412: "ko_KR", - 0x0416: "pt-BR", - //0x0419: "ru_RU", - Will add support for Russian when nicksnyder/go-i18n supports Russian - 0x0804: "zh-CN", - 0x0c04: "zh-HK", - 0x0404: "zh-TW", -} - -func getWindowsLocaleFrom(sysCall string) (locale string, err error) { - buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH) - - dll := syscall.MustLoadDLL("kernel32") - proc := dll.MustFindProc(sysCall) - r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH)) - if r == 0 { - err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error()) - return - } - - locale = syscall.UTF16ToString(buffer) - - return -} - -func getAllWindowsLocaleFrom(sysCall string) (string, error) { - dll, err := syscall.LoadDLL("kernel32") - if err != nil { - return "", errors.New("Could not find kernel32 dll") - } - - proc, err := dll.FindProc(sysCall) - if err != nil { - return "", err - } - - locale, _, dllError := proc.Call() - if locale == 0 { - return "", errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error()) - } - - return SUPPORTED_LOCALES[locale], nil -} - -func getWindowsLocale() (locale string, err error) { - dll, err := syscall.LoadDLL("kernel32") - if err != nil { - return "", errors.New("Could not find kernel32 dll") - } - - proc, err := dll.FindProc("GetVersion") - if err != nil { - return "", err - } - - v, _, _ := proc.Call() - windowsVersion := byte(v) - isVistaOrGreater := (windowsVersion >= 6) - - if isVistaOrGreater { - locale, err = getWindowsLocaleFrom("GetUserDefaultLocaleName") - if err != nil { - locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName") - } - } else if !isVistaOrGreater { - locale, err = getAllWindowsLocaleFrom("GetUserDefaultLCID") - if err != nil { - locale, err = getAllWindowsLocaleFrom("GetSystemDefaultLCID") - } - } else { - panic(v) - } - return -} -func DetectIETF() (locale string, err error) { - locale, err = getWindowsLocale() - return -} - -func DetectLanguage() (language string, err error) { - windows_locale, err := getWindowsLocale() - if err == nil { - language, _ = splitLocale(windows_locale) - } - - return -} - -func DetectTerritory() (territory string, err error) { - windows_locale, err := getWindowsLocale() - if err == nil { - _, territory = splitLocale(windows_locale) - } - - return -} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go deleted file mode 100644 index f325d981ef..0000000000 --- a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build windows - -package jibber_jabber_test - -import ( - "regexp" - - . "github.com/cloudfoundry/jibber_jabber" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -const ( - LOCALE_REGEXP = "^[a-z]{2}-[A-Z]{2}$" - LANGUAGE_REGEXP = "^[a-z]{2}$" - TERRITORY_REGEXP = "^[A-Z]{2}$" -) - -var _ = Describe("Windows", func() { - BeforeEach(func() { - locale, err := DetectIETF() - Ω(err).Should(BeNil()) - Ω(locale).ShouldNot(BeNil()) - Ω(locale).ShouldNot(Equal("")) - }) - - Describe("#DetectIETF", func() { - It("detects correct IETF locale", func() { - locale, _ := DetectIETF() - matched, _ := regexp.MatchString(LOCALE_REGEXP, locale) - Ω(matched).Should(BeTrue()) - }) - }) - - Describe("#DetectLanguage", func() { - It("detects correct Language", func() { - language, _ := DetectLanguage() - matched, _ := regexp.MatchString(LANGUAGE_REGEXP, language) - Ω(matched).Should(BeTrue()) - }) - }) - - Describe("#DetectTerritory", func() { - It("detects correct Territory", func() { - territory, _ := DetectTerritory() - matched, _ := regexp.MatchString(TERRITORY_REGEXP, territory) - Ω(matched).Should(BeTrue()) - }) - }) -}) diff --git a/web/web_test.go b/web/web_test.go index d4d9a5f26b..0a9869e401 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -21,7 +21,7 @@ var URL string func Setup() { if api.Srv == nil { utils.LoadConfig("config.json") - utils.InitTranslations() + utils.InitTranslations(utils.Cfg.LocalizationSettings) api.NewServer() api.StartServer() api.InitApi() diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index 91b51a9c22..0b264a9b3c 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -392,8 +392,10 @@ export function newLocalizationSelected(locale) { translations: en }); } else { + const localeInfo = I18n.getLanguageInfo(locale) || I18n.getLanguageInfo(global.window.mm_config.DefaultClientLocale); + Client.getTranslations( - I18n.getLanguageInfo(locale).url, + localeInfo.url, (data, res) => { let translations = data; if (!data && res.text) { @@ -412,16 +414,11 @@ export function newLocalizationSelected(locale) { } } -export function loadBrowserLocale() { - let locale = (navigator.languages && navigator.languages.length > 0 ? navigator.languages[0] : - (navigator.language || navigator.userLanguage)).split('-')[0]; +export function loadDefaultLocale() { + const defaultLocale = global.window.mm_config.DefaultClientLocale; + let locale = global.window.mm_user ? global.window.mm_user.locale || defaultLocale : defaultLocale; - const user = UserStore.getCurrentUser(); - if (user) { - locale = user.locale || locale; - } - - if (!I18n.getLanguages()[locale]) { + if (!I18n.getLanguageInfo(locale)) { locale = 'en'; } return newLocalizationSelected(locale); diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx index cdb7e29d53..9548a77638 100644 --- a/webapp/components/admin_console/admin_sidebar.jsx +++ b/webapp/components/admin_console/admin_sidebar.jsx @@ -292,6 +292,15 @@ export default class AdminSidebar extends React.Component { /> } /> + + } + /> { + return {value: locales[l].value, text: locales[l].name}; + }) + }); + } + + canSave() { + return this.state.availableLocales.join(',').indexOf(this.state.defaultClientLocale) !== -1; + } + + getConfigFromState(config) { + config.LocalizationSettings.DefaultServerLocale = this.state.defaultServerLocale; + config.LocalizationSettings.DefaultClientLocale = this.state.defaultClientLocale; + config.LocalizationSettings.AvailableLocales = this.state.availableLocales.join(','); + + return config; + } + + renderTitle() { + return ( +

+ +

+ ); + } + + renderSettings() { + return ( + + } + > + + } + value={this.state.defaultServerLocale} + onChange={this.handleChange} + helpText={ + + } + /> + + } + value={this.state.defaultClientLocale} + onChange={this.handleChange} + helpText={ + + } + /> + + } + selected={this.state.availableLocales} + mustBePresent={this.state.defaultClientLocale} + onChange={this.handleChange} + helpText={ + + } + noResultText={ + + } + errorText={ + + } + notPresent={ + + } + /> + + ); + } +} \ No newline at end of file diff --git a/webapp/components/admin_console/multiselect_settings.jsx b/webapp/components/admin_console/multiselect_settings.jsx new file mode 100644 index 0000000000..deba983de4 --- /dev/null +++ b/webapp/components/admin_console/multiselect_settings.jsx @@ -0,0 +1,80 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. +import React from 'react'; +import ReactSelect from 'react-select'; + +import Setting from './setting.jsx'; +import FormError from 'components/form_error.jsx'; + +export default class MultiSelectSetting extends React.Component { + constructor(props) { + super(props); + + this.handleChange = this.handleChange.bind(this); + this.state = {error: false}; + } + + handleChange(newValue) { + const values = newValue.map((n) => { + return n.value; + }); + + if (!newValue || newValue.length === 0) { + this.setState({error: this.props.errorText}); + } else if (this.props.mustBePresent && values.join(',').indexOf(this.props.mustBePresent) === -1) { + this.setState({error: this.props.notPresent}); + } else { + this.props.onChange(this.props.id, values); + this.setState({error: false}); + } + } + + componentWillReceiveProps(newProps) { + if (newProps.mustBePresent && newProps.selected.join(',').indexOf(newProps.mustBePresent) === -1) { + this.setState({error: this.props.notPresent}); + } else { + this.setState({error: false}); + } + } + + render() { + return ( + + + + + ); + } +} + +MultiSelectSetting.defaultProps = { + disabled: false +}; + +MultiSelectSetting.propTypes = { + id: React.PropTypes.string.isRequired, + values: React.PropTypes.array.isRequired, + label: React.PropTypes.node.isRequired, + selected: React.PropTypes.array.isRequired, + mustBePresent: React.PropTypes.string, + onChange: React.PropTypes.func.isRequired, + disabled: React.PropTypes.bool, + helpText: React.PropTypes.node, + noResultText: React.PropTypes.node, + errorText: React.PropTypes.node, + notPresent: React.PropTypes.node +}; \ No newline at end of file diff --git a/webapp/components/root.jsx b/webapp/components/root.jsx index c964993923..abaa05bb59 100644 --- a/webapp/components/root.jsx +++ b/webapp/components/root.jsx @@ -6,6 +6,7 @@ import * as GlobalActions from 'actions/global_actions.jsx'; import LocalizationStore from 'stores/localization_store.jsx'; +import Client from 'utils/web_client.jsx'; import {IntlProvider} from 'react-intl'; @@ -41,7 +42,10 @@ export default class Root extends React.Component { FastClick.attach(document.body); } localizationChanged() { - this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()}); + const locale = LocalizationStore.getLocale(); + + Client.setAcceptLanguage(locale); + this.setState({locale, translations: LocalizationStore.getTranslations()}); } redirectIfNecessary(props) { @@ -67,7 +71,7 @@ export default class Root extends React.Component { LocalizationStore.addChangeListener(this.localizationChanged); // Get our localizaiton - GlobalActions.loadBrowserLocale(); + GlobalActions.loadDefaultLocale(); } componentWillUnmount() { LocalizationStore.removeChangeListener(this.localizationChanged); diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx index 16175d4de8..98d4ed7da7 100644 --- a/webapp/components/user_settings/user_settings_display.jsx +++ b/webapp/components/user_settings/user_settings_display.jsx @@ -641,7 +641,11 @@ export default class UserSettingsDisplay extends React.Component { ); } + const userLocale = this.props.user.locale; if (this.props.activeSection === 'languages') { + if (!I18n.isLanguageAvailable(userLocale)) { + this.props.user.locale = global.window.mm_config.DefaultClientLocale; + } languagesSection = ( ); } else { - var locale = I18n.getLanguageInfo(this.props.user.locale).name; + let locale; + if (I18n.isLanguageAvailable(userLocale)) { + locale = I18n.getLanguageInfo(userLocale).name; + } else { + locale = I18n.getLanguageInfo(global.window.mm_config.DefaultClientLocale).name; + } languagesSection = ( { + if (languages[l]) { + availableLanguages[l] = languages[l]; + } + }); +} + +export function getAllLanguages() { return languages; } +export function getLanguages() { + if (!availableLanguages) { + setAvailableLanguages(); + } + return availableLanguages; +} + export function getLanguageInfo(locale) { - return languages[locale]; + if (!availableLanguages) { + setAvailableLanguages(); + } + return availableLanguages[locale]; +} + +export function isLanguageAvailable(locale) { + return !!availableLanguages[locale]; } export function safariFix(callback) { diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt-BR.json similarity index 100% rename from webapp/i18n/pt.json rename to webapp/i18n/pt-BR.json diff --git a/webapp/package.json b/webapp/package.json index 8e2063c847..2f61faeb91 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -27,8 +27,9 @@ "react-bootstrap": "0.29.3", "react-custom-scrollbars": "4.0.0-beta.1", "react-dom": "15.0.2", - "react-intl": "2.0.0-rc-1", + "react-intl": "2.1.2", "react-router": "2.4.0", + "react-select": "1.0.0-beta13", "react-textarea-autosize": "4.0.1", "superagent": "1.8.3", "twemoji": "2.0.5", diff --git a/webapp/root.jsx b/webapp/root.jsx index dc2df64ac1..40cf351de4 100644 --- a/webapp/root.jsx +++ b/webapp/root.jsx @@ -53,6 +53,7 @@ const ActionTypes = Constants.ActionTypes; import AdminConsole from 'components/admin_console/admin_console.jsx'; import SystemAnalytics from 'components/analytics/system_analytics.jsx'; import ConfigurationSettings from 'components/admin_console/configuration_settings.jsx'; +import LocalizationSettings from 'components/admin_console/localization_settings.jsx'; import UsersAndTeamsSettings from 'components/admin_console/users_and_teams_settings.jsx'; import PrivacySettings from 'components/admin_console/privacy_settings.jsx'; import LogSettings from 'components/admin_console/log_settings.jsx'; @@ -142,8 +143,8 @@ function preRenderSetup(callwhendone) { ); function afterIntl() { - I18n.doAddLocaleData(); $.when(d1).done(() => { + I18n.doAddLocaleData(); callwhendone(); }); } @@ -362,6 +363,10 @@ function renderRootComponent() { path='configuration' component={ConfigurationSettings} /> +