From 1f44233e05d2a9ef1bf00c51004f0d395b9f9fb3 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Thu, 12 Oct 2017 21:24:45 +0200 Subject: [PATCH 01/19] Better translations in RoomList.js Signed-off-by: Stefan Parviainen --- src/components/views/rooms/RoomList.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index da77174dff..56589353f9 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -34,27 +34,18 @@ const Receipt = require('../../../utils/Receipt'); const HIDE_CONFERENCE_CHANS = true; function phraseForSection(section) { - // These would probably be better as individual strings, - // but for some reason we have translations for these strings - // as-is, so keeping it like this for now. - let verb; switch (section) { case 'm.favourite': - verb = _t('to favourite'); - break; + return _t('Drop here to favourite'); case 'im.vector.fake.direct': - verb = _t('to tag direct chat'); - break; + return _t('Drop here to tag direct chat'); case 'im.vector.fake.recent': - verb = _t('to restore'); - break; + return _t('Drop here to restore'); case 'm.lowpriority': - verb = _t('to demote'); - break; + return _t('Drop here to demote'); default: return _t('Drop here to tag %(section)s', {section: section}); } - return _t('Drop here %(toAction)s', {toAction: verb}); } module.exports = React.createClass({ From 9495ccdbb53f8bf7e30388cdb39b61fe6b2dadb2 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Thu, 12 Oct 2017 21:37:12 +0200 Subject: [PATCH 02/19] Don't hardcode ConfirmUserActionDialog title Signed-off-by: Stefan Parviainen --- src/components/views/dialogs/ConfirmUserActionDialog.js | 4 ++-- src/components/views/groups/GroupMemberInfo.js | 1 + src/components/views/rooms/MemberInfo.js | 5 +++-- src/components/views/rooms/RoomSettings.js | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js index 9091d8975e..64e25df5f1 100644 --- a/src/components/views/dialogs/ConfirmUserActionDialog.js +++ b/src/components/views/dialogs/ConfirmUserActionDialog.js @@ -36,6 +36,7 @@ export default React.createClass({ // group member object. Supply either this or 'member' groupMember: GroupMemberType, action: React.PropTypes.string.isRequired, // eg. 'Ban' + title: React.PropTypes.string.isRequired, // eg. 'Ban this user?' // Whether to display a text field for a reason // If true, the second argument to onFinished will @@ -75,7 +76,6 @@ export default React.createClass({ const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); - const title = _t("%(actionVerb)s this person?", { actionVerb: this.props.action}); const confirmButtonClass = classnames({ 'mx_Dialog_primary': true, 'danger': this.props.danger, @@ -113,7 +113,7 @@ export default React.createClass({ return (
diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index 6f1a370f26..aca2b1b222 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -69,6 +69,7 @@ module.exports = withMatrixClient(React.createClass({ Modal.createDialog(ConfirmUserActionDialog, { groupMember: this.props.groupMember, action: _t('Remove from group'), + title: _t('Remove this user from group?'), danger: true, onFinished: (proceed) => { if (!proceed) return; diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 856d3ebad4..180db1d5dd 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -247,11 +247,11 @@ module.exports = withMatrixClient(React.createClass({ onKick: function() { const membership = this.props.member.membership; - const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick"); const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog"); Modal.createTrackedDialog('Confirm User Action Dialog', 'onKick', ConfirmUserActionDialog, { member: this.props.member, - action: kickLabel, + action: membership === "invite" ? _t("Disinvite") : _t("Kick"), + title: membership === "invite" ? _t("Disinvite this user?") : _t("Kick this user?"), askReason: membership == "join", danger: true, onFinished: (proceed, reason) => { @@ -285,6 +285,7 @@ module.exports = withMatrixClient(React.createClass({ Modal.createTrackedDialog('Confirm User Action Dialog', 'onBanOrUnban', ConfirmUserActionDialog, { member: this.props.member, action: this.props.member.membership == 'ban' ? _t("Unban") : _t("Ban"), + title: this.props.member.membership == 'ban' ? _t("Unban this user?") : _t("Ban this user?"), askReason: this.props.member.membership != 'ban', danger: this.props.member.membership != 'ban', onFinished: (proceed, reason) => { diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 9934456597..b1a2f41cec 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -72,6 +72,7 @@ const BannedUser = React.createClass({ Modal.createTrackedDialog('Confirm User Action Dialog', 'onUnbanClick', ConfirmUserActionDialog, { member: this.props.member, action: _t('Unban'), + title: _t('Unban this user?'), danger: false, onFinished: (proceed) => { if (!proceed) return; From 3b91ada4c8806fedada2d3d0f1fd809070044ecc Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Fri, 13 Oct 2017 20:44:01 +0200 Subject: [PATCH 03/19] Departify sending emails and text messages Signed-off-by: Stefan Parviainen --- src/components/structures/login/ForgotPassword.js | 2 +- src/components/views/login/InteractiveAuthEntryComponents.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/login/ForgotPassword.js b/src/components/structures/login/ForgotPassword.js index 3e76291d20..4500e385e5 100644 --- a/src/components/structures/login/ForgotPassword.js +++ b/src/components/structures/login/ForgotPassword.js @@ -166,7 +166,7 @@ module.exports = React.createClass({ } else if (this.state.progress === "sent_email") { resetPasswordJsx = (
- { _t('An email has been sent to') } { this.state.email }. { _t("Once you've followed the link it contains, click below") }. + { _t("An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.", { emailAddress: this.state.email }) }
diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index 4c53c23f76..d0cd3931e4 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -256,7 +256,7 @@ export const EmailIdentityAuthEntry = React.createClass({ } else { return (
-

{ _t("An email has been sent to") } { this.props.inputs.emailAddress }

+

{ _t("An email has been sent to %(emailAddress)s", { emailAddress: '' + this.props.inputs.emailAddress + '' }) }

{ _t("Please check your email to continue registration.") }

); @@ -370,7 +370,7 @@ export const MsisdnAuthEntry = React.createClass({ }); return (
-

{ _t("A text message has been sent to") } +{ this._msisdn }

+

{ _t("A text message has been sent to %(msisdn)s", { msisdn: '' + this._msisdn + '' }) }

{ _t("Please enter the code it contains:") }

From a84b42bf24883dc57160f315027cf0802ec7bf49 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Fri, 13 Oct 2017 21:10:50 +0200 Subject: [PATCH 04/19] Departify RoomSettings Signed-off-by: Stefan Parviainen --- src/components/views/rooms/RoomSettings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index b1a2f41cec..0cb12002e7 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -868,21 +868,21 @@ module.exports = React.createClass({ disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)} checked={historyVisibility === "shared"} onChange={this._onHistoryRadioToggle} /> - { _t('Members only') } ({ _t('since the point in time of selecting this option') }) + { _t('Members only since the point in time of selecting this option') })
From 15d1dc1f3b0c441b6feef2b23ccb92325dd538d7 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Sun, 15 Oct 2017 16:57:13 +0200 Subject: [PATCH 05/19] Fix indentation Signed-off-by: Stefan Parviainen --- src/components/views/rooms/RoomList.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 56589353f9..e689579650 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -36,13 +36,13 @@ const HIDE_CONFERENCE_CHANS = true; function phraseForSection(section) { switch (section) { case 'm.favourite': - return _t('Drop here to favourite'); + return _t('Drop here to favourite'); case 'im.vector.fake.direct': - return _t('Drop here to tag direct chat'); + return _t('Drop here to tag direct chat'); case 'im.vector.fake.recent': - return _t('Drop here to restore'); + return _t('Drop here to restore'); case 'm.lowpriority': - return _t('Drop here to demote'); + return _t('Drop here to demote'); default: return _t('Drop here to tag %(section)s', {section: section}); } From ad2f54f8ab228383eb602c9cead1083ad16dec61 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Sun, 15 Oct 2017 18:01:57 +0200 Subject: [PATCH 06/19] Fix italics and parens Signed-off-by: Stefan Parviainen --- .../views/login/InteractiveAuthEntryComponents.js | 6 +++--- src/components/views/rooms/RoomSettings.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index d0cd3931e4..5f5a74ccd1 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -20,7 +20,7 @@ import url from 'url'; import classnames from 'classnames'; import sdk from '../../../index'; -import { _t } from '../../../languageHandler'; +import { _t, _tJsx } from '../../../languageHandler'; /* This file contains a collection of components which are used by the * InteractiveAuth to prompt the user to enter the information needed @@ -256,7 +256,7 @@ export const EmailIdentityAuthEntry = React.createClass({ } else { return (
-

{ _t("An email has been sent to %(emailAddress)s", { emailAddress: '' + this.props.inputs.emailAddress + '' }) }

+

{ _tJsx("An email has been sent to %(emailAddress)s", /%\(emailAddress\)s/, (sub) => {this.props.inputs.emailAddress}) }

{ _t("Please check your email to continue registration.") }

); @@ -370,7 +370,7 @@ export const MsisdnAuthEntry = React.createClass({ }); return (
-

{ _t("A text message has been sent to %(msisdn)s", { msisdn: '' + this._msisdn + '' }) }

+

{ _tJsx("A text message has been sent to %(msisdn)s", /%\(msisdn\)s/, (sub) => {this._msisdn}) }

{ _t("Please enter the code it contains:") }

diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 0cb12002e7..50478eaf43 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -868,21 +868,21 @@ module.exports = React.createClass({ disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)} checked={historyVisibility === "shared"} onChange={this._onHistoryRadioToggle} /> - { _t('Members only since the point in time of selecting this option') }) + { _t('Members only (since the point in time of selecting this option)') }
From 8083dccfa5239340dc8994d743e629fa8b055bd2 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Sun, 15 Oct 2017 21:08:41 +0200 Subject: [PATCH 07/19] De-partify SenderProfile Signed-off-by: Stefan Parviainen Also, text does not need to be EmojiText --- .../views/messages/SenderProfile.js | 38 ++++++++++++++----- src/components/views/rooms/EventTile.js | 12 +++--- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index 63e3144115..f6940cd4b3 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -19,6 +19,7 @@ import React from 'react'; import sdk from '../../../index'; import Flair from '../elements/Flair.js'; +import { _tJsx } from '../../../languageHandler'; export default function SenderProfile(props) { const EmojiText = sdk.getComponent('elements.EmojiText'); @@ -26,27 +27,44 @@ export default function SenderProfile(props) { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const {msgtype} = mxEvent.getContent(); + // Display sender name by default if nothing else is given + const text = props.text ? props.text : '%(senderName)s'; + if (msgtype === 'm.emote') { return ; // emote message must include the name so don't duplicate it } + // Name + flair + const nameElem = [ + { name || '' }, + props.enableFlair ? + + : null, + ] + + if(props.text) { + // Replace senderName, and wrap surrounding text in spans with the right class + content = _tJsx(props.text, /^(.*)\%\(senderName\)s(.*)$/m, (p1, p2) => [ + p1 ? {p1} : null, + nameElem, + p2 ? {p2} : null, + ]); + } else { + content = nameElem; + } + return (
- { name || '' } - { props.enableFlair ? - - : null - } - { props.aux ? { props.aux } : null } + { content }
); } SenderProfile.propTypes = { mxEvent: React.PropTypes.object.isRequired, // event whose sender we're showing - aux: React.PropTypes.string, // stuff to go after the sender name, if anything + text: React.PropTypes.string, // Text to show. Defaults to sender name onClick: React.PropTypes.func, }; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 499d0ec09a..812d72a26a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -19,7 +19,7 @@ limitations under the License. const React = require('react'); const classNames = require("classnames"); -import { _t } from '../../../languageHandler'; +import { _t, _td } from '../../../languageHandler'; const Modal = require('../../../Modal'); const sdk = require('../../../index'); @@ -502,12 +502,12 @@ module.exports = withMatrixClient(React.createClass({ } if (needsSenderProfile) { - let aux = null; + let text = null; if (!this.props.tileShape) { - if (msgtype === 'm.image') aux = _t('sent an image'); - else if (msgtype === 'm.video') aux = _t('sent a video'); - else if (msgtype === 'm.file') aux = _t('uploaded a file'); - sender = ; + if (msgtype === 'm.image') text = _td('%(senderName)s sent an image'); + else if (msgtype === 'm.video') text = _td('%(senderName)s sent a video'); + else if (msgtype === 'm.file') text = _td('%(senderName)s uploaded a file'); + sender = ; } else { sender = ; } From 468a05c6f1f2e14218cbf7fa083dd9ab9fb73612 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 17 Oct 2017 21:32:35 +0200 Subject: [PATCH 08/19] Fix SenderProfile Signed-off-by: Stefan Parviainen --- src/components/views/messages/SenderProfile.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index f6940cd4b3..afdb97272f 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -27,30 +27,29 @@ export default function SenderProfile(props) { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const {msgtype} = mxEvent.getContent(); - // Display sender name by default if nothing else is given - const text = props.text ? props.text : '%(senderName)s'; - if (msgtype === 'm.emote') { return ; // emote message must include the name so don't duplicate it } // Name + flair const nameElem = [ - { name || '' }, + { name || '' }, props.enableFlair ? - : null, - ] + ]; + + let content = ''; if(props.text) { // Replace senderName, and wrap surrounding text in spans with the right class content = _tJsx(props.text, /^(.*)\%\(senderName\)s(.*)$/m, (p1, p2) => [ - p1 ? {p1} : null, + p1 ? { p1 } : null, nameElem, - p2 ? {p2} : null, + p2 ? { p2 } : null, ]); } else { content = nameElem; From fc860c66bc0f1d7cb5b4785a1372bbb422fd47e9 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 17 Oct 2017 22:03:49 +0200 Subject: [PATCH 09/19] De-partify RoomPreviewBar Signed-off-by: Stefan Parviainen --- src/components/views/rooms/RoomPreviewBar.js | 22 +++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 368d81e606..0c0601a504 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -83,10 +83,8 @@ module.exports = React.createClass({ } }, - _roomNameElement: function(fallback) { - fallback = fallback || _t('a room'); - const name = this.props.room ? this.props.room.name : (this.props.room_alias || ""); - return name ? name : fallback; + _roomNameElement: function() { + return this.props.room ? this.props.room.name : (this.props.room_alias || ""); }, render: function() { @@ -150,7 +148,7 @@ module.exports = React.createClass({
); } else if (kicked || banned) { - const roomName = this._roomNameElement(_t('This room')); + const roomName = this._roomNameElement(); const kickerMember = this.props.room.currentState.getMember( myMember.events.member.getSender(), ); @@ -167,9 +165,17 @@ module.exports = React.createClass({ let actionText; if (kicked) { - actionText = _t("You have been kicked from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + if(roomName) { + actionText = _t("You have been kicked from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + } else { + actionText = _t("You have been kicked from this room by %(userName)s.", {userName: kickerName}); + } } else if (banned) { - actionText = _t("You have been banned from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + if(roomName) { + actionText = _t("You have been banned from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + } else { + actionText = _t("You have been banned from this room by %(userName)s.", {userName: kickerName}); + } } // no other options possible due to the kicked || banned check above. joinBlock = ( @@ -203,7 +209,7 @@ module.exports = React.createClass({ joinBlock = (
- { _t('You are trying to access %(roomName)s.', {roomName: name}) } + { name ? _t('You are trying to access %(roomName)s.', {roomName: name}) : _t('You are trying to access a room.') }
{ _tJsx("Click here to join the discussion!", /(.*?)<\/a>/, From 7eeed3e0932841fea286d5a7bdad86cfc412c98e Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 17 Oct 2017 23:46:23 +0200 Subject: [PATCH 10/19] Simplify MemberEventListSummary by using pluralization provided by the i18n library Signed-off-by: Stefan Parviainen --- .../views/elements/MemberEventListSummary.js | 154 +++++------------- 1 file changed, 40 insertions(+), 114 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 596838febe..9b303b4bd9 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -86,7 +86,6 @@ module.exports = React.createClass({ const summaries = orderedTransitionSequences.map((transitions) => { const userNames = eventAggregates[transitions]; const nameList = this._renderNameList(userNames); - const plural = userNames.length > 1; const splitTransitions = transitions.split(','); @@ -101,7 +100,7 @@ module.exports = React.createClass({ const descs = coalescedTransitions.map((t) => { return this._getDescriptionForTransition( - t.transitionType, plural, t.repeats, + t.transitionType, userNames.length, t.repeats, ); }); @@ -208,148 +207,75 @@ module.exports = React.createClass({ * For a certain transition, t, describe what happened to the users that * underwent the transition. * @param {string} t the transition type. - * @param {boolean} plural whether there were multiple users undergoing the same - * transition. + * @param {integer} userCount number of usernames * @param {number} repeats the number of times the transition was repeated in a row. * @returns {string} the written Human Readable equivalent of the transition. */ - _getDescriptionForTransition(t, plural, repeats) { + _getDescriptionForTransition(t, userCount, repeats) { // The empty interpolations 'severalUsers' and 'oneUser' // are there only to show translators to non-English languages // that the verb is conjugated to plural or singular Subject. let res = null; switch(t) { case "joined": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)sjoined %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)sjoined %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)sjoined", { severalUsers: "" }) - : _t("%(oneUser)sjoined", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)sjoined %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)sjoined %(count)s times", { oneUser: "", count: repeats }); break; case "left": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)sleft %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)sleft %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)sleft", { severalUsers: "" }) - : _t("%(oneUser)sleft", { oneUser: "" }); - } - break; + res = (userCount > 1) + ? _t("%(severalUsers)sleft %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)sleft %(count)s times", { oneUser: "", count: repeats }); + break; case "joined_and_left": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)sjoined and left %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)sjoined and left %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)sjoined and left", { severalUsers: "" }) - : _t("%(oneUser)sjoined and left", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)sjoined and left %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)sjoined and left %(count)s times", { oneUser: "", count: repeats }); break; case "left_and_joined": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)sleft and rejoined %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)sleft and rejoined %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)sleft and rejoined", { severalUsers: "" }) - : _t("%(oneUser)sleft and rejoined", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)sleft and rejoined %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)sleft and rejoined %(count)s times", { oneUser: "", count: repeats }); break; case "invite_reject": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)srejected their invitations %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)srejected their invitation %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)srejected their invitations", { severalUsers: "" }) - : _t("%(oneUser)srejected their invitation", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)srejected their invitations %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)srejected their invitation %(count)s times", { oneUser: "", count: repeats }); break; case "invite_withdrawal": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)shad their invitations withdrawn %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)shad their invitation withdrawn %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)shad their invitations withdrawn", { severalUsers: "" }) - : _t("%(oneUser)shad their invitation withdrawn", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)shad their invitations withdrawn %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)shad their invitation withdrawn %(count)s times", { oneUser: "", count: repeats }); break; case "invited": - if (repeats > 1) { - res = (plural) - ? _t("were invited %(repeats)s times", { repeats: repeats }) - : _t("was invited %(repeats)s times", { repeats: repeats }); - } else { - res = (plural) - ? _t("were invited") - : _t("was invited"); - } + res = (userCount > 1) + ? _t("were invited %(count)s times", { count: repeats }) + : _t("was invited %(count)s times", { count: repeats }); break; case "banned": - if (repeats > 1) { - res = (plural) - ? _t("were banned %(repeats)s times", { repeats: repeats }) - : _t("was banned %(repeats)s times", { repeats: repeats }); - } else { - res = (plural) - ? _t("were banned") - : _t("was banned"); - } + res = (userCount > 1) + ? _t("were banned %(count)s times", { count: repeats }) + : _t("was banned %(count)s times", { count: repeats }); break; case "unbanned": - if (repeats > 1) { - res = (plural) - ? _t("were unbanned %(repeats)s times", { repeats: repeats }) - : _t("was unbanned %(repeats)s times", { repeats: repeats }); - } else { - res = (plural) - ? _t("were unbanned") - : _t("was unbanned"); - } + res = (userCount > 1) + ? _t("were unbanned %(count)s times", { count: repeats }) + : _t("was unbanned %(count)s times", { count: repeats }); break; case "kicked": - if (repeats > 1) { - res = (plural) - ? _t("were kicked %(repeats)s times", { repeats: repeats }) - : _t("was kicked %(repeats)s times", { repeats: repeats }); - } else { - res = (plural) - ? _t("were kicked") - : _t("was kicked"); - } + res = (userCount > 1) + ? _t("were kicked %(count)s times", { count: repeats }) + : _t("was kicked %(count)s times", { count: repeats }); break; case "changed_name": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)schanged their name %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)schanged their name %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)schanged their name", { severalUsers: "" }) - : _t("%(oneUser)schanged their name", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)schanged their name %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)schanged their name %(count)s times", { oneUser: "", count: repeats }); break; case "changed_avatar": - if (repeats > 1) { - res = (plural) - ? _t("%(severalUsers)schanged their avatar %(repeats)s times", { severalUsers: "", repeats: repeats }) - : _t("%(oneUser)schanged their avatar %(repeats)s times", { oneUser: "", repeats: repeats }); - } else { - res = (plural) - ? _t("%(severalUsers)schanged their avatar", { severalUsers: "" }) - : _t("%(oneUser)schanged their avatar", { oneUser: "" }); - } + res = (userCount > 1) + ? _t("%(severalUsers)schanged their avatar %(count)s times", { severalUsers: "", count: repeats }) + : _t("%(oneUser)schanged their avatar %(count)s times", { oneUser: "", count: repeats }); break; } From ef30ba889b617a22a46c2173d0d388bc480cd305 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Mon, 23 Oct 2017 19:55:40 +0200 Subject: [PATCH 11/19] Make MemberEventListSummary more translatable Signed-off-by: Stefan Parviainen --- src/components/views/elements/MemberEventListSummary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 9b303b4bd9..6e75c32e0d 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -106,7 +106,7 @@ module.exports = React.createClass({ const desc = this._renderCommaSeparatedList(descs); - return nameList + " " + desc; + return _t('%(nameList)s %(transitionList)s', { nameList: nameList, transitionList: desc }); }); if (!summaries) { From 6406fc3865ff3fe434b5e7c341bcf7a5792e63e0 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 24 Oct 2017 18:32:50 +0200 Subject: [PATCH 12/19] Use plurals in WhoIsTyping --- src/WhoIsTyping.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/WhoIsTyping.js b/src/WhoIsTyping.js index 6bea2cbb92..0edad8d4a5 100644 --- a/src/WhoIsTyping.js +++ b/src/WhoIsTyping.js @@ -68,10 +68,8 @@ module.exports = { const names = whoIsTyping.map(function(m) { return m.name; }); - if (othersCount==1) { - return _t('%(names)s and one other are typing', {names: names.slice(0, limit - 1).join(', ')}); - } else if (othersCount>1) { - return _t('%(names)s and %(count)s others are typing', {names: names.slice(0, limit - 1).join(', '), count: othersCount}); + if (othersCount>=1) { + return _t('%(names)s and %(count)s others are typing', {names: names.slice(0, limit - 1).join(', '), count: othersCount}); } else { const lastPerson = names.pop(); return _t('%(names)s and %(lastPerson)s are typing', {names: names.join(', '), lastPerson: lastPerson}); From b5024cca750d546eb917ff48f41abb1e613d943a Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 24 Oct 2017 19:34:08 +0200 Subject: [PATCH 13/19] Further simplify MemberEventListSummary a bit Signed-off-by: Stefan Parviainen --- src/components/views/elements/MemberEventListSummary.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 6e75c32e0d..6a1566c961 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -302,11 +302,9 @@ module.exports = React.createClass({ return ""; } else if (items.length === 1) { return items[0]; - } else if (remaining) { + } else if (remaining >= 0) { items = items.slice(0, itemLimit); - return (remaining > 1) - ? _t("%(items)s and %(remaining)s others", { items: items.join(', '), remaining: remaining } ) - : _t("%(items)s and one other", { items: items.join(', ') }); + return _t("%(items)s and %(count)s others", { items: items.join(', '), count: remaining } ) } else { const lastItem = items.pop(); return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem }); From bc034f3083796384f42999bc355534052f149d63 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 24 Oct 2017 19:34:32 +0200 Subject: [PATCH 14/19] Update strings Signed-off-by: Stefan Parviainen --- src/i18n/strings/en_EN.json | 145 +++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 492113989b..6955ed053a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -153,8 +153,8 @@ "Communities": "Communities", "Message Pinning": "Message Pinning", "%(displayName)s is typing": "%(displayName)s is typing", - "%(names)s and one other are typing": "%(names)s and one other are typing", "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing", + "%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing", "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", "Failure to create room": "Failure to create room", "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", @@ -210,9 +210,9 @@ " (unsupported)": " (unsupported)", "Join as voice or video.": "Join as voice or video.", "Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)s.", - "sent an image": "sent an image", - "sent a video": "sent a video", - "uploaded a file": "uploaded a file", + "%(senderName)s sent an image": "%(senderName)s sent an image", + "%(senderName)s sent a video": "%(senderName)s sent a video", + "%(senderName)s uploaded a file": "%(senderName)s uploaded a file", "Options": "Options", "Undecryptable": "Undecryptable", "Encrypted by a verified device": "Encrypted by a verified device", @@ -225,9 +225,13 @@ "device id: ": "device id: ", "Disinvite": "Disinvite", "Kick": "Kick", + "Disinvite this user?": "Disinvite this user?", + "Kick this user?": "Kick this user?", "Failed to kick": "Failed to kick", "Unban": "Unban", "Ban": "Ban", + "Unban this user?": "Unban this user?", + "Ban this user?": "Ban this user?", "Failed to ban user": "Failed to ban user", "Failed to mute user": "Failed to mute user", "Failed to toggle moderator status": "Failed to toggle moderator status", @@ -312,12 +316,11 @@ "Forget room": "Forget room", "Search": "Search", "Show panel": "Show panel", - "to favourite": "to favourite", - "to tag direct chat": "to tag direct chat", - "to restore": "to restore", - "to demote": "to demote", + "Drop here to favourite": "Drop here to favourite", + "Drop here to tag direct chat": "Drop here to tag direct chat", + "Drop here to restore": "Drop here to restore", + "Drop here to demote": "Drop here to demote", "Drop here to tag %(section)s": "Drop here to tag %(section)s", - "Drop here %(toAction)s": "Drop here %(toAction)s", "Press to start a chat with someone": "Press to start a chat with someone", "You're not in any rooms yet! Press to make a room or to browse the directory": "You're not in any rooms yet! Press to make a room or to browse the directory", "Invites": "Invites", @@ -327,20 +330,22 @@ "Low priority": "Low priority", "Historical": "Historical", "Unnamed Room": "Unnamed Room", - "a room": "a room", "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", - "This room": "This room", "Reason: %(reasonText)s": "Reason: %(reasonText)s", "Rejoin": "Rejoin", "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", + "You have been kicked from this room by %(userName)s.": "You have been kicked from this room by %(userName)s.", "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", + "You have been banned from this room by %(userName)s.": "You have been banned from this room by %(userName)s.", + "This room": "This room", "%(roomName)s does not exist.": "%(roomName)s does not exist.", "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", + "You are trying to access a room.": "You are trying to access a room.", "Click here to join the discussion!": "Click here to join the discussion!", "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", "To change the room's avatar, you must be a": "To change the room's avatar, you must be a", @@ -385,10 +390,9 @@ "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "Who can read history?": "Who can read history?", "Anyone": "Anyone", - "Members only": "Members only", - "since the point in time of selecting this option": "since the point in time of selecting this option", - "since they were invited": "since they were invited", - "since they joined": "since they joined", + "Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)", + "Members only (since they were invited)": "Members only (since they were invited)", + "Members only (since they joined)": "Members only (since they joined)", "Room Colour": "Room Colour", "Permissions": "Permissions", "The default role for new room members is": "The default role for new room members is", @@ -460,10 +464,10 @@ "Dismiss": "Dismiss", "To continue, please enter your password.": "To continue, please enter your password.", "Password:": "Password:", - "An email has been sent to": "An email has been sent to", + "An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s", "Please check your email to continue registration.": "Please check your email to continue registration.", "Token incorrect": "Token incorrect", - "A text message has been sent to": "A text message has been sent to", + "A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s", "Please enter the code it contains:": "Please enter the code it contains:", "Start authentication": "Start authentication", "powered by Matrix": "powered by Matrix", @@ -485,6 +489,7 @@ "Identity server URL": "Identity server URL", "What does this mean?": "What does this mean?", "Remove from community": "Remove from community", + "Remove this user from community?": "Remove this user from community?", "Failed to remove user from community": "Failed to remove user from community", "Filter community members": "Filter community members", "Filter community rooms": "Filter community rooms", @@ -510,56 +515,57 @@ "Integrations Error": "Integrations Error", "Could not connect to the integration server": "Could not connect to the integration server", "Manage Integrations": "Manage Integrations", - "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times", - "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times", - "%(severalUsers)sjoined": "%(severalUsers)sjoined", - "%(oneUser)sjoined": "%(oneUser)sjoined", - "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times", - "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times", - "%(severalUsers)sleft": "%(severalUsers)sleft", - "%(oneUser)sleft": "%(oneUser)sleft", - "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times", - "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times", - "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left", - "%(oneUser)sjoined and left": "%(oneUser)sjoined and left", - "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times", - "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times", - "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined", - "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined", - "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times", - "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times", - "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations", - "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation", - "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times", - "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times", - "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn", - "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn", - "were invited %(repeats)s times": "were invited %(repeats)s times", - "was invited %(repeats)s times": "was invited %(repeats)s times", - "were invited": "were invited", - "was invited": "was invited", - "were banned %(repeats)s times": "were banned %(repeats)s times", - "was banned %(repeats)s times": "was banned %(repeats)s times", - "were banned": "were banned", - "was banned": "was banned", - "were unbanned %(repeats)s times": "were unbanned %(repeats)s times", - "was unbanned %(repeats)s times": "was unbanned %(repeats)s times", - "were unbanned": "were unbanned", - "was unbanned": "was unbanned", - "were kicked %(repeats)s times": "were kicked %(repeats)s times", - "was kicked %(repeats)s times": "was kicked %(repeats)s times", - "were kicked": "were kicked", - "was kicked": "was kicked", - "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times", - "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times", - "%(severalUsers)schanged their name": "%(severalUsers)schanged their name", - "%(oneUser)schanged their name": "%(oneUser)schanged their name", - "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times", - "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times", - "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar", - "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", - "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others", - "%(items)s and one other": "%(items)s and one other", + "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", + "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times", + "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)sjoined", + "%(oneUser)sjoined %(count)s times|other": "%(oneUser)sjoined %(count)s times", + "%(oneUser)sjoined %(count)s times|one": "%(oneUser)sjoined", + "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)sleft %(count)s times", + "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)sleft", + "%(oneUser)sleft %(count)s times|other": "%(oneUser)sleft %(count)s times", + "%(oneUser)sleft %(count)s times|one": "%(oneUser)sleft", + "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)sjoined and left %(count)s times", + "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)sjoined and left", + "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)sjoined and left %(count)s times", + "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)sjoined and left", + "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)sleft and rejoined %(count)s times", + "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)sleft and rejoined", + "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)sleft and rejoined %(count)s times", + "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)sleft and rejoined", + "%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)srejected their invitations %(count)s times", + "%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)srejected their invitations", + "%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)srejected their invitation %(count)s times", + "%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)srejected their invitation", + "%(severalUsers)shad their invitations withdrawn %(count)s times|other": "%(severalUsers)shad their invitations withdrawn %(count)s times", + "%(severalUsers)shad their invitations withdrawn %(count)s times|one": "%(severalUsers)shad their invitations withdrawn", + "%(oneUser)shad their invitation withdrawn %(count)s times|other": "%(oneUser)shad their invitation withdrawn %(count)s times", + "%(oneUser)shad their invitation withdrawn %(count)s times|one": "%(oneUser)shad their invitation withdrawn", + "were invited %(count)s times|other": "were invited %(count)s times", + "were invited %(count)s times|one": "were invited", + "was invited %(count)s times|other": "was invited %(count)s times", + "was invited %(count)s times|one": "was invited", + "were banned %(count)s times|other": "were banned %(count)s times", + "were banned %(count)s times|one": "were banned", + "was banned %(count)s times|other": "was banned %(count)s times", + "was banned %(count)s times|one": "was banned", + "were unbanned %(count)s times|other": "were unbanned %(count)s times", + "were unbanned %(count)s times|one": "were unbanned", + "was unbanned %(count)s times|other": "was unbanned %(count)s times", + "was unbanned %(count)s times|one": "was unbanned", + "were kicked %(count)s times|other": "were kicked %(count)s times", + "were kicked %(count)s times|one": "were kicked", + "was kicked %(count)s times|other": "was kicked %(count)s times", + "was kicked %(count)s times|one": "was kicked", + "%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)schanged their name %(count)s times", + "%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)schanged their name", + "%(oneUser)schanged their name %(count)s times|other": "%(oneUser)schanged their name %(count)s times", + "%(oneUser)schanged their name %(count)s times|one": "%(oneUser)schanged their name", + "%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)schanged their avatar %(count)s times", + "%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)schanged their avatar", + "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)schanged their avatar %(count)s times", + "%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)schanged their avatar", + "%(items)s and %(count)s others|other": "%(items)s and %(count)s others", + "%(items)s and %(count)s others|one": "%(items)s and one other", "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", "Custom level": "Custom level", "Room directory": "Room directory", @@ -581,7 +587,6 @@ "Start Chatting": "Start Chatting", "Confirm Removal": "Confirm Removal", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", - "%(actionVerb)s this person?": "%(actionVerb)s this person?", "Community IDs may only contain alphanumeric characters": "Community IDs may only contain alphanumeric characters", "Something went wrong whilst creating your community": "Something went wrong whilst creating your community", "Create Community": "Create Community", @@ -676,6 +681,7 @@ "Leave %(groupName)s?": "Leave %(groupName)s?", "Leave": "Leave", "Unable to leave room": "Unable to leave room", + "Community Settings": "Community Settings", "Add rooms to this community": "Add rooms to this community", "Featured Rooms:": "Featured Rooms:", "Featured Users:": "Featured Users:", @@ -686,7 +692,6 @@ "Publish this community on your profile": "Publish this community on your profile", "Long Description (HTML)": "Long Description (HTML)", "Description": "Description", - "Community Settings": "Community Settings", "Community %(groupId)s not found": "Community %(groupId)s not found", "This Home server does not support communities": "This Home server does not support communities", "Failed to load %(groupId)s": "Failed to load %(groupId)s", @@ -824,7 +829,7 @@ "A new password must be entered.": "A new password must be entered.", "New passwords must match each other.": "New passwords must match each other.", "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", - "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", + "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.", "I have verified my email address": "I have verified my email address", "Your password has been reset": "Your password has been reset", "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device", From 88fd60066fd011d8cb475961e9ae398413974820 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Tue, 24 Oct 2017 20:07:57 +0200 Subject: [PATCH 15/19] Fix typo Signed-off-by: Stefan Parviainen --- src/components/views/elements/MemberEventListSummary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 6a1566c961..de6f801a21 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -302,7 +302,7 @@ module.exports = React.createClass({ return ""; } else if (items.length === 1) { return items[0]; - } else if (remaining >= 0) { + } else if (remaining > 0) { items = items.slice(0, itemLimit); return _t("%(items)s and %(count)s others", { items: items.join(', '), count: remaining } ) } else { From 047bf6e4dd641e2ee350b2c5a0c11c89b6c65201 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 1 Nov 2017 11:30:25 +0000 Subject: [PATCH 16/19] Redact group IDs from analytics --- src/Analytics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analytics.js b/src/Analytics.js index a82f57a144..1b4f45bc6b 100644 --- a/src/Analytics.js +++ b/src/Analytics.js @@ -19,7 +19,7 @@ import PlatformPeg from './PlatformPeg'; import SdkConfig from './SdkConfig'; function getRedactedUrl() { - const redactedHash = window.location.hash.replace(/#\/(room|user)\/(.+)/, "#/$1/"); + const redactedHash = window.location.hash.replace(/#\/(group|room|user)\/(.+)/, "#/$1/"); // hardcoded url to make piwik happy return 'https://riot.im/app/' + redactedHash; } From 15bafd6818ed732ef96e66abd879fa730b14aeac Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Wed, 1 Nov 2017 15:55:58 +0100 Subject: [PATCH 17/19] Convert from weblate to counterpart at runtime to make tests happy Signed-off-by: Stefan Parviainen --- src/languageHandler.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index a90b78c40e..da62bfee56 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -252,6 +252,26 @@ function getLangsJson() { }); } +function weblateToCounterpart(inTrs) { + const outTrs = {}; + + for (const key of Object.keys(inTrs)) { + const keyParts = key.split('|', 2); + if (keyParts.length === 2) { + let obj = outTrs[keyParts[0]]; + if (obj === undefined) { + obj = {}; + outTrs[keyParts[0]] = obj; + } + obj[keyParts[1]] = inTrs[key]; + } else { + outTrs[key] = inTrs[key]; + } + } + + return outTrs; +} + function getLanguage(langPath) { return new Promise((resolve, reject) => { request( @@ -261,7 +281,7 @@ function getLanguage(langPath) { reject({err: err, response: response}); return; } - resolve(JSON.parse(body)); + resolve(weblateToCounterpart(JSON.parse(body))); }, ); }); From e1e4fc2dacb7d2979aecd5cf871f5d3f8ca3196f Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Wed, 1 Nov 2017 16:18:48 +0100 Subject: [PATCH 18/19] Make eslint happy Signed-off-by: Stefan Parviainen --- src/components/views/groups/GroupMemberInfo.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index a36c32fb07..01270cd79d 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -85,7 +85,8 @@ module.exports = React.createClass({ Modal.createDialog(ConfirmUserActionDialog, { groupMember: this.props.groupMember, action: this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community'), - title: this.state.isUserInvited ? _t('Disinvite this user from community?') : _t('Remove this user from community?'), + title: this.state.isUserInvited ? _t('Disinvite this user from community?') + : _t('Remove this user from community?'), danger: true, onFinished: (proceed) => { if (!proceed) return; From 7d7cd30e46b3da81821127b5860cd22acf744175 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 1 Nov 2017 22:10:03 +0000 Subject: [PATCH 19/19] turn NPE on flair resolution errors into a logged error --- src/stores/FlairStore.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stores/FlairStore.js b/src/stores/FlairStore.js index 1ac518a4f6..d848ca7dda 100644 --- a/src/stores/FlairStore.js +++ b/src/stores/FlairStore.js @@ -129,7 +129,11 @@ class FlairStore extends EventEmitter { } const updatedUserGroups = resp.users; usersInFlight.forEach((userId) => { - this._usersPending[userId].resolve(updatedUserGroups[userId] || []); + if (this._usersPending[userId]) { + this._usersPending[userId].resolve(updatedUserGroups[userId] || []); + } else { + console.error("Promise vanished for resolving groups for " + userId); + } }); }