Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
MM-12740 Move translations into redux store and remove LocalizationSt…
Browse files Browse the repository at this point in the history
…ore (#1896)

* MM-12581 Move translations into redux store and remove LocalizationStore

* Switch i18n file mocking to not use identity-obj-proxy

* Address feedback
  • Loading branch information
hmhealey committed Oct 17, 2018
1 parent 3e4c1f3 commit 02c26ca
Show file tree
Hide file tree
Showing 39 changed files with 544 additions and 221 deletions.
59 changes: 6 additions & 53 deletions actions/global_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {closeRightHandSide, closeMenu as closeRhsMenu, updateRhsState} from 'act
import {close as closeLhs} from 'actions/views/lhs';
import * as WebsocketActions from 'actions/websocket_actions.jsx';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import {getCurrentLocale} from 'selectors/i18n';
import {getIsRhsOpen, getRhsState} from 'selectors/rhs';
import BrowserStore from 'stores/browser_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
Expand All @@ -44,8 +45,6 @@ import {ActionTypes, Constants, ErrorPageTypes, PostTypes, RHSStates} from 'util
import EventTypes from 'utils/event_types.jsx';
import {filterAndSortTeamsByDisplayName} from 'utils/team_utils.jsx';
import * as Utils from 'utils/utils.jsx';
import en from 'i18n/en.json';
import * as I18n from 'i18n/i18n.jsx';
import {equalServerVersions} from 'utils/server_version';

const dispatch = store.dispatch;
Expand Down Expand Up @@ -367,55 +366,6 @@ export function sendAddToChannelEphemeralPost(user, addedUsername, addedUserId,
dispatch(handleNewPost(post));
}

export function newLocalizationSelected(locale) {
const localeInfo = I18n.getLanguageInfo(locale);

if (locale === 'en' || !localeInfo) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_LOCALE,
locale,
translations: en,
});
} else {
Client4.getTranslations(localeInfo.url).then(
(data, res) => {
let translations = data;
if (!data && res.text) {
translations = JSON.parse(res.text);
}
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_LOCALE,
locale,
translations,
});
}
).catch(
() => { } //eslint-disable-line no-empty-function
);
}
}

export function loadCurrentLocale() {
const user = UserStore.getCurrentUser();

if (user && user.locale) {
newLocalizationSelected(user.locale);
} else {
loadDefaultLocale();
}
}

export function loadDefaultLocale() {
const config = getConfig(getState());
let locale = config.DefaultClientLocale;

if (!I18n.getLanguageInfo(locale)) {
locale = 'en';
}

return newLocalizationSelected(locale);
}

