diff --git a/client/startup/i18n.js b/client/startup/i18n.js new file mode 100644 index 000000000000..ee61abe42f4c --- /dev/null +++ b/client/startup/i18n.js @@ -0,0 +1,84 @@ +/* globals isRtl */ + +import moment from 'moment'; + +const currentLanguage = new ReactiveVar(); + +Meteor.startup(() => { + TAPi18n.conf.i18n_files_route = Meteor._relativeToSiteRootUrl('/tap-i18n'); + currentLanguage.set(localStorage.getItem('userLanguage')); + + const availableLanguages = TAPi18n.getLanguages(); + + const filterLanguage = language => { + // Fix browsers having all-lowercase language settings eg. pt-br, en-us + const regex = /([a-z]{2})-([a-z]{2})/; + const matches = regex.exec(language); + if (matches) { + return `${ matches[1] }-${ matches[2].toUpperCase() }`; + } + + return language; + }; + + const getBrowserLanguage = () => filterLanguage(window.navigator.userLanguage || window.navigator.language); + + const loadMomentLocale = language => new Promise((resolve, reject) => { + if (moment.locales().includes(language.toLowerCase())) { + resolve(language); + return; + } + + Meteor.call('loadLocale', language, (error, localeSrc) => { + if (error) { + reject(error); + return; + } + + Function(localeSrc).call({ moment }); + resolve(language); + }); + }); + + const applyLanguage = (language = 'en') => { + language = filterLanguage(language); + + if (!availableLanguages[language]) { + language = language.split('-').shift(); + } + + if (!language) { + return; + } + + document.documentElement.classList[isRtl(language) ? 'add' : 'remove']('rtl'); + TAPi18n.setLanguage(language); + loadMomentLocale(language).then(locale => moment.locale(locale), error => console.error(error)); + }; + + const setLanguage = language => { + currentLanguage.set(filterLanguage(language)); + localStorage.setItem('userLanguage', currentLanguage.get()); + }; + + window.setLanguage = setLanguage; + + window.defaultUserLanguage = () => RocketChat.settings.get('Language') || getBrowserLanguage() || 'en'; + + Tracker.autorun(() => { + const user = RocketChat.models.Users.findOne(Meteor.userId(), { fields: { username: 1 }}); + const userLanguage = user && user.language; + + const defaultLanguage = userLanguage || RocketChat.settings.get('Language') || 'en'; + + if (!currentLanguage.get()) { + setLanguage(defaultLanguage); + } + + if (userLanguage && userLanguage !== currentLanguage.get()) { + setLanguage(userLanguage); + } + + applyLanguage(currentLanguage.get()); + }); +}); diff --git a/client/startup/startup.js b/client/startup/startup.js index 14a24f10b94a..b26d75a32be7 100644 --- a/client/startup/startup.js +++ b/client/startup/startup.js @@ -1,6 +1,5 @@ -/* globals UserPresence, fireGlobalEvent, isRtl */ +/* globals UserPresence, fireGlobalEvent */ -import moment from 'moment'; import toastr from 'toastr'; import hljs from 'highlight.js'; import 'highlight.js/styles/github.css'; @@ -22,57 +21,6 @@ Meteor.startup(function() { window.lastMessageWindow = {}; window.lastMessageWindowHistory = {}; - TAPi18n.conf.i18n_files_route = Meteor._relativeToSiteRootUrl('/tap-i18n'); - - const defaultAppLanguage = () => { - let lng = window.navigator.userLanguage || window.navigator.language || 'en'; - // Fix browsers having all-lowercase language settings eg. pt-br, en-us - const re = /([a-z]{2}-)([a-z]{2})/; - if (re.test(lng)) { - lng = lng.replace(re, (match, ...parts) => { - return parts[0] + parts[1].toUpperCase(); - }); - } - return lng; - }; - - window.defaultUserLanguage = () => RocketChat.settings.get('Language') || defaultAppLanguage(); - - const availableLanguages = TAPi18n.getLanguages(); - const loadedLanguages = []; - - window.setLanguage = function(language) { - if (!language) { - return; - } - - if (loadedLanguages.indexOf(language) > -1) { - return; - } - - loadedLanguages.push(language); - - if (isRtl(language)) { - $('html').addClass('rtl'); - } else { - $('html').removeClass('rtl'); - } - - if (!availableLanguages[language]) { - language = language.split('-').shift(); - } - - TAPi18n.setLanguage(language); - - language = language.toLowerCase(); - if (language !== 'en') { - Meteor.call('loadLocale', language, (err, localeFn) => { - Function(localeFn).call({moment}); - moment.locale(language); - }); - } - }; - Tracker.autorun(function(computation) { if (!Meteor.userId() && !RocketChat.settings.get('Accounts_AllowAnonymousRead')) { return; @@ -114,13 +62,4 @@ Meteor.startup(function() { fireGlobalEvent('status-changed', status); } }); - - Tracker.autorun(() => { - const userLanguage = Meteor.user() && Meteor.user().language || RocketChat.settings.get('Language') || 'en'; - - if (loadedLanguages.length === 0 || localStorage.getItem('userLanguage') !== userLanguage) { - localStorage.setItem('userLanguage', userLanguage); - window.setLanguage(userLanguage); - } - }); }); diff --git a/packages/rocketchat-ui-account/client/accountPreferences.js b/packages/rocketchat-ui-account/client/accountPreferences.js index 23972e48cb92..d5088d5df7f1 100644 --- a/packages/rocketchat-ui-account/client/accountPreferences.js +++ b/packages/rocketchat-ui-account/client/accountPreferences.js @@ -41,11 +41,8 @@ Template.accountPreferences.helpers({ .map(([ key, language ]) => ({ ...language, key: key.toLowerCase() })) .sort((a, b) => a.key - b.key); - const appLanguageKey = RocketChat.settings.get('Language') || 'en'; - const appLanguage = result.filter(({ key }) => key === appLanguageKey.toLowerCase())[0]; - result.unshift({ - 'name': appLanguage ? `Default (${ appLanguage.name })` : 'Default', + 'name': 'Default', 'en': 'Default', 'key': '' }); diff --git a/packages/rocketchat-ui-admin/client/admin.js b/packages/rocketchat-ui-admin/client/admin.js index 6baea91ea436..94163a944370 100644 --- a/packages/rocketchat-ui-admin/client/admin.js +++ b/packages/rocketchat-ui-admin/client/admin.js @@ -413,16 +413,27 @@ Template.admin.events({ 'click .rc-header__section-button .save'() { const group = FlowRouter.getParam('group'); const query = { group, changed: true }; - const settings = TempSettings.find(query, { fields: { _id: 1, value: 1, editor: 1 }}).fetch(); - if (!_.isEmpty(settings)) { - RocketChat.settings.batchSet(settings, function(err) { - if (err) { - return handleError(err); - } - TempSettings.update({ changed: true }, { $unset: { changed: 1 }}); - toastr.success(TAPi18n.__('Settings_updated')); - }); + const settings = TempSettings.find(query, { fields: { _id: 1, value: 1, editor: 1 }}).fetch() || []; + if (settings.length === 0) { + return; } + + RocketChat.settings.batchSet(settings, (err) => { + if (err) { + return handleError(err); + } + + TempSettings.update({ changed: true }, { $unset: { changed: 1 }}); + + if (settings.some(({ _id }) => _id === 'Language')) { + const lng = Meteor.user().language + || settings.filter(({ _id }) => _id === 'Language').shift().value + || 'en'; + return TAPi18n._loadLanguage(lng).then(() => toastr.success(TAPi18n.__('Settings_updated', { lng }))); + } + toastr.success(TAPi18n.__('Settings_updated')); + }); + }, 'click .rc-header__section-button .refresh-clients'() { Meteor.call('refreshClients', function() { diff --git a/packages/rocketchat-ui-login/client/login/footer.html b/packages/rocketchat-ui-login/client/login/footer.html index 69f08dcdacb7..c8016e4ca9ae 100644 --- a/packages/rocketchat-ui-login/client/login/footer.html +++ b/packages/rocketchat-ui-login/client/login/footer.html @@ -1,5 +1,9 @@ diff --git a/packages/rocketchat-ui-login/client/login/footer.js b/packages/rocketchat-ui-login/client/login/footer.js index 5960e6aa6a35..b9bd0282ced2 100644 --- a/packages/rocketchat-ui-login/client/login/footer.js +++ b/packages/rocketchat-ui-login/client/login/footer.js @@ -1,35 +1,37 @@ -/*globals defaultUserLanguage */ -Template.loginFooter.helpers({ - LanguageVersion() { - if (Template.instance().languageVersion.get()) { - return TAPi18n.__('Language_Version', { - lng: Template.instance().languageVersion.get() - }); +Template.loginFooter.onCreated(function() { + this.suggestedLanguage = new ReactiveVar(); + + this.suggestAnotherLanguageFor = language => { + const loadAndSetSuggestedLanguage = language => TAPi18n._loadLanguage(language) + .then(() => this.suggestedLanguage.set(language)); + + const serverLanguage = RocketChat.settings.get('Language'); + + if (serverLanguage !== language) { + loadAndSetSuggestedLanguage(serverLanguage || 'en'); + } else if (!/^en/.test(language)) { + loadAndSetSuggestedLanguage('en'); + } else { + this.suggestedLanguage.set(undefined); } - } + }; + + const currentLanguage = localStorage.getItem('userLanguage'); + this.suggestAnotherLanguageFor(currentLanguage); }); -Template.loginFooter.events({ - 'click button.switch-language'(e, t) { - const userLanguage = t.languageVersion.get(); - localStorage.setItem('userLanguage', userLanguage); - TAPi18n.setLanguage(userLanguage); - moment.locale(userLanguage); - return t.languageVersion.set(userLanguage !== defaultUserLanguage() ? defaultUserLanguage() : 'en'); +Template.loginFooter.helpers({ + languageVersion() { + const lng = Template.instance().suggestedLanguage.get(); + return lng && TAPi18n.__('Language_Version', { lng }); } }); -Template.loginFooter.onCreated(function() { - const self = this; - this.languageVersion = new ReactiveVar; - const userLanguage = localStorage.getItem('userLanguage'); - if (userLanguage !== defaultUserLanguage()) { - return TAPi18n._loadLanguage(defaultUserLanguage()).done(function() { - return self.languageVersion.set(defaultUserLanguage()); - }); - } else if (userLanguage.indexOf('en') !== 0) { - return TAPi18n._loadLanguage('en').done(function() { - return self.languageVersion.set('en'); - }); +Template.loginFooter.events({ + 'click button.js-switch-language'(e, t) { + const language = t.suggestedLanguage.get(); + window.setLanguage(language); + t.suggestAnotherLanguageFor(language); + return false; } }); diff --git a/packages/rocketchat-ui-master/client/main.js b/packages/rocketchat-ui-master/client/main.js index d718b196722d..39209dea6935 100644 --- a/packages/rocketchat-ui-master/client/main.js +++ b/packages/rocketchat-ui-master/client/main.js @@ -1,4 +1,4 @@ -/* globals toolbarSearch, menu, isRtl, fireGlobalEvent, CachedChatSubscription, DynamicCss */ +/* globals toolbarSearch, menu, fireGlobalEvent, CachedChatSubscription, DynamicCss */ import Clipboard from 'clipboard'; import s from 'underscore.string'; @@ -191,7 +191,6 @@ Template.main.events({ }); Template.main.onRendered(function() { - document.body.classList[(isRtl(localStorage.getItem('userLanguage'))? 'add': 'remove')]('rtl'); $('#initial-page-loading').remove(); window.addEventListener('focus', function() { return Meteor.setTimeout(function() {