let lastTimeTypingSent = 0;
export function emitLocalUserTypingEvent(channelId, parentPostId) {
const userTyping = async (actionDispatch, actionGetState) => {
Expand Down Expand Up @@ -484,7 +434,10 @@ export function emitBrowserFocus(focus) {
}

export async function redirectUserToDefaultTeam() {
const userId = getCurrentUserId(getState());
const state = getState();
const userId = getCurrentUserId(state);
const locale = getCurrentLocale(state);

const teams = TeamStore.getAll();
const teamMembers = TeamStore.getMyTeamMembers();
let teamId = LocalStorageStore.getPreviousTeamId(userId);
Expand All @@ -503,7 +456,7 @@ export async function redirectUserToDefaultTeam() {
}

if (myTeams.length > 0) {
myTeams = filterAndSortTeamsByDisplayName(myTeams);
myTeams = filterAndSortTeamsByDisplayName(myTeams, locale);
if (myTeams && myTeams[0]) {
teamId = myTeams[0].id;
}
Expand Down
4 changes: 0 additions & 4 deletions actions/user_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';

import {browserHistory} from 'utils/browser_history';
import {getChannelMembersForUserIds} from 'actions/channel_actions.jsx';
import {loadCurrentLocale} from 'actions/global_actions.jsx';
import {loadStatusesForProfilesList, loadStatusesForProfilesMap} from 'actions/status_actions.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
Expand Down Expand Up @@ -498,7 +497,6 @@ export async function resendVerification(email, success, error) {
export async function loginById(userId, password, mfaToken, success, error) {
const {data: ok, error: err} = await UserActions.loginById(userId, password, mfaToken)(dispatch, getState);
if (ok && success) {
loadCurrentLocale();
success();
} else if (err && error) {
if (err.server_error_id === 'api.context.mfa_required.app_error') {
Expand All @@ -523,7 +521,6 @@ export async function createUserWithInvite(user, token, inviteId, success, error
export async function webLogin(loginId, password, token, success, error) {
const {data: ok, error: err} = await UserActions.login(loginId, password, token)(dispatch, getState);
if (ok && success) {
loadCurrentLocale();
success();
} else if (err && error) {
if (err.server_error_id === 'api.context.mfa_required.app_error') {
Expand Down Expand Up @@ -557,7 +554,6 @@ export async function getTermsOfService(success, error) {
export async function webLoginByLdap(loginId, password, token, success, error) {
const {data: ok, error: err} = await UserActions.login(loginId, password, token, true)(dispatch, getState);
if (ok && success) {
loadCurrentLocale();
success();
} else if (err && error) {
if (err.server_error_id === 'api.context.mfa_required.app_error') {
Expand Down
17 changes: 17 additions & 0 deletions actions/views/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

import {getClientConfig, getLicenseConfig} from 'mattermost-redux/actions/general';
import * as UserActions from 'mattermost-redux/actions/users';
import {Client4} from 'mattermost-redux/client';

import {ActionTypes} from 'utils/constants';

export function loadMeAndConfig() {
return (dispatch) => {
Expand All @@ -18,3 +21,17 @@ export function loadMeAndConfig() {
return Promise.all(promises);
};
}

export function loadTranslations(locale, url) {
return (dispatch) => {
Client4.getTranslations(url).then((translations) => {
dispatch({
type: ActionTypes.RECEIVED_TRANSLATIONS,
data: {
locale,
translations,
},
});
}).catch(() => {}); // eslint-disable-line no-empty-function
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import MenuIcon from 'components/svg/menu_icon';

export default class AdminNavbarDropdown extends React.Component {
static propTypes = {
locale: PropTypes.string.isRequired,

/*
* Bool whether the navigation is blocked by unsaved changes
Expand Down Expand Up @@ -76,12 +77,12 @@ export default class AdminNavbarDropdown extends React.Component {
};

render() {
const {teams} = this.props;
const {locale, teams} = this.props;
const teamToRender = []; // Array of team components
let switchTeams;

if (teams && teams.length > 0) {
const teamsArray = filterAndSortTeamsByDisplayName(teams);
const teamsArray = filterAndSortTeamsByDisplayName(teams, locale);

for (const team of teamsArray) {
teamToRender.push(
Expand Down
2 changes: 2 additions & 0 deletions components/admin_console/admin_navbar_dropdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {bindActionCreators} from 'redux';
import {getMyTeams} from 'mattermost-redux/selectors/entities/teams';

import {deferNavigation} from 'actions/admin_actions.jsx';
import {getCurrentLocale} from 'selectors/i18n';
import {getNavigationBlocked} from 'selectors/views/admin';

import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';

function mapStateToProps(state) {
return {
locale: getCurrentLocale(state),
teams: getMyTeams(state),
navigationBlocked: getNavigationBlocked(state),
};
Expand Down
16 changes: 16 additions & 0 deletions components/admin_console/manage_teams_modal/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {connect} from 'react-redux';

import {getCurrentLocale} from 'selectors/i18n';

import ManageTeamsModal from './manage_teams_modal';

function mapStateToProps(state) {
return {
locale: getCurrentLocale(state),
};
}

export default connect(mapStateToProps)(ManageTeamsModal);
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import RemoveFromTeamButton from './remove_from_team_button.jsx';

export default class ManageTeamsModal extends React.Component {
static propTypes = {
locale: PropTypes.string.isRequired,
onModalDismissed: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
user: PropTypes.object,
Expand Down Expand Up @@ -59,7 +60,7 @@ export default class ManageTeamsModal extends React.Component {
loadTeamsAndTeamMembers = (user = this.props.user) => {
TeamActions.getTeamsForUser(user.id, (teams) => {
this.setState({
teams: filterAndSortTeamsByDisplayName(teams),
teams: filterAndSortTeamsByDisplayName(teams, this.props.locale),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {FormattedMessage} from 'react-intl';
import {Constants} from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';
import ManageRolesModal from 'components/admin_console/manage_roles_modal';
import ManageTeamsModal from 'components/admin_console/manage_teams_modal/manage_teams_modal.jsx';
import ManageTeamsModal from 'components/admin_console/manage_teams_modal';
import ManageTokensModal from 'components/admin_console/manage_tokens_modal';
import ResetPasswordModal from 'components/admin_console/reset_password_modal';
import ResetEmailModal from 'components/admin_console/reset_email_modal/reset_email_modal.jsx';
Expand Down
10 changes: 5 additions & 5 deletions components/announcement_bar/announcement_bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import AnalyticsStore from 'stores/analytics_store.jsx';
import ErrorStore from 'stores/error_store.jsx';

import {AnnouncementBarTypes, AnnouncementBarMessages, StatTypes, StoragePrefixes} from 'utils/constants.jsx';
import {displayExpiryDate, isLicenseExpired, isLicenseExpiring, isLicensePastGracePeriod} from 'utils/license_utils.jsx';
import {isLicenseExpired, isLicenseExpiring, isLicensePastGracePeriod} from 'utils/license_utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx';
import {t} from 'utils/i18n';
Expand All @@ -33,7 +33,7 @@ export default class AnnouncementBar extends React.PureComponent {
*/
canViewSystemErrors: PropTypes.bool.isRequired,
canViewAPIv3Banner: PropTypes.bool.isRequired,
licenseId: PropTypes.string,
license: PropTypes.object,
siteURL: PropTypes.string,
sendEmailNotifications: PropTypes.bool.isRequired,
bannerText: PropTypes.string,
Expand Down Expand Up @@ -328,7 +328,7 @@ export default class AnnouncementBar extends React.PureComponent {
);
}

const renewalLink = RENEWAL_LINK + '?id=' + this.props.licenseId + '&user_count=' + this.state.totalUsers;
const renewalLink = RENEWAL_LINK + '?id=' + this.props.license.id + '&user_count=' + this.state.totalUsers;

let message = this.state.message;
if (this.state.type === AnnouncementBarTypes.ANNOUNCEMENT) {
Expand All @@ -348,9 +348,9 @@ export default class AnnouncementBar extends React.PureComponent {
message = (
<FormattedMarkdownMessage
id={AnnouncementBarMessages.LICENSE_EXPIRING}
defaultMessage='Enterprise license expires on {date}. [Prease renew](!{link}).'
defaultMessage='Enterprise license expires on {date, date, long}. [Please renew](!{link}).'
values={{
date: displayExpiryDate(),
date: new Date(parseInt(this.props.license.ExpiresAt, 10)),
link: renewalLink,
}}
/>
Expand Down
3 changes: 1 addition & 2 deletions components/announcement_bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ function mapStateToProps(state) {
const license = getLicense(state);
const config = getConfig(state);
const user = getCurrentUser(state);
const licenseId = license.Id;
const siteURL = config.SiteURL;
const sendEmailNotifications = config.SendEmailNotifications === 'true';
const requireEmailVerification = config.RequireEmailVerification === 'true';
Expand All @@ -33,7 +32,7 @@ function mapStateToProps(state) {
canViewSystemErrors,
user,
canViewAPIv3Banner,
licenseId,
license,
siteURL,
sendEmailNotifications,
requireEmailVerification,
Expand Down
5 changes: 3 additions & 2 deletions components/file_upload/file_upload.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
generateId,
isFileTransfer,
localizeMessage,
sortFilesByName,
} from 'utils/utils.jsx';

import AttachmentIcon from 'components/svg/attachment_icon';
Expand Down Expand Up @@ -84,6 +83,8 @@ export default class FileUpload extends PureComponent {
*/
getTarget: PropTypes.func.isRequired,

locale: PropTypes.string.isRequired,

/**
* Function to be called when file upload input is clicked
*/
Expand Down Expand Up @@ -205,7 +206,7 @@ export default class FileUpload extends PureComponent {
// clear any existing errors
this.props.onUploadError(null);

let sortedFiles = sortFilesByName(files);
let sortedFiles = Array.from(files).sort((a, b) => a.name.localeCompare(b.name, this.props.locale, {numeric: true}));

const willUploadHooks = this.props.pluginFilesWillUploadHooks;
for (const h of willUploadHooks) {
Expand Down
2 changes: 2 additions & 0 deletions components/file_upload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'
import {getConfig} from 'mattermost-redux/selectors/entities/general';

import {uploadFile} from 'actions/file_actions.jsx';
import {getCurrentLocale} from 'selectors/i18n';
import {canUploadFiles} from 'utils/file_utils';

import FileUpload from './file_upload.jsx';
Expand All @@ -20,6 +21,7 @@ function mapStateToProps(state) {
uploadFile,
maxFileSize,
canUploadFiles: canUploadFiles(config),
locale: getCurrentLocale(state),
pluginFileUploadMethods: state.plugins.components.FileUploadMethod,
pluginFilesWillUploadHooks: state.plugins.components.FilesWillUploadHook,
};
Expand Down
30 changes: 30 additions & 0 deletions components/intl_provider/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

import {loadTranslations} from 'actions/views/root';

import {getCurrentLocale, getTranslations} from 'selectors/i18n';

import IntlProvider from './intl_provider';

function mapStateToProps(state) {
const locale = getCurrentLocale(state);

return {
locale,
translations: getTranslations(state, locale),
};
}

function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
loadTranslations,
}, dispatch),
};
}

export default connect(mapStateToProps, mapDispatchToProps)(IntlProvider);
Loading

0 comments on commit 02c26ca

Please sign in to comment